package hub import ( "strings" "testing" "time" ) // Spec ยง15.26: the tags are random, a call's are shared, and a token is // good for its participation and ten minutes after, and is swept then. func TestReportTokensTagsAndTheAfterLeaveWindow(t *testing.T) { now := time.Date(2026, 10, 3, 15, 40, 0, 0, time.UTC) h := testHub(&now) a, _ := h.Open("r1", "sessA", 1, "Ann", KindMember) b, _ := h.Open("r1", "sessB", 2, "Bob", KindMember) if a.ReportToken == "" || a.ReportToken == b.ReportToken { t.Fatalf("tokens %q %q", a.ReportToken, b.ReportToken) } ra, ok := h.Reporter("r1", "sessA", a.ReportToken) rb, _ := h.Reporter("r1", "sessB", b.ReportToken) if !ok || !ra.Live || ra.CallTag != rb.CallTag || ra.PeerTag == rb.PeerTag || len(ra.CallTag) != 16 { t.Fatalf("reporters %+v %+v", ra, rb) } for _, tag := range []string{ra.CallTag, ra.PeerTag} { for _, s := range []string{"r1", "sessA", "Ann", string(a.Peer)} { if strings.Contains(tag, s) || strings.Contains(s, tag) { t.Fatalf("tag %q derived from %q", tag, s) } } } if ra.Peers[b.Peer] != rb.PeerTag { t.Fatalf("A's reporter maps B to %q, want %q", ra.Peers[b.Peer], rb.PeerTag) } if _, ok := h.Reporter("r1", "sessB", a.ReportToken); ok { t.Fatal("another session's token was honoured") } if _, ok := h.Reporter("r2", "sessA", a.ReportToken); ok { t.Fatal("a token was honoured for another room") } if err := h.Leave("r1", "sessA", a.Peer); err != nil { t.Fatal(err) } if r, ok := h.Reporter("r1", "sessA", a.ReportToken); !ok || r.Live { t.Fatalf("just after leaving: %+v %v, want an ended grant", r, ok) } now = now.Add(ReportAfterLeave + time.Second) h.Tick(testCtx) h.mu.Lock() _, kept := h.reports[a.ReportToken] n := len(h.reports) h.mu.Unlock() if kept || n != 1 { t.Fatalf("after the window: kept %v, %d grants, want A's swept and B's live one left", kept, n) } } // The token table is bounded: past maxReportGrants the grant that ended // longest ago goes first, and a live one never does. func TestReportGrantsAreBounded(t *testing.T) { now := time.Date(2026, 10, 3, 15, 40, 0, 0, time.UTC) h := testHub(&now) live, _ := h.Open("keep", "sessLive", 1, "Ann", KindMember) for i := 0; i < maxReportGrants+10; i++ { now = now.Add(time.Millisecond) res, err := h.Open("r1", "s", int64(i%3), "x", KindMember) if err != nil { t.Fatal(err) } if err := h.Leave("r1", "s", res.Peer); err != nil { t.Fatal(err) } } h.mu.Lock() n := len(h.reports) h.mu.Unlock() if n > maxReportGrants { t.Fatalf("%d grants, bound %d", n, maxReportGrants) } if _, ok := h.Reporter("keep", "sessLive", live.ReportToken); !ok { t.Fatal("a live participation's token was evicted") } }