package e2e import ( "net/http" "net/url" "os/exec" "strings" "testing" ) // Chapter 19.2: participation is subscription, and a reply pushed from a clone is a reply. func TestAReplyPushedFromACloneReachesTheInbox(t *testing.T) { if _, err := exec.LookPath("git"); err != nil { t.Skip("git is not installed") } in := newInstance(t) john := in.account("john") mark := in.account("mark") seed(t, in, john, "john", "johnbot") // John opens a thread here, which is the door that already recorded an event. resp := post(t, in, "john", "/john/johnbot/threads", url.Values{ "title": {"the reconnect loop spins"}, "body": {"it retries with no delay"}, }) if resp.StatusCode != http.StatusFound { t.Fatalf("opening a thread answered %d", resp.StatusCode) } // Mark replies the other way, by pushing a note from his clone, which chapter 3 calls the point. work := clone(t, in, mark, "/john/johnbot") run(t, work, "git", "fetch", "-q", in.url(mark, "/john/johnbot"), "refs/notes/threads/1:refs/notes/threads/1") run(t, work, "git", "notes", "--ref=threads/1", "append", "-m", "mark\ntime: 2026-08-19T00:00:00Z\n\nseen it on a flaky link too") run(t, work, "git", "push", "-q", in.url(mark, "/john/johnbot"), "refs/notes/threads/1:refs/notes/threads/1") // John owns the repository, so the reply must be in his inbox. body := inboxOf(t, in, "john") if !strings.Contains(body, "mark replied") { t.Errorf("a reply pushed from a clone is not in the owner's inbox:\n%s", body) } // And mark took part, so he hears about what comes next without pressing anything. if body := inboxOf(t, in, "mark"); !strings.Contains(body, "reconnect loop") { t.Errorf("pushing a reply did not subscribe the author to the thread:\n%s", body) } } func inboxOf(t *testing.T, in *instance, account string) string { t.Helper() token, err := in.db.NewSession(t.Context(), account) if err != nil { t.Fatal(err) } req, err := http.NewRequest(http.MethodGet, in.http.URL+"/inbox", nil) if err != nil { t.Fatal(err) } req.AddCookie(&http.Cookie{Name: "barerepo_session", Value: token}) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() return readAll(t, resp) }