#173: page verdict shares FaultsFrom judgement so push and landing cannot disagree

This commit is contained in:
2026-08-23 11:24:09 +07:00
parent a7bbeb50e5
commit 820f57a7fa
3 changed files with 137 additions and 17 deletions
+91 -1
View File
@@ -3099,7 +3099,7 @@ func TestOverviewVerdictThreeStates(t *testing.T) {
})
t.Run("lanes need a look", func(t *testing.T) {
seedPass(t, st, store.LanePass{Site: "demonic", RanAt: now.UnixMilli(), Skip: latest.SkipRefusing})
seedPass(t, st, store.LanePass{Site: "demonic", RanAt: now.UnixMilli(), Due: 1, Checked: 0, Skip: "", Refused: 0})
body := overviewBody(t, router, st)
if !strings.Contains(body, "1 lane needs a look") {
t.Errorf("attention verdict missing:\n%s", body)
@@ -3118,6 +3118,96 @@ func TestOverviewVerdictThreeStates(t *testing.T) {
})
}
// TestOverviewVerdictFromFaults mirrors the four fault inputs the notifier judges
// (stall, no-browser-route, sidecar-down, adapter-broken); plus healthy and
// virgin. Each seeds the durable rows the page reads, with no poller running.
func TestOverviewVerdictFromFaults(t *testing.T) {
t.Run("stall is a fault", func(t *testing.T) {
router, st := newWebTestServer(t, testConfig())
now := time.Now()
seedPass(t, st, store.LanePass{Site: "asura", RanAt: now.UnixMilli(), Due: 3, Checked: 0, Skip: "", Refused: 0})
body := overviewBody(t, router, st)
if !strings.Contains(body, "1 lane needs a look") {
t.Errorf("stall fault verdict missing:\n%s", body)
}
})
t.Run("no-browser-route is a fault on plain TLS site", func(t *testing.T) {
router, st := newWebTestServer(t, testConfig())
old := time.Now().Add(-13 * time.Hour).UnixMilli()
seedPass(t, st, store.LanePass{Site: "asura", RanAt: old, Skip: latest.SkipRefusing})
body := overviewBody(t, router, st)
if !strings.Contains(body, "1 lane needs a look") {
t.Errorf("no-browser-route fault verdict missing:\n%s", body)
}
})
t.Run("sidecar-down is one fault even though every browser lane is down", func(t *testing.T) {
router, st := newWebTestServer(t, testConfig())
now := time.Now().UnixMilli()
for _, site := range []string{"comix", "kagane", "novelfull"} {
seedPass(t, st, store.LanePass{Site: site, RanAt: now, Skip: latest.SkipSidecarDown})
}
body := overviewBody(t, router, st)
if !strings.Contains(body, "1 lane needs a look") {
t.Errorf("sidecar-down fault should count as one, want %q:\n%s", "1 lane needs a look", body)
}
if strings.Contains(body, "3 lanes need a look") {
t.Errorf("sidecar-down fault must not count lanes, got 3:\n%s", body)
}
})
t.Run("adapter-broken is a fault over half no_chapter", func(t *testing.T) {
st, dsn := newTestStoreURL(t)
db, err := sql.Open("pgx", dsn)
if err != nil {
t.Fatalf("open %s: %v", dsn, err)
}
defer db.Close()
router := newRouter(st, testConfig())
// Four series of the same site; three hold an old no_chapter row (>12h).
now := time.Now()
old := now.Add(-13 * time.Hour).UnixMilli()
for i := 1; i <= 4; i++ {
key := fmt.Sprintf("asura:a%d", i)
url := fmt.Sprintf("https://asurascans.com/comics/a%d", i)
seedSeriesRow(t, st, db, seriesRowSeed{key: key, url: url, cover: "aaa", checkedAt: now.UnixMilli(), latestNum: floatPtr(1), bookmarks: 1})
}
for i := 1; i <= 3; i++ {
if err := st.RecordSeriesFailure("asura", fmt.Sprintf("a%d", i), "no_chapter", old); err != nil {
t.Fatalf("seed no_chapter failure: %v", err)
}
}
seedPass(t, st, store.LanePass{Site: "asura", RanAt: now.UnixMilli(), Due: 1, Checked: 1})
body := overviewBody(t, router, st)
if !strings.Contains(body, "1 lane needs a look") {
t.Errorf("adapter-broken fault verdict missing:\n%s", body)
}
})
t.Run("healthy when no fault holds", func(t *testing.T) {
router, st := newWebTestServer(t, testConfig())
now := time.Now()
seedPass(t, st, store.LanePass{Site: "asura", RanAt: now.UnixMilli(), Due: 1, Checked: 1})
seedPass(t, st, store.LanePass{Site: "kagane", RanAt: now.UnixMilli(), Skip: latest.SkipAsleep, Due: 1})
body := overviewBody(t, router, st)
if !strings.Contains(body, "all lanes healthy") {
t.Errorf("healthy verdict missing:\n%s", body)
}
})
t.Run("virgin still draws no figures", func(t *testing.T) {
virgin, fresh := newWebTestServer(t, testConfig())
body := overviewBody(t, virgin, fresh)
if !strings.Contains(body, "no Lane has reported yet") {
t.Errorf("virgin verdict missing:\n%s", body)
}
if strings.Contains(body, "all lanes healthy") || strings.Contains(body, "series waiting") {
t.Errorf("virgin verdict draws confident zeroes:\n%s", body)
}
})
}
// The Overview never refreshes itself: the Lane rest is an hour, so a timer
// would re-run a cross-Series join to redraw identical rows. Only the Lanes
// block carries a refresh attribute.