Skip to content
quarry Artifact [1479a1db21]
Login

Artifact [1479a1db21]

Artifact 1479a1db21319657fae8ccc1dabd48c92b4bb6524f29c8dbad1f0a8c1f016bbf:


package oltp

import "testing"

// Chat read-state advances only forward — a re-read at the same seq
// (or an older one arriving out of order) must not move the pointer
// backward or generate a write.
func TestChatReadAdvancesForwardOnly(t *testing.T) {
	db := testDB(t)
	if got := db.ChatReadSeq("r", "alice"); got != 0 {
		t.Fatalf("unread channel read seq = %d, want 0", got)
	}
	if err := db.MarkChatRead("r", "alice", 10); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", "alice"); got != 10 {
		t.Fatalf("read seq = %d, want 10", got)
	}
	// A revisit with nothing new (or older) does not move the pointer.
	if err := db.MarkChatRead("r", "alice", 5); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", "alice"); got != 10 {
		t.Fatalf("read seq regressed to %d after an older mark, want 10", got)
	}
	if err := db.MarkChatRead("r", "alice", 10); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", "alice"); got != 10 {
		t.Fatalf("read seq = %d after a same-seq revisit, want 10", got)
	}
	// A new message arrives; the pointer only advances on an explicit mark.
	if err := db.MarkChatRead("r", "alice", 42); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", "alice"); got != 42 {
		t.Fatalf("read seq = %d, want 42", got)
	}
}

// Read-state is per account and per repo, same isolation as forum_read.
func TestChatReadIsPerAccountAndRepo(t *testing.T) {
	db := testDB(t)
	if err := db.MarkChatRead("r", "alice", 10); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", "bob"); got != 0 {
		t.Fatalf("alice's mark leaked to bob: seq = %d, want 0", got)
	}
	if got := db.ChatReadSeq("other", "alice"); got != 0 {
		t.Fatalf("alice's mark leaked across repos: seq = %d, want 0", got)
	}
	if got := db.ChatReadSeq("r", ""); got != 0 {
		t.Fatalf("anonymous read seq = %d, want 0", got)
	}
	if err := db.MarkChatRead("r", "", 99); err != nil {
		t.Fatal(err)
	}
	if got := db.ChatReadSeq("r", ""); got != 0 {
		t.Fatalf("anonymous mark must be a no-op, got seq = %d", got)
	}
}

// ChatReadAll answers every repo's pointer for one account in one call.
func TestChatReadAll(t *testing.T) {
	db := testDB(t)
	if err := db.MarkChatRead("r1", "alice", 5); err != nil {
		t.Fatal(err)
	}
	if err := db.MarkChatRead("r2", "alice", 20); err != nil {
		t.Fatal(err)
	}
	if err := db.MarkChatRead("r1", "bob", 100); err != nil {
		t.Fatal(err)
	}
	all, err := db.ChatReadAll("alice")
	if err != nil {
		t.Fatal(err)
	}
	if all["r1"] != 5 || all["r2"] != 20 {
		t.Fatalf("ChatReadAll(alice) = %v, want {r1:5, r2:20}", all)
	}
	if len(all) != 2 {
		t.Fatalf("ChatReadAll(alice) leaked another account's repo: %v", all)
	}
	all, err = db.ChatReadAll("")
	if err != nil {
		t.Fatal(err)
	}
	if len(all) != 0 {
		t.Fatalf("ChatReadAll(anonymous) = %v, want empty", all)
	}
}