Skip to content
quarry Artifact [9d4a413a8a]
Login

Artifact [9d4a413a8a]

Artifact 9d4a413a8aed107c428604151189440145ecfa8b03cda22d2c0ac4b51c18c9b7:


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 ""
}