package link import ( "context" "encoding/json" "strings" "testing" "github.com/nats-io/nats.go" "github.com/novox/mesh-controller/internal/testbus" ) // An answer too large for one message is recorded as paged, and a call's record says so: `calls` no // longer reads "answered" for an answer nobody received (novox/hq issue 314). func TestAPagedAnswerIsSaidInCalls(t *testing.T) { l, a := NewCallLog(), newAnswers(t) big := strings.Repeat("x", 5000) l.serveCallWithin("mesh-controller", "conditions", nil, "_INBOX.operator.ABCDEFGHIJKLMNOPQRSTUV", func(context.Context, json.RawMessage) (any, error) { return big, nil }, a.respond, 2048, nil) got := a.only() said, _ := got["error"].(string) paged, _ := got["paged"].(map[string]any) if !strings.Contains(said, "more than the bus carries") || paged == nil || paged["parts"].(float64) < 3 { t.Fatalf("answered %v", got) } recent, _ := l.Recent() if len(recent) != 1 || recent[0].Paged == "" || strings.Contains(string(recent[0].Answer), "in full") { t.Fatalf("kept %+v", recent[0]) } } // An answer the bus refuses anyway is said to its caller and to `calls`, never only to the journal. func TestAnAnswerTheBusRefusesIsSaid(t *testing.T) { l := NewCallLog() var sent [][]byte respond := func(body []byte) error { if len(body) > 1000 { return nats.ErrMaxPayload } sent = append(sent, body) return nil } l.serveCallWithin("mesh-controller", "status", nil, "_INBOX.x.1", func(context.Context, json.RawMessage) (any, error) { return strings.Repeat("y", 4000), nil }, respond, 0, nil) if len(sent) != 1 || !strings.Contains(string(sent[0]), "would not carry it") { t.Fatalf("the caller was told %q", sent) } recent, _ := l.Recent() if recent[0].Refused == "" { t.Fatalf("calls does not say the answer was not sent: %+v", recent[0]) } } // A part goes only to the caller its answer was paged to, on the verb that answered it. func TestAPartGoesOnlyToItsCaller(t *testing.T) { conn, err := nats.Connect(testbus.URL(t)) if err != nil { t.Fatal(err) } defer conn.Close() l := NewCallLog() c := l.begin("mesh-controller", "status", nil, "_INBOX.operator.ABCDEFGHIJKLMNOPQRSTUV") l.page(c, []byte(strings.Repeat("z", 5000)), 2048) sub, err := conn.Subscribe("pages.test", func(m *nats.Msg) { l.servePage("mesh-controller", "status", m, nil) }) if err != nil { t.Fatal(err) } defer sub.Unsubscribe() _, err = askPart(context.Background(), conn, "pages.test", Paged{Call: c.ID, Parts: 3}, 0) if err == nil || !strings.Contains(err.Error(), "another caller") { t.Fatalf("a part was handed to a caller it was not paged to: %v", err) } _, err = askPart(context.Background(), conn, "pages.test", Paged{Call: "call-0-0", Parts: 3}, 0) if err == nil || !strings.Contains(err.Error(), "not held here") { t.Fatalf("a part of nothing held: %v", err) } } // A part that cannot be had fails the whole answer, naming it — never a shorter answer. func TestAMissingPartFailsTheWhole(t *testing.T) { conn, err := nats.Connect(testbus.URL(t)) if err != nil { t.Fatal(err) } defer conn.Close() sub, err := conn.Subscribe("pages.none", func(m *nats.Msg) { _ = m.Respond([]byte(`{"error":"gone"}`)) }) if err != nil { t.Fatal(err) } defer sub.Unsubscribe() first, _ := json.Marshal(map[string]any{"error": "too large", "paged": Paged{Call: "call-1-1", Bytes: 10, Parts: 2}}) _, err = Whole(context.Background(), conn, "pages.none", first) if err == nil || !strings.Contains(err.Error(), "part 0") || !strings.Contains(err.Error(), "gone") { t.Fatalf("a missing part read as %v", err) } if plain, err := Whole(context.Background(), conn, "pages.none", []byte(`{"result":1}`)); err != nil || string(plain) != `{"result":1}` { t.Fatalf("an answer that fits was changed: %s %v", plain, err) } }