package api
import (
"strings"
"testing"
)
// Both stream ops warn that their sequence is shared.
//
// jva measured it on the attention stream: 16 frames arrived as ids
// 523-542 with 535, 537, 539 and 541 missing, and those belonged to
// other people. The ids are assigned across every subscriber, so the
// ones any one reader receives are a subset. A client author would
// reasonably assume contiguity and build a gap-detector, and that
// detector would report loss constantly on a healthy stream.
//
// The warning is on BOTH ops because chat has the same property and
// nobody had noticed: attn.> and chat.> are each ONE JetStream stream
// filtered by subject, and a chat message's seq is the publish ack's
// stream sequence. I first documented chat as the contrasting case --
// "a reader sees every message here, so ids arrive contiguous" -- and
// that was wrong. Asserted here so the two cannot drift apart again,
// and so a future reader does not have to re-derive which streams are
// shared by reading NATS configuration.
func TestStreamOpsWarnTheSequenceIsShared(t *testing.T) {
for _, op := range []string{"me.attention", "chat.stream"} {
doc := opDoc(t, op)
if doc == "" {
t.Errorf("%s has no Doc", op)
continue
}
for _, want := range []string{"Last-Event-ID", "gaps between them"} {
if !strings.Contains(doc, want) {
t.Errorf("%s's Doc does not mention %q: a client author is not warned that "+
"holes in the id sequence are ordinary", op, want)
}
}
if !strings.Contains(doc, "Do not detect loss by looking for holes") {
t.Errorf("%s's Doc does not tell a client how NOT to check for loss", op)
}
}
}
// opDoc returns one op's Doc as the source declares it, so the
// assertion is about what a reader of the API reference is told.
func opDoc(t *testing.T, name string) string {
t.Helper()
for _, file := range []string{"register.go", "attention.go"} {
src := readSource(t, file)
i := strings.Index(src, `Name: "`+name+`"`)
if i < 0 {
i = strings.Index(src, `Name: "`+name+`"`)
}
if i < 0 {
continue
}
rest := src[i:]
if end := strings.Index(rest, "\n\t\tKind:"); end > 0 {
return rest[:end]
}
return rest[:min(len(rest), 3000)]
}
return ""
}