Skip to content
quarry Artifact [739417546e]
Login

Artifact [739417546e]

Artifact 739417546e1ebc6434e67c20836c173ad8e4a2a471e0973499a787ce20d596c3:


package events

import (
	"testing"
	"time"
)

func TestAttendSequencesPerAccount(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	var seqs []uint64
	for i := 0; i < 3; i++ {
		seq, err := ev.Attend("alice", AttentionEvent{Kind: "mention", Repo: "alpha", From: "bob"})
		if err != nil {
			t.Fatal(err)
		}
		seqs = append(seqs, seq)
	}
	for i := 1; i < len(seqs); i++ {
		if seqs[i] <= seqs[i-1] {
			t.Fatalf("sequence went backwards: %v", seqs)
		}
	}
}

// Two accounts share one stream, so every per-account number must come
// from a subject-filtered view — the exact bug chat.Unread documents.
func TestAttentionAccountsAreIsolated(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	for i := 0; i < 3; i++ {
		if _, err := ev.Attend("alice", AttentionEvent{Kind: "mention"}); err != nil {
			t.Fatal(err)
		}
	}
	if _, err := ev.Attend("bob", AttentionEvent{Kind: "mention"}); err != nil {
		t.Fatal(err)
	}
	a, err := ev.AttentionPending("alice", 0)
	if err != nil {
		t.Fatal(err)
	}
	b, err := ev.AttentionPending("bob", 0)
	if err != nil {
		t.Fatal(err)
	}
	if a != 3 || b != 1 {
		t.Fatalf("pending alice=%d bob=%d, want 3 and 1", a, b)
	}
	recent, err := ev.RecentAttention("bob", 10)
	if err != nil {
		t.Fatal(err)
	}
	if len(recent) != 1 {
		t.Fatalf("bob has %d recent events, want 1", len(recent))
	}
}

func TestTailAttentionFromSeq(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	var seqs []uint64
	for _, s := range []string{"one", "two", "three"} {
		seq, err := ev.Attend("alice", AttentionEvent{Kind: "mention", Summary: s})
		if err != nil {
			t.Fatal(err)
		}
		seqs = append(seqs, seq)
	}
	ch, cancel := ev.TailAttention("alice", seqs[0])
	defer cancel()
	var got []string
	for i := 0; i < 2; i++ {
		select {
		case e, open := <-ch:
			if !open {
				t.Fatal("tail closed early")
			}
			got = append(got, e.Summary)
			if e.Seq != seqs[i+1] {
				t.Fatalf("event %d carries seq %d, want %d", i, e.Seq, seqs[i+1])
			}
		case <-time.After(5 * time.Second):
			t.Fatal("tail delivered nothing")
		}
	}
	if got[0] != "two" || got[1] != "three" {
		t.Fatalf("tail from %d replayed %v, want [two three]", seqs[0], got)
	}
}

// Pending counts from a read pointer, which is what the local inbox
// count is: "events after the pointer", never last-minus-since.
func TestAttentionPendingSince(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	var last uint64
	for i := 0; i < 4; i++ {
		seq, err := ev.Attend("alice", AttentionEvent{Kind: "mention"})
		if err != nil {
			t.Fatal(err)
		}
		if i == 1 {
			last = seq
		}
	}
	n, err := ev.AttentionPending("alice", last)
	if err != nil {
		t.Fatal(err)
	}
	if n != 2 {
		t.Fatalf("pending after %d = %d, want 2", last, n)
	}
}

func TestAttendRejectsUnroutableAccount(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	for _, bad := range []string{"", "a.b", "*", ">", "with space"} {
		if _, err := ev.Attend(bad, AttentionEvent{Kind: "mention"}); err == nil {
			t.Fatalf("Attend accepted account %q", bad)
		}
	}
}

// A resume point older than the window is a resume that looks
// successful and is not, so the tail says so before replaying.
func TestTailAttentionSignalsGap(t *testing.T) {
	ev, err := Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	defer ev.Close()
	// Somebody else's events come first, so alice's own first sequence
	// is well above the resume point asked for below.
	for i := 0; i < 3; i++ {
		if _, err := ev.Attend("bob", AttentionEvent{Kind: "mention"}); err != nil {
			t.Fatal(err)
		}
	}
	seq, err := ev.Attend("alice", AttentionEvent{Kind: "mention"})
	if err != nil {
		t.Fatal(err)
	}
	// Ask to resume from well before anything this subject holds.
	ch, cancel := ev.TailAttention("alice", 1)
	defer cancel()
	select {
	case e := <-ch:
		if e.Kind != KindAttnGap {
			t.Fatalf("first delivery is %q, want %q", e.Kind, KindAttnGap)
		}
		if e.Seq != seq || e.Ref != 1 {
			t.Fatalf("gap carries seq=%d ref=%d, want %d and 1", e.Seq, e.Ref, seq)
		}
	case <-time.After(5 * time.Second):
		t.Fatal("no gap signal")
	}
}