The facts snapshot, the take preview and the settings history read a layer as it was set; tests that place give the layer through LayerOf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
128 lines
5.0 KiB
Go
128 lines
5.0 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A setting is judged where it is stored, and an impossible one costs a module, not a machine
|
|
// (novox/hq ADR 0163, rule 6): the one judgement, used by SetSettings before storing and by
|
|
// Compose when a definition has moved under a stored setting.
|
|
func TestASettingThatCannotComposeIsRefusedByNameAndLeavesOnlyItsModuleOut(t *testing.T) {
|
|
web := Manifest{Module: "hello-web",
|
|
Listens: []Listening{{Port: 8080, From: FromEverywhere}},
|
|
Resources: []map[string]any{{"id": "server", "type": "container", "name": "hello-web",
|
|
"ports": []any{"8080"}}}}
|
|
for _, c := range []struct {
|
|
name string
|
|
layer map[string]any
|
|
refuse string
|
|
}{
|
|
{"a port the module does not publish", map[string]any{PortsSetting: map[string]any{"9999": 10000}},
|
|
"hello-web gives port 9999 a machine port, and no container of its publishes 9999"},
|
|
{"a mesh-wide port", map[string]any{PortsSetting: map[string]any{"8080": 10000}},
|
|
"a port is a fact about one machine"},
|
|
} {
|
|
from := "anchor"
|
|
if c.name == "a mesh-wide port" {
|
|
from = MeshWideLayer
|
|
}
|
|
err := JudgeSettings(web, []Layer{LayerOf(from, c.layer)}, true)
|
|
if err == nil || !strings.Contains(err.Error(), c.refuse) {
|
|
t.Errorf("%s: judged %v, want %q", c.name, err, c.refuse)
|
|
}
|
|
}
|
|
if err := JudgeSettings(web, []Layer{{From: "anchor", Values: map[string]any{PortsSetting: map[string]any{"8080": 10000}}}}, true); err != nil {
|
|
t.Fatalf("a port the module publishes was refused: %v", err)
|
|
}
|
|
|
|
// Composed, a module whose stored setting no longer works is left out by name; the rest of
|
|
// the machine is declared.
|
|
r := anAdoptedAnchor()
|
|
with := anchorRendering(false)
|
|
with.Settings = SettingsBy{"hello-web": {{From: "anchor", Values: map[string]any{PortsSetting: map[string]any{"9999": 10000}}}}}
|
|
composed, err := r.Compose(with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
why, left := composed.LeftOut["hello-web"]
|
|
if !left || !strings.Contains(why, "no container of its publishes 9999") {
|
|
t.Fatalf("hello-web is not left out by name: %v", composed.LeftOut)
|
|
}
|
|
if len(composed.LeftOut) != 1 {
|
|
t.Fatalf("more than hello-web is left out: %v", composed.LeftOut)
|
|
}
|
|
got := byID(composed.Resources)
|
|
if _, declared := got["hello-web.server"]; declared {
|
|
t.Fatal("the left-out module's container is still declared")
|
|
}
|
|
if _, declared := got["distribution.store"]; !declared {
|
|
t.Fatal("the rest of the machine was not declared")
|
|
}
|
|
if left := r.LeftOut(with.Settings, false); len(left) != 1 || left["hello-web"] == "" {
|
|
t.Fatalf("the judgement a plan reads differs from what compose did: %v", left)
|
|
}
|
|
}
|
|
|
|
// A taken container keeps a found network by a per-machine setting (novox/hq ADR 0163, rule 4):
|
|
// on an adopted machine only, for a container the module declares, and it reaches the container's
|
|
// declaration as the networks it also joins.
|
|
func TestAKeptNetworkReachesTheContainerOnAnAdoptedMachineOnly(t *testing.T) {
|
|
r := anAdoptedAnchor()
|
|
keep := SettingsBy{"hello-web": {LayerOf("anchor",
|
|
map[string]any{NetworksSetting: map[string]any{"server": []any{"predecessor_default"}}})}}
|
|
|
|
with := anchorRendering(true)
|
|
with.Settings = keep
|
|
composed, err := r.Compose(with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(composed.LeftOut) != 0 {
|
|
t.Fatalf("a kept network left a module out: %v", composed.LeftOut)
|
|
}
|
|
server := byID(composed.Resources)["hello-web.server"]
|
|
networks, _ := server["networks"].([]any)
|
|
if len(networks) != 1 || networks[0] != "predecessor_default" {
|
|
t.Fatalf("the container does not join the kept network: %v", server)
|
|
}
|
|
if _, has := byID(composed.Resources)["distribution.store"]["networks"]; has {
|
|
t.Fatal("another container joins a network nobody kept for it")
|
|
}
|
|
|
|
// Converged, the setting reaches nothing it was for, and the module is left out saying so.
|
|
with = anchorRendering(false)
|
|
with.Settings = keep
|
|
composed, err = r.Compose(with)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if why := composed.LeftOut["hello-web"]; !strings.Contains(why, "anchor is converged") {
|
|
t.Fatalf("a kept network on a converged machine: %v", composed.LeftOut)
|
|
}
|
|
|
|
web := r.Modules[4]
|
|
for _, c := range []struct {
|
|
name string
|
|
layer Layer
|
|
want string
|
|
}{
|
|
{"mesh-wide", LayerOf(MeshWideLayer, map[string]any{NetworksSetting: map[string]any{"server": []any{"x"}}}),
|
|
"a found network is a fact about one machine"},
|
|
{"an unknown container", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"db": []any{"x"}}}),
|
|
`names the container "db", which it does not declare`},
|
|
{"not a list", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"server": "x"}}),
|
|
"is a list of network names"},
|
|
{"not a network name", LayerOf("anchor", map[string]any{NetworksSetting: map[string]any{"server": []any{"a/b"}}}),
|
|
"which is not a network name"},
|
|
} {
|
|
_, err := KeptNetworks(web, []Layer{c.layer}, true)
|
|
if err == nil || !strings.Contains(err.Error(), c.want) {
|
|
t.Errorf("%s: %v, want %q", c.name, err, c.want)
|
|
}
|
|
}
|
|
if kept, err := KeptNetworks(web, nil, false); err != nil || kept != nil {
|
|
t.Fatalf("no setting: %v %v", kept, err)
|
|
}
|
|
}
|