Skip to content
quarry Artifact [7963a27883]
Login

Artifact [7963a27883]

Artifact 7963a27883d685f413948214e7b6944224fcd0a2abf2f9f492bf842a5523c152:


package web

import (
	"encoding/xml"
	"net/http"
	"strings"
	"testing"
)

// A visitor who cannot sign in can follow a repository (7a608b5b): the
// timeline is an Atom feed whose items link where the timeline rows
// do, it keeps the page's filters, and the pages announce it.
func TestAVisitorCanFollowARepositoryTimeline(t *testing.T) {
	srv := activityServer(t)
	resp, err := http.Get(srv.URL + "/r/alpha/timeline.atom")
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 || !strings.HasPrefix(resp.Header.Get("Content-Type"), "application/atom+xml") {
		t.Fatalf("FAIL: status %d, type %q", resp.StatusCode, resp.Header.Get("Content-Type"))
	}
	var f atomFeed
	if err := xml.NewDecoder(resp.Body).Decode(&f); err != nil {
		t.Fatalf("FAIL: not Atom: %v", err)
	}
	if len(f.Entries) != 4 || f.Updated != "2026-08-04T16:00:00Z" {
		t.Fatalf("FAIL: %d entries updated %q, want 4 and the newest check-in's time", len(f.Entries), f.Updated)
	}
	ids := map[string]bool{}
	for _, e := range f.Entries {
		if !strings.HasPrefix(e.Link.Href, srv.URL+"/r/alpha/checkin/") || e.Author.Name == "" || ids[e.ID] {
			t.Fatalf("FAIL: entry %+v: link must be an absolute check-in URL, author named, id unique", e)
		}
		ids[e.ID] = true
	}

	_, all := getPage(t, srv, "/r/alpha/timeline.atom")
	_, feat := getPage(t, srv, "/r/alpha/timeline.atom?branch=feat")
	if strings.Count(feat, "<entry>") >= strings.Count(all, "<entry>") || !strings.Contains(feat, "timeline.atom?branch=feat") {
		t.Fatal("FAIL: a branch feed should narrow the entries and name itself with the filter")
	}

	_, page := getPage(t, srv, "/r/alpha/timeline?branch=feat")
	if !strings.Contains(page, `rel="alternate" type="application/atom+xml"`) {
		t.Fatal("FAIL: the page head does not announce the feed")
	}
	if !strings.Contains(page, `data-hook="timeline-feed"`) || !strings.Contains(page, `/r/alpha/timeline.atom?branch=feat`) {
		t.Fatal("FAIL: the timeline's feed link does not carry the branch filter")
	}
	t.Logf("ok: 4 entries, newest %s; branch feed %d of %d; page announces and links it",
		f.Updated, strings.Count(feat, "<entry>"), strings.Count(all, "<entry>"))
}

// A feed item says what kind of event it is exactly once: the stored
// comment of a ticket or landing already starts with the word.
func TestAFeedItemNamesItsKindOnce(t *testing.T) {
	cases := []struct{ kind, head, want string }{
		{"t", "Ticket [bf5fbed5d6] Mirror admin", "Ticket [bf5fbed5d6] Mirror admin"},
		{"l", "Landing accepted: 7a608b5b", "Landing accepted: 7a608b5b"},
		{"lc", "Landing comment on 7a608b5b", "Landing comment on 7a608b5b"},
		{"w", "Changes to wiki page Home", "Wiki: Changes to wiki page Home"},
		{"e", "Technote: release 1.0", "Note: release 1.0"},
		{"f", "Re: build fails", "Forum: Re: build fails"},
		{"ci", "fix the parser", "fix the parser"},
	}
	for _, c := range cases {
		if got := feedTitle(c.kind, c.head); got != c.want {
			t.Errorf("FAIL %s: %q, want %q", c.kind, got, c.want)
		}
	}
	t.Log("ok: tickets and landings keep their own word; wiki, notes and forum are labelled; check-ins bare")
}

// Two changes to one ticket are two different feed entries to a reader.
func TestAFeedTicketEntrySaysWhatChanged(t *testing.T) {
	base := timelineEntry{Kind: "t", Comment: "Ticket [2ee2bc83b1] held-file probe"}
	opened, closed := base, base
	opened.Change = "opened"
	closed.Change = "status Closed, resolution Fixed"
	h1, s1 := feedText(opened)
	h2, s2 := feedText(closed)
	if h1 == h2 || s1 == s2 {
		t.Fatalf("FAIL: two changes read alike: %q / %q", h1, h2)
	}
	if h2 != "Ticket [2ee2bc83b1] held-file probe — status Closed, resolution Fixed" {
		t.Errorf("FAIL: %q", h2)
	}
	if h, _ := feedText(base); h != "Ticket [2ee2bc83b1] held-file probe" {
		t.Errorf("FAIL: a row with no change summary grew a suffix: %q", h)
	}
}