| 1 | package autoresearch |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "testing" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // TestFindingsTailEquivalence: newest-N reads match a full scan followed by a |
| 10 | // reverse/limit without writing through a production API. |
| 11 | func TestFindingsTailEquivalence(t *testing.T) { |
| 12 | root := t.TempDir() |
| 13 | taskID := "findings-tail" |
| 14 | taskRoot := writeArchiveFixture(t, root, taskID, "Findings tail equivalence", nil) |
| 15 | for i := 1; i <= 40; i++ { |
| 16 | appendFindingLine(t, taskRoot, Finding{ |
| 17 | ID: fmt.Sprintf("f%d", i), |
| 18 | Kind: "manual", |
| 19 | Summary: fmt.Sprintf("finding %d", i), |
| 20 | Source: FindingSourceManual, |
| 21 | Accepted: true, |
| 22 | CreatedAt: time.Date(2026, 6, 30, 10, 0, i, 0, time.UTC), |
| 23 | }) |
| 24 | } |
| 25 | store := NewStore(root) |
| 26 | all, err := store.Findings(taskID, 0) |
| 27 | if err != nil { |
| 28 | t.Fatalf("Findings full: %v", err) |
| 29 | } |
| 30 | tail, err := store.Findings(taskID, 5) |
| 31 | if err != nil { |
| 32 | t.Fatalf("Findings tail: %v", err) |
| 33 | } |
| 34 | if len(tail) != 5 { |
| 35 | t.Fatalf("tail len = %d", len(tail)) |
| 36 | } |
| 37 | // Newest-first order: first of tail is the latest full entry. |
| 38 | if tail[0].ID != all[0].ID || tail[4].ID != all[4].ID { |
| 39 | t.Fatalf("tail=%+v all[:5]=%+v", tail, all[:5]) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | func TestHeartbeatTailEquivalence(t *testing.T) { |
| 44 | root := t.TempDir() |
| 45 | taskID := "heartbeat-tail" |
| 46 | taskRoot := writeArchiveFixture(t, root, taskID, "Tail read equivalence", nil) |
| 47 | for i := 1; i <= 30; i++ { |
| 48 | appendHeartbeatLine(t, taskRoot, Heartbeat{ |
| 49 | Status: HeartbeatTurnDone, |
| 50 | Iteration: i, |
| 51 | CreatedAt: time.Date(2026, 6, 30, 10, 0, i, 0, time.UTC), |
| 52 | }) |
| 53 | } |
| 54 | store := NewStore(root) |
| 55 | all, err := store.Heartbeats(taskID, 0) |
| 56 | if err != nil { |
| 57 | t.Fatalf("Heartbeats full: %v", err) |
| 58 | } |
| 59 | tail, err := store.Heartbeats(taskID, 3) |
| 60 | if err != nil { |
| 61 | t.Fatalf("Heartbeats tail: %v", err) |
| 62 | } |
| 63 | if len(tail) != 3 { |
| 64 | t.Fatalf("tail len = %d", len(tail)) |
| 65 | } |
| 66 | want := all[len(all)-3:] |
| 67 | for i := range want { |
| 68 | if tail[i].Iteration != want[i].Iteration { |
| 69 | t.Fatalf("tail[%d]=%+v want %+v", i, tail[i], want[i]) |
| 70 | } |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | func TestDirectionsFileIsReadableThroughSummary(t *testing.T) { |
| 75 | root := t.TempDir() |
| 76 | taskID := "directions-readable" |
| 77 | taskRoot := writeArchiveFixture(t, root, taskID, "Legacy fingerprint migration", nil) |
| 78 | summary := "Benchmark the desktop frontend markdown rendering pipeline for large tables" |
| 79 | writeDirections(t, taskRoot, []DirectionTried{{ |
| 80 | Fingerprint: "legacy-slug", |
| 81 | Summary: summary, |
| 82 | FirstSeenIteration: 1, |
| 83 | LastSeenIteration: 1, |
| 84 | Count: 1, |
| 85 | }}) |
| 86 | writeProgress(t, taskRoot, Progress{ |
| 87 | Status: StatusRunning, |
| 88 | Iteration: 1, |
| 89 | CurrentDirection: summary, |
| 90 | UpdatedAt: time.Date(2026, 6, 30, 10, 0, 0, 0, time.UTC), |
| 91 | }) |
| 92 | store := NewStore(root) |
| 93 | got, err := store.Summary(taskID) |
| 94 | if err != nil { |
| 95 | t.Fatalf("Summary: %v", err) |
| 96 | } |
| 97 | if got.CurrentDirection != summary || got.Iteration != 1 { |
| 98 | t.Fatalf("summary = %+v", got) |
| 99 | } |
| 100 | } |
| 101 |