package assign import ( "slices" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" ) func meshOf() Mesh { return Mesh{ Manifests: map[string]catalogue.Manifest{ "mail": {Module: "mail", Version: "1", Requires: []string{"database"}}, "store": {Module: "store", Version: "1", Provides: []catalogue.Offer{{Name: "database"}}}, "web": {Module: "web", Version: "1"}, }, Assigned: map[string][]string{"ace": {"mail", "store"}, "g14": {"web"}}, } } func hasWarning(v delivery.CompositionVerdict, part string) bool { return slices.ContainsFunc(v.Warnings, func(w string) bool { return strings.Contains(w, part) }) } // 1297/AC1 (its unit half): a change touching one module on one machine composes that machine alone, passes, names // the machine and the mesh-sdk it composed with, and says what it did not judge. func TestAChangeOfOneModuleComposesItsMachine(t *testing.T) { c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{"web": {Module: "web", Version: "2"}}} v := Compose(meshOf(), c, "v0.1.14-x", 0) if v.State != delivery.CompositionPass || !slices.Equal(v.Machines, []string{"g14"}) || v.SDK != "v0.1.14-x" { t.Fatalf("composition = %+v, want a pass on g14 alone", v) } if !hasWarning(v, "issue 1461") { t.Errorf("the verdict does not say what it did not judge: %v", v.Warnings) } if _, err := delivery.EncodeCompositionVerdict(v); err != nil { t.Errorf("the verdict cannot be written: %v", err) } } // 1297/AC2: a change that removes a module a machine runs fails, naming the machine and the module. func TestRemovingAModuleAMachineRunsFails(t *testing.T) { c := Change{Group: "g", Heads: "0123456789ab", Removed: []string{"web"}} v := Compose(meshOf(), c, "sdk", 0) if v.State != delivery.CompositionFail || v.Machine != "g14" || !strings.Contains(v.Words, "g14 runs web") { t.Fatalf("composition = %+v, want a fail naming g14 and web", v) } } // 1297/B3c: a machine that composes on the trunk and not with the change fails it, naming the machine and why; the // consumer's machine is touched when its provider changes. func TestAMachineTheChangeBreaksFails(t *testing.T) { c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{"store": {Module: "store", Version: "2"}}} v := Compose(meshOf(), c, "sdk", 0) if v.State != delivery.CompositionFail || v.Machine != "ace" || !strings.Contains(v.Words, "database") { t.Fatalf("composition = %+v, want a fail on ace naming the provision nothing provides", v) } } // 1297/B3c: what was already broken on the trunk is said and fails nothing. func TestWhatWasBrokenBeforeFailsNothing(t *testing.T) { m := meshOf() m.Manifests["store"] = catalogue.Manifest{Module: "store", Version: "1"} c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{"mail": {Module: "mail", Version: "2", Requires: []string{"database"}}}} v := Compose(m, c, "sdk", 0) if v.State != delivery.CompositionPass || !hasWarning(v, "on the trunk either") { t.Fatalf("composition = %+v, want a pass saying ace was already broken", v) } } // 1297/B3c: an unreadable manifest and a module defined twice fail; more than twelve rebuilt modules warn. func TestUnreadableTwiceAndWide(t *testing.T) { c := Change{Group: "g", Heads: "0123456789ab", Unreadable: map[string]string{"web": "unknown field \"scope\""}, Rebuilt: 13} v := Compose(meshOf(), c, "sdk", 0) if v.State != delivery.CompositionFail || !strings.Contains(v.Words, "web cannot be read") || !hasWarning(v, "rebuilds 13 modules") { t.Fatalf("composition = %+v", v) } c = Change{Group: "g", Heads: "0123456789ab", Twice: map[string][]string{"web": {"novox/a", "novox/b"}}, Rebuilt: 12} v = Compose(meshOf(), c, "sdk", 0) if v.State != delivery.CompositionFail || !strings.Contains(v.Words, "web is defined twice") || hasWarning(v, "rebuilds") { t.Fatalf("composition = %+v", v) } } // 1622's criterion as R4g delivers it: a group with a mesh-sdk member is composed with the assigner's own mesh-sdk, and // the verdict says so, naming the member's head it did not use; a group without one says nothing of it. func TestAGroupWithAnSDKMemberSaysWhichSDKComposedIt(t *testing.T) { c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{"web": {Module: "web", Version: "2"}}, SDKHead: "f625d98713e4"} v := Compose(meshOf(), c, "v0.1.14-own", 0) if !hasWarning(v, "not the group's mesh-sdk head f625d98713e4") || !hasWarning(v, "v0.1.14-own") || !hasWarning(v, "1623") { t.Errorf("the verdict does not say it composed with its own mesh-sdk: %v", v.Warnings) } c.SDKHead = "" if v := Compose(meshOf(), c, "v0.1.14-own", 0); hasWarning(v, "mesh-sdk head") { t.Errorf("a group without a mesh-sdk member says it has one: %v", v.Warnings) } } // 1297/B3a: the machines touched are those running a moved module or a consumer of what it provides; an added module // assigned nowhere touches none, and the verdict says so. func TestTouched(t *testing.T) { m := meshOf() if got := Touched(m, Change{Manifests: map[string]catalogue.Manifest{"store": m.Manifests["store"]}}); !slices.Equal(got, []string{"ace"}) { t.Errorf("touched by store = %v", got) } m.Assigned["g14"] = append(m.Assigned["g14"], "mail") if got := Touched(m, Change{Manifests: map[string]catalogue.Manifest{"store": m.Manifests["store"]}}); !slices.Equal(got, []string{"ace", "g14"}) { t.Errorf("touched by store with a consumer on g14 = %v", got) } c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{"new": {Module: "new", Version: "1"}}} if v := Compose(m, c, "sdk", 0); len(v.Machines) != 0 || !hasWarning(v, "touches no machine") || v.State != delivery.CompositionPass { t.Errorf("an added module assigned nowhere: %+v", v) } } // ADR 0537 clause 1: every verdict names, by machine, those that could run a moved or added module and are not assigned // it, and those whose grants alone the change alters (the bus's machine, a provider of what the module requires), as not // judged; shanks, providing what web now requires, is composed and so named in neither. A change that alters no grant // names none of the second. func TestTheMachinesNotJudgedAreNamed(t *testing.T) { m := meshOf() m.Manifests["bus"] = catalogue.Manifest{Module: "bus", Version: "1", Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: "mesh"}}} m.Manifests["db"] = catalogue.Manifest{Module: "db", Version: "1", Provides: []catalogue.Offer{{Name: "cache"}}} m.Assigned["novox"] = []string{"bus"} m.Assigned["shanks"] = []string{"db"} c := Change{Group: "g", Heads: "0123456789ab", Manifests: map[string]catalogue.Manifest{ "web": {Module: "web", Version: "2", Reads: []string{"mesh-controller.assignments"}, Requires: []string{"cache"}}}} v := Compose(m, c, "sdk", 0) if !hasWarning(v, "could run a moved or added module and are not assigned it: ace, novox") || !slices.Contains(v.Machines, "shanks") { t.Errorf("the machines that could run web are not named: %v", v.Warnings) } if !hasWarning(v, "whose grants alone the change alters: novox") { t.Errorf("the machines whose grants alone change are not named: %v", v.Warnings) } c.Manifests["web"] = catalogue.Manifest{Module: "web", Version: "3"} if v := Compose(meshOf(), c, "sdk", 0); hasWarning(v, "grants alone") { t.Errorf("a change altering no grant names machines whose grants change: %v", v.Warnings) } }