package broker import ( "os" "testing" "github.com/nats-io/nats.go" ) // Raising the bus's objects against a real server. // // The pure tests above say what is asked for and in what order. Only a server can say whether it // accepts them — and two of these are claims about the server's own behaviour that nothing else // could answer: that asserting twice changes nothing, and that a consumer really is bound to the one // subject its node is allowed to read. // // docker run -d --rm --name t -p 14227:4222 nats:2.10-alpine -js // MESH_TEST_NATS=nats://127.0.0.1:14227 go test ./internal/broker/ -run TestRaising func aLiveBus(t *testing.T) *JetStream { t.Helper() url := os.Getenv("MESH_TEST_NATS") if url == "" { t.Skip("MESH_TEST_NATS unset") } js, err := Dial(url) if err != nil { t.Fatal(err) } t.Cleanup(js.Close) // Deleted before, so what this test asserts is what it finds — and after, so the next test does // not inherit it. Deleting a stream takes its consumers with it, which is why this is enough. clear := func() { for _, s := range MeshStreams() { _ = js.Context().DeleteStream(s.Name) } } clear() t.Cleanup(clear) return js } // Every object the mesh's own traffic needs, accepted by a real server, and asserting again changes // nothing — which is the whole requirement, because this runs on every start. func TestRaisingTheBusIsAcceptedAndIdempotent(t *testing.T) { js := aLiveBus(t) if err := Raise(js, []string{"anchor", "laptop"}); err != nil { t.Fatalf("a real server refused the mesh's own objects: %v", err) } // Twice, with nothing in between. A start that failed the second time is a controller that // cannot restart. if err := Raise(js, []string{"anchor", "laptop"}); err != nil { t.Fatalf("asserting the bus's objects a second time failed, so a restart would: %v", err) } // And again with a machine that was not there before, which is what enrolling one is. if err := Raise(js, []string{"anchor", "laptop", "workstation"}); err != nil { t.Fatalf("a machine joining an already-raised bus was refused: %v", err) } for _, s := range MeshStreams() { if _, err := js.Context().StreamInfo(s.Name); err != nil { t.Errorf("stream %s is not there: %v", s.Name, err) } } for _, c := range MeshConsumers() { if _, err := js.Context().ConsumerInfo(c.Stream, c.Name); err != nil { t.Errorf("the controller's consumer on %s is not there: %v", c.Stream, err) } } for _, node := range []string{"anchor", "laptop", "workstation"} { info, err := js.Context().ConsumerInfo("NODES", node) if err != nil { t.Errorf("%s has no way to hear its declaration: %v", node, err) continue } // **Its own subject and no other node's.** A consumer filtered on anything wider is a node // reading another machine's declaration, and its own ack grant would not cover it either. if info.Config.FilterSubject != "mesh.node."+node+".declare" { t.Errorf("%s's consumer reads %q", node, info.Config.FilterSubject) } if info.Config.AckPolicy != nats.AckExplicitPolicy { t.Errorf("%s's consumer acknowledges on delivery, so a declaration it died applying is "+ "never sent again", node) } } } // The store window needs unlimited redelivery on CONTROL: the bound belongs to the controller, and a // server that dead-lettered first would discard the push the stream exists to protect. func TestTheControlConsumerDoesNotDeadLetterBeforeTheControllerGivesUp(t *testing.T) { js := aLiveBus(t) if err := Raise(js, nil); err != nil { t.Fatal(err) } info, err := js.Context().ConsumerInfo("CONTROL", ControllerName) if err != nil { t.Fatal(err) } if info.Config.MaxDeliver > 0 { t.Fatalf("max-deliver is %d: a push held through a store restart would be dead-lettered "+ "before the controller finished deciding about it", info.Config.MaxDeliver) } }