//go:build integration package runtime import ( "context" "encoding/json" "strings" "sync" "testing" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" "github.com/novox/mesh-tools/node-tools/internal/bus" mt "github.com/novox/mesh-tools/node-tools/internal/meshtest" ) // Design 47 G ยง559, A-153: against the real bus a publish with ack answers the sequence the bus gave it, and a subscription // for new messages hears only what is published after it was made, on the subject mesh-sdk's one rule names for the event; // a wildcard is refused. func TestAnAckedPublishAnswersItsSequenceAndANewSubscriptionHearsOnlyWhatComesAfter(t *testing.T) { mt.New(t) nc, err := nats.Connect(mt.URL(t)) if err != nil { t.Fatal(err) } t.Cleanup(nc.Close) js, _ := jetstream.New(nc) ctx := context.Background() if _, err := js.CreateStream(ctx, jetstream.StreamConfig{Name: "SEATEVENTS", Subjects: []string{"mesh.seat.mesh-conditions.event.>"}}); err != nil { t.Fatal(err) } conn := connect(t, "node-tools", "anchor") publish := func(body string) { t.Helper() if _, err := js.Publish(ctx, "mesh.seat.mesh-conditions.event.findings-asked", []byte(body)); err != nil { t.Fatal(err) } } publish(`{"before":true}`) var mu sync.Mutex var heard []string stop, err := conn.SubscribeNew("mesh-conditions.findings-asked", func(raw json.RawMessage) error { var env bus.Envelope _ = json.Unmarshal(raw, &env) mu.Lock() heard = append(heard, env.Key+" "+string(env.Body)) mu.Unlock() return nil }) if err != nil { t.Fatal(err) } defer stop() publish(`{"after":true}`) publish(`{"after":2}`) mt.Until(t, func() error { mu.Lock() defer mu.Unlock() if len(heard) < 2 { return context.DeadlineExceeded } return nil }) mu.Lock() if len(heard) != 2 || heard[0] != `mesh-conditions.findings-asked {"after":true}` { t.Errorf("heard %v, want the messages after the subscription alone, in order", heard) } mu.Unlock() for name, want := range map[string]string{"mesh-conditions.*": "[A-Za-z0-9_-]"} { if _, err := conn.SubscribeNew(name, func(json.RawMessage) error { return nil }); err == nil || !strings.Contains(err.Error(), want) { t.Errorf("a subscription for %s answered %v, want one naming %q", name, err, want) } } first, err := conn.PublishAsAcked("node-tools", bus.Envelope{Key: "finding-written", Body: json.RawMessage(`{}`)}) second, err2 := conn.PublishAsAcked("node-tools", bus.Envelope{Key: "finding-written", Body: json.RawMessage(`{}`)}) if err != nil || err2 != nil || first == 0 || second != first+1 { t.Errorf("acknowledged publishes answered %d (%v) and %d (%v), want consecutive sequences", first, err, second, err2) } }