Skip to content
quarry Artifact [8ade3204d5]
Login

Artifact [8ade3204d5]

Artifact 8ade3204d53438e99af6587b3db8a57a7ade8708380551f92c41946e8d9ee808:


package chat

import (
	"fmt"
	"testing"
	"time"
)

// fakeChannels is a set of in-memory channels behaving like the plane's
// History: oldest-first, newest page first, with a continuation seq.
type fakeChannels struct {
	msgs  map[string][]Message // oldest-first per code
	reads int                  // how many channel reads the merge cost
}

func (f *fakeChannels) back(code string, before uint64, limit int) ([]Message, uint64, error) {
	f.reads++
	all := f.msgs[code]
	// Everything strictly older than `before` (0 = from the newest).
	end := len(all)
	if before > 0 {
		end = 0
		for i, m := range all {
			if m.Seq < before {
				end = i + 1
			}
		}
	}
	start := end - limit
	if start < 0 {
		start = 0
	}
	page := append([]Message(nil), all[start:end]...)
	var next uint64
	if start > 0 {
		next = all[start].Seq
	}
	return page, next, nil
}

// at builds a message stamped n seconds after a fixed base, so the
// interleaving is explicit in the test rather than incidental.
func at(code string, seq uint64, sec int, text string) Message {
	base := time.Date(2026, 9, 13, 12, 0, 0, 0, time.UTC)
	return Message{Seq: seq, Kind: "msg", Repo: code, User: "dan", Text: text,
		At: base.Add(time.Duration(sec) * time.Second).Format(time.RFC3339Nano)}
}

// The merged room is the channels in one time order, and paging through
// it visits every message exactly once. An off-by-one in the cursor
// either drops a line or shows it twice, and both look like a working
// room until somebody counts.
func TestMergeBackVisitsEveryMessageOnce(t *testing.T) {
	f := &fakeChannels{msgs: map[string][]Message{
		"a": {at("a", 1, 0, "a1"), at("a", 2, 3, "a2"), at("a", 3, 6, "a3")},
		"b": {at("b", 10, 1, "b1"), at("b", 11, 4, "b2")},
		"c": {at("c", 100, 2, "c1"), at("c", 101, 5, "c2"), at("c", 102, 7, "c3")},
	}}
	codes := []string{"a", "b", "c"}

	// Newest first across all three: c3(7) a3(6) c2(5) b2(4) a2(3) c1(2) b1(1) a1(0)
	want := []string{"c3", "a3", "c2", "b2", "a2", "c1", "b1", "a1"}

	for _, limit := range []int{1, 2, 3, 8, 20} {
		var got []string
		cur := Cursor{}
		for {
			page, next, err := MergeBack(codes, cur, limit, f.back)
			if err != nil {
				t.Fatal(err)
			}
			if len(page) == 0 {
				break
			}
			for _, m := range page {
				got = append(got, m.Text)
			}
			cur = next
			if len(got) > len(want) {
				t.Fatalf("limit %d: the merge is repeating messages: %v", limit, got)
			}
		}
		if len(got) != len(want) {
			t.Errorf("FAIL: limit %d visited %d messages, want %d: %v", limit, len(got), len(want), got)
			continue
		}
		for i := range want {
			if got[i] != want[i] {
				t.Errorf("FAIL: limit %d position %d is %q, want %q (%v)", limit, i, got[i], want[i], got)
				break
			}
		}
	}
}

// A merged page costs one read per channel, not one per message, and
// not one per page-of-history.
func TestMergeBackReadsEachChannelOncePerPage(t *testing.T) {
	f := &fakeChannels{msgs: map[string][]Message{}}
	for i, code := range []string{"a", "b", "c", "d"} {
		for s := 1; s <= 25; s++ {
			f.msgs[code] = append(f.msgs[code],
				at(code, uint64(i*1000+s), s*4+i, fmt.Sprintf("%s%d", code, s)))
		}
	}
	f.reads = 0
	page, _, err := MergeBack([]string{"a", "b", "c", "d"}, Cursor{}, 10, f.back)
	if err != nil {
		t.Fatal(err)
	}
	if len(page) != 10 {
		t.Fatalf("FAIL: page holds %d, want 10", len(page))
	}
	if f.reads != 4 {
		t.Errorf("FAIL: one page cost %d channel reads, want 4 (one per channel)", f.reads)
	}
}

// Fractional seconds: At is RFC3339Nano, which TRIMS trailing zeros, so
// ".1" and ".05" do not order correctly as strings. The merge must
// parse. This is the case that a lexicographic compare gets wrong.
func TestMergeBackOrdersFractionalSecondsNumerically(t *testing.T) {
	base := time.Date(2026, 9, 13, 12, 0, 0, 0, time.UTC)
	mk := func(code string, seq uint64, ns int, text string) Message {
		return Message{Seq: seq, Repo: code, Text: text,
			At: base.Add(time.Duration(ns)).Format(time.RFC3339Nano)}
	}
	f := &fakeChannels{msgs: map[string][]Message{
		// .05s  — lexicographically "0.05" < "0.1", and numerically too
		"a": {mk("a", 1, 50*int(time.Millisecond), "fifty-ms")},
		// .1s
		"b": {mk("b", 2, 100*int(time.Millisecond), "hundred-ms")},
	}}
	page, _, err := MergeBack([]string{"a", "b"}, Cursor{}, 5, f.back)
	if err != nil {
		t.Fatal(err)
	}
	if len(page) != 2 {
		t.Fatalf("FAIL: %d messages", len(page))
	}
	if page[0].Text != "hundred-ms" {
		t.Errorf("FAIL: newest first put %q before %q", page[0].Text, page[1].Text)
	}
}

// An exhausted channel stays exhausted. Without the Done half of the
// cursor, "read back to 0" is indistinguishable from "not read yet",
// and a finished channel restarts from its newest message — so the
// oldest page of a merged room would repeat the newest one forever.
func TestAnExhaustedChannelDoesNotRestart(t *testing.T) {
	f := &fakeChannels{msgs: map[string][]Message{
		"a": {at("a", 1, 0, "only")},
	}}
	page, cur, err := MergeBack([]string{"a"}, Cursor{}, 5, f.back)
	if err != nil || len(page) != 1 {
		t.Fatalf("first page: %d messages, err %v", len(page), err)
	}
	if !cur.Done["a"] {
		t.Error("FAIL: a fully-read channel is not marked done")
	}
	page, _, err = MergeBack([]string{"a"}, cur, 5, f.back)
	if err != nil {
		t.Fatal(err)
	}
	if len(page) != 0 {
		t.Errorf("FAIL: the exhausted channel served %d messages again", len(page))
	}
}

// The team's own channel cannot collide with a repository's, which is
// keyed by the store's 40-hex project code.
func TestATeamCodeCannotLookLikeAProjectCode(t *testing.T) {
	code := TeamCode("craft-design-group")
	if len(code) == 40 {
		t.Error("FAIL: a team code is the length of a project code")
	}
	for _, r := range code {
		if !((r >= '0' && r <= '9') || (r >= 'a' && r <= 'f')) {
			return // contains something no hex code can
		}
	}
	t.Errorf("FAIL: %q is all hex — it could be a project code", code)
}