diff options
Diffstat (limited to 'cmds/export_test.go')
| -rw-r--r-- | cmds/export_test.go | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/cmds/export_test.go b/cmds/export_test.go index 0190d39..25eadbb 100644 --- a/cmds/export_test.go +++ b/cmds/export_test.go @@ -115,10 +115,13 @@ func runExportAgainst(t *testing.T, p *pagedServer, dir string, extra ...string) t.Setenv("MINIFLUX_URL", srv.URL) t.Setenv("MINIFLUX_TOKEN", "test-token") + // Ratio/dedup semantics are tested uncapped; the per-feed ceiling has + // its own dedicated test (TestExportNegativesFeedCap). args := append([]string{ "--labels", filepath.Join(dir, "labels.jsonl"), "--state", filepath.Join(dir, "export_state.json"), "--run-out", filepath.Join(dir, "export_run.json"), + "--neg-feed-cap-pct", "0", }, extra...) cmd := &ExportCommand{} @@ -347,6 +350,70 @@ func TestExportNegatives(t *testing.T) { } } +// The per-feed ceiling: one feed's negatives are bounded by the cap, the +// budget then flows to other feeds, and the shortfall (vs. the ratio +// target) is reported rather than silently padded. +func TestExportNegativesFeedCap(t *testing.T) { + now := time.Now() + starred := makeEntries(10, 1000, true, 7) + for i := range starred { + starred[i].PublishedAt = now.Add(-24 * time.Hour) + } + // A big firehose pool and a small quiet feed's worth, both in-window. + big := makeEntries(500, 5000, false, 8) + small := makeEntries(30, 9000, false, 9) + for i := range big { + big[i].PublishedAt = now.Add(-10 * 24 * time.Hour) + } + for i := range small { + small[i].PublishedAt = now.Add(-10 * 24 * time.Hour) + } + p := &pagedServer{starred: starred, read: append(big, small...)} + dir := t.TempDir() + + // 10 positives × ratio 20 = target 200 negatives. Ceiling = 5% × 200 = + // 10 → floor 50 wins; cap is 50/feed. + out := runExportAgainst(t, p, dir, "--neg-days", "100000", "--neg-feed-cap-pct", "5") + + labels, err := core.ReadLabels(filepath.Join(dir, "labels.jsonl")) + if err != nil { + t.Fatal(err) + } + perFeed := map[int64]int{} + negN := 0 + for _, row := range labels { + if row.Label == core.LabelNeg { + perFeed[row.FeedID]++ + negN++ + } + } + if perFeed[8] != 50 { + t.Errorf("firehose feed should be capped at 50, got %d", perFeed[8]) + } + if perFeed[9] != 30 { + t.Errorf("quiet feed should give all 30 of its candidates, got %d", perFeed[9]) + } + if negN != 80 { + t.Errorf("total negatives = %d, want 80 (50 capped + 30 quiet)", negN) + } + if !strings.Contains(out, "capped=") || !strings.Contains(out, "negatives short by 120") { + t.Errorf("shortfall from capping should be reported:\n%s", out) + } + body, err := os.ReadFile(filepath.Join(dir, "export_run.json")) + if err != nil { + t.Fatal(err) + } + var rs runSummary + if err := json.Unmarshal(body, &rs); err != nil { + t.Fatal(err) + } + // All 500 firehose candidates walk the permutation; 50 are accepted, + // the other 450 are skipped by its ceiling (the quiet feed never caps). + if rs.NegCapped != 450 { + t.Errorf("neg_capped = %d, want 450", rs.NegCapped) + } +} + func TestExportNegativesSeededDeterministic(t *testing.T) { dir1, dir2 := t.TempDir(), t.TempDir() runExportAgainst(t, negFixture(4, 120, 0), dir1) |
