Remove the rest of the equality test: its comparison of two compositions (issue 1384, 1389)

The controller's own composition it compared mesh-sdk's with is deleted by the commit before; compose_equal_test.go held the comparison (bothWaysDiffer, madeDiffer and their helpers) and is not needed with nothing to compare against. The made type, which compose_perform_integration_test still reads a store through, moves next to madeIn.
This commit is contained in:
2026-10-20 11:19:43 +00:00
parent 2111b78fc5
commit 37d61441f8
2 changed files with 8 additions and 341 deletions
-341
View File
@@ -1,341 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"
"testing"
)
// The comparison of the controller's composition and mesh-sdk's (novox/hq ADR 0453 clause 3, design 47 R4b, issue 1384),
// the part of it that needs no store. The test that raises a mesh and composes every machine both ways is
// compose_equal_integration_test.go; both are deleted with the controller's own composition (issue 1389).
// composedBoth is what one composition of a machine said, in the terms the two paths are compared in.
type composedBoth struct {
Err string
Problems []string
LeftOut map[string]string
Withheld []string
Unbound []string
Unplaced []string
Handed []string
// Foreseen are the own secrets the composition names foreseen, as `module/name`.
Foreseen []string
Body []byte
}
// capturedFrom is a sendable, its validator problems and the error that came with it, as compared.
func capturedFrom(d sendable, problems []string, err error) composedBoth {
if err != nil {
return composedBoth{Err: err.Error()}
}
c := composedBoth{Problems: append([]string(nil), problems...), LeftOut: d.leftOutWhy,
Unplaced: append([]string(nil), d.unplaced...), Handed: append([]string(nil), d.handedOnly...),
Foreseen: append([]string(nil), d.foreseen...)}
for _, o := range d.withheld {
c.Withheld = append(c.Withheld, o.String())
}
for _, u := range d.unbound {
c.Unbound = append(c.Unbound, u.String())
}
sort.Strings(c.Problems)
sort.Strings(c.Withheld)
sort.Strings(c.Unbound)
sort.Strings(c.Unplaced)
sort.Strings(c.Handed)
sort.Strings(c.Foreseen)
c.Body, _ = d.Body()
return c
}
// bothWaysDiffer says how two compositions of one machine differ, one line for each field that does, naming the machine and
// the comparison. A machine both refuse alike is no difference. withBody compares the declaration byte for byte and names
// the first resource that differs.
func bothWaysDiffer(machine, comparison string, old, sdk composedBoth, withBody bool) []string {
var out []string
say := func(field string, a, b any) {
out = append(out, fmt.Sprintf("%s, %s: %s differs: the controller's %v, mesh-sdk's %v", machine, comparison, field, a, b))
}
if old.Err != sdk.Err {
say("the refusal", quoted(old.Err), quoted(sdk.Err))
return out
}
if old.Err != "" {
return nil
}
for _, f := range []struct {
name string
old, sdk []string
}{
{"the validator's problems", old.Problems, sdk.Problems},
{"Withheld", old.Withheld, sdk.Withheld},
{"Unbound", old.Unbound, sdk.Unbound},
{"Unplaced", old.Unplaced, sdk.Unplaced},
{"HandedOnly", old.Handed, sdk.Handed},
{"the own secrets named foreseen", old.Foreseen, sdk.Foreseen},
} {
if !sameStrings(f.old, f.sdk) {
say(f.name, f.old, f.sdk)
}
}
if !sameLeftOut(old.LeftOut, sdk.LeftOut) {
say("LeftOut and its reasons", old.LeftOut, sdk.LeftOut)
}
if withBody && !bytes.Equal(old.Body, sdk.Body) {
out = append(out, fmt.Sprintf("%s, %s: the declaration differs byte for byte; %s", machine, comparison,
firstDifferingResource(old.Body, sdk.Body)))
}
return out
}
func quoted(s string) string {
if s == "" {
return "none"
}
return fmt.Sprintf("%q", s)
}
func sameStrings(a, b []string) bool {
if len(a) == 0 && len(b) == 0 {
return true
}
return reflect.DeepEqual(a, b)
}
func sameLeftOut(a, b map[string]string) bool {
if len(a) == 0 && len(b) == 0 {
return true
}
return reflect.DeepEqual(a, b)
}
// firstDifferingResource names the first resource of two declaration bodies that is not the same, and the first field of it.
func firstDifferingResource(a, b []byte) string {
var x, y struct {
Resources []map[string]any `json:"resources"`
}
if json.Unmarshal(a, &x) != nil || json.Unmarshal(b, &y) != nil {
return "a body is not a declaration"
}
byID := func(rs []map[string]any) map[string]map[string]any {
out := map[string]map[string]any{}
for _, r := range rs {
out[fmt.Sprint(r["id"])] = r
}
return out
}
ids := map[string]bool{}
left, right := byID(x.Resources), byID(y.Resources)
for id := range left {
ids[id] = true
}
for id := range right {
ids[id] = true
}
var order []string
for id := range ids {
order = append(order, id)
}
sort.Strings(order)
for _, id := range order {
l, inLeft := left[id]
r, inRight := right[id]
switch {
case !inRight:
return fmt.Sprintf("resource %s is only in the controller's", id)
case !inLeft:
return fmt.Sprintf("resource %s is only in mesh-sdk's", id)
}
var fields []string
for k := range l {
fields = append(fields, k)
}
for k := range r {
if _, have := l[k]; !have {
fields = append(fields, k)
}
}
sort.Strings(fields)
for _, k := range fields {
if !reflect.DeepEqual(l[k], r[k]) {
return fmt.Sprintf("resource %s, field %s: the controller's %v, mesh-sdk's %v", id, k, l[k], r[k])
}
}
}
if !reflect.DeepEqual(left, right) {
return "the envelopes differ"
}
return "the resources are the same; the envelope differs"
}
// made is what a send made in a store, by key and never by value: the machine ports given, the pair credentials and the
// own secrets.
type made struct {
Ports []string
Pairs []string
OwnSecrets []string
}
// madeDiffer says how what two sends made differs, by entry: for each kind, the entries only the controller made, the ones only
// mesh-sdk made, and the ones both made with a different value. Never a whole list. The entries of a machine in refused (a
// machine whose send is refused on either side, which sends nothing) are left out of both.
func madeDiffer(comparison string, old, sdk made, refused map[string]bool) []string {
var out []string
for _, f := range []struct {
name string
old, sdk []string
split func(string) (key, value string)
nodes func(string) []string
}{
{"the ports allocated", old.Ports, sdk.Ports, splitAt("->"), firstWord},
{"the pair credentials made", old.Pairs, sdk.Pairs, func(l string) (string, string) { return l, "" }, pairNodes},
{"the own secrets made", old.OwnSecrets, sdk.OwnSecrets, splitAtLast(" ("), firstWord},
} {
left, right := entriesOf(f.old, f.split, f.nodes, refused), entriesOf(f.sdk, f.split, f.nodes, refused)
var onlyOld, onlySDK, different []string
for key, v := range left {
if w, both := right[key]; !both {
onlyOld = append(onlyOld, key)
} else if w != v {
different = append(different, fmt.Sprintf("%s (the controller's %s, mesh-sdk's %s)", key, v, w))
}
}
for key := range right {
if _, both := left[key]; !both {
onlySDK = append(onlySDK, key)
}
}
sort.Strings(onlyOld)
sort.Strings(onlySDK)
sort.Strings(different)
var said []string
if len(onlyOld) > 0 {
said = append(said, "made only by the controller: "+strings.Join(onlyOld, ", "))
}
if len(onlySDK) > 0 {
said = append(said, "made only by mesh-sdk: "+strings.Join(onlySDK, ", "))
}
if len(different) > 0 {
said = append(said, "made by both with a different value: "+strings.Join(different, ", "))
}
if len(said) > 0 {
out = append(out, fmt.Sprintf("%s: %s differ: %s", comparison, f.name, strings.Join(said, "; ")))
}
}
return out
}
// entriesOf is the entries of a list by key, those of a machine in skip left out.
func entriesOf(lines []string, split func(string) (string, string), nodes func(string) []string, skip map[string]bool) map[string]string {
out := map[string]string{}
for _, l := range lines {
skipped := false
for _, n := range nodes(l) {
skipped = skipped || skip[n]
}
if skipped {
continue
}
k, v := split(l)
out[k] = v
}
return out
}
func splitAt(sep string) func(string) (string, string) {
return func(l string) (string, string) {
k, v, _ := strings.Cut(l, sep)
return k, v
}
}
func splitAtLast(sep string) func(string) (string, string) {
return func(l string) (string, string) {
if i := strings.LastIndex(l, sep); i >= 0 {
return l[:i], l[i+1:]
}
return l, ""
}
}
func firstWord(l string) []string {
f := strings.Fields(l)
if len(f) == 0 {
return nil
}
return f[:1]
}
// pairNodes are the consumer and the provider of a pair credential line ("name module on consumer from provider local=...").
func pairNodes(l string) []string {
f := strings.Fields(l)
var out []string
for i := 0; i+1 < len(f); i++ {
if f[i] == "on" || f[i] == "from" {
out = append(out, f[i+1])
}
}
return out
}
// 1384/AC3: the comparison itself. A composition that differs by one setting's worth is named, with its machine and the
// resource and field that first differ; two that agree, and two that refuse alike, are not differences. It failed first,
// before bothWaysDiffer read the body: a changed setting went unseen.
func TestTheComparisonNamesTheMachineWhoseCompositionDiffers(t *testing.T) {
body := func(port string) []byte {
raw, err := json.Marshal(map[string]any{"declaration": 1, "resources": []map[string]any{
{"id": "files.config", "type": "file", "content": "port=" + port},
{"id": "files.dir", "type": "directory"}}})
if err != nil {
t.Fatal(err)
}
return raw
}
same := composedBoth{Body: body("8080"), Foreseen: []string{"files/admin"}}
if got := bothWaysDiffer("laptop", "Reading", same, same, true); len(got) != 0 {
t.Fatalf("a composition differs from itself: %v", got)
}
changed := composedBoth{Body: body("8081"), Foreseen: []string{"files/admin"}}
got := bothWaysDiffer("laptop", "Reading", same, changed, true)
if len(got) != 1 || !strings.Contains(got[0], "laptop, Reading") ||
!strings.Contains(got[0], "resource files.config, field content") ||
!strings.Contains(got[0], "port=8080") || !strings.Contains(got[0], "port=8081") {
t.Fatalf("the changed setting is not named with its machine, resource and field: %v", got)
}
// A field of the lists.
other := composedBoth{Body: body("8080"), Foreseen: []string{"files/admin", "files/token"}}
if got := bothWaysDiffer("anchor", "Foreseeing", same, other, false); len(got) != 1 ||
!strings.Contains(got[0], "anchor, Foreseeing: the own secrets named foreseen differs") {
t.Fatalf("a secret named foreseen on one path alone is not named: %v", got)
}
// Refused alike is said, not failed; refused differently is a difference.
a, b := composedBoth{Err: "files on laptop needs a credential"}, composedBoth{Err: "files on laptop needs a credential"}
if got := bothWaysDiffer("laptop", "Foreseeing", a, b, true); len(got) != 0 {
t.Fatalf("two compositions that refuse a machine alike differ: %v", got)
}
if got := bothWaysDiffer("laptop", "Foreseeing", a, composedBoth{Body: body("1")}, true); len(got) != 1 ||
!strings.Contains(got[0], "the refusal differs") {
t.Fatalf("a machine one path refuses and the other composes is not named: %v", got)
}
// And what a send made: only the entries that differ, per machine, never the whole lists.
long := made{Ports: []string{"anchor files:80->8080", "anchor web:81->8081", "laptop db:5432->5432"},
OwnSecrets: []string{"anchor files/admin (made)", "laptop db/root (made)"}}
other2 := made{Ports: []string{"anchor files:80->8080", "anchor web:81->9091", "laptop db:5432->5432", "laptop cache:6379->6379"},
OwnSecrets: []string{"anchor files/admin (made)", "laptop db/root (accepted)"}}
got = madeDiffer("the send", long, other2, nil)
if len(got) != 2 || !strings.Contains(got[0], "made only by mesh-sdk: laptop cache:6379") ||
!strings.Contains(got[0], "anchor web:81 (the controller's 8081, mesh-sdk's 9091)") ||
strings.Contains(got[0], "anchor files:80") || strings.Contains(got[0], "laptop db:5432") ||
!strings.Contains(got[1], "laptop db/root (the controller's (made), mesh-sdk's (accepted))") ||
strings.Contains(got[1], "anchor files/admin") {
t.Fatalf("the send's differences are not only the differing entries: %v", got)
}
// A machine refused on either side is left out of both.
if got := madeDiffer("the send", long, other2, map[string]bool{"anchor": true, "laptop": true}); len(got) != 0 {
t.Fatalf("a refused machine is compared: %v", got)
}
}
@@ -8,6 +8,14 @@ import (
"sort"
)
// made is what a send made in a store, by key and never by value: the machine ports given, the pair credentials and the
// own secrets.
type made struct {
Ports []string
Pairs []string
OwnSecrets []string
}
// madeIn is what a store holds that a send makes, by key and never by value.
func madeIn(ctx context.Context, open *stores) (made, error) {
var out made