test: improve statement coverage from 87.8% to 88.6%#28
Conversation
…, scoring, renderer, store Agent-Logs-Url: https://github.com/faultline-cli/faultline/sessions/241c234e-ffe6-4982-9a8a-8d7e2c2b8018 Co-authored-by: jacoblehr <16401696+jacoblehr@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR increases overall Go statement coverage (87.8% → 88.6%) by adding and extending unit tests across core packages (output formatting, trace/compare helpers, scoring feature extraction, coverage robustness calculations, renderer defaults, and store/SQLite behaviors).
Changes:
- Added new test suites for compare/workflow output formatters and scoring delta-feature extraction.
- Expanded existing tests in trace/compare/coverage/store/renderer/output to cover previously untested branches and edge cases.
- Added store (SQLite) coverage for directory creation, migration idempotency, and BeginRun timestamp handling.
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/trace/trace_test.go | Adds tests for rule-note helpers and why/unmatched-why builders. |
| internal/store/sqlite_extra_test.go | Adds tests for BeginRun zero timestamp, openSQLite parent dir creation, and migrate idempotency. |
| internal/scoring/features_test.go | New tests for deltaPlaybookFeatures branches (missing/unmatched/matched/default weight/etc.). |
| internal/renderer/renderer_test.go | Adds tests for New() option defaults/preservation. |
| internal/output/output_workflow_test.go | New tests for FormatWorkflowText and FormatWorkflowJSON branches. |
| internal/output/output_trace_test.go | Extends tests for trace history window/section join and CI annotations formatting. |
| internal/output/output_test.go | Extends tests for markdown list parsing, score breakdown formatting, match summary, and AnalysisView helpers. |
| internal/output/output_compare_test.go | New tests for compare formatting (text/markdown/json) and helper sections. |
| internal/coverage/report_test.go | Adds tests for FP/FN risk labeling and robustness score computation. |
| internal/compare/compare_test.go | Adds tests ensuring artifact fix-steps/dominant-signals extraction and propagation via compare Build. |
Comments suppressed due to low confidence (1)
internal/store/sqlite_extra_test.go:347
- TestBeginRunWithZeroTimestampUsesNow currently only asserts that BeginRun returns a non-zero handle ID, but it doesn't actually verify the behavior implied by the name/comment (that StartedAt was auto-populated when zero). Since these tests are in package store, consider type-asserting to *sqliteStore and querying the inserted row's started_at (or otherwise observing it) to make this test meaningful.
// StartedAt zero → should be auto-populated by BeginRun.
ctx := context.Background()
handle, err := st.BeginRun(ctx, BeginRunParams{
Surface: "analyze",
SourceKind: "log",
Source: "stdin",
InputHash: "zero-ts-input",
// StartedAt is zero value
})
if err != nil {
t.Fatalf("BeginRun with zero timestamp: %v", err)
}
if handle.ID == 0 {
t.Error("expected non-zero run handle ID")
}
| got := allRuleNote(true) | ||
| if got != "required rule matched" { | ||
| t.Errorf("expected 'required rule matched', got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestAllRuleNoteMissing(t *testing.T) { | ||
| got := allRuleNote(false) | ||
| if got != "required rule was missing" { | ||
| t.Errorf("expected 'required rule was missing', got %q", got) | ||
| } | ||
| } | ||
|
|
||
| // ── anyRuleNote ─────────────────────────────────────────────────────────────── | ||
|
|
||
| func TestAnyRuleNoteMatched(t *testing.T) { | ||
| got := anyRuleNote(true) | ||
| if got != "trigger rule matched the log" { | ||
| t.Errorf("expected 'trigger rule matched the log', got %q", got) | ||
| } | ||
| } |
| got := traceHistoryWindow("not-a-date", "2026-01-01T00:00:00Z") | ||
| if got != "" { | ||
| t.Errorf("expected empty for invalid start, got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestTraceHistoryWindowInvalidEndReturnsEmpty(t *testing.T) { | ||
| got := traceHistoryWindow("2026-01-01T00:00:00Z", "not-a-date") | ||
| if got != "" { | ||
| t.Errorf("expected empty for invalid end, got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestTraceHistoryWindowEndBeforeStartReturnsEmpty(t *testing.T) { | ||
| got := traceHistoryWindow("2026-01-02T00:00:00Z", "2026-01-01T00:00:00Z") | ||
| if got != "" { | ||
| t.Errorf("expected empty when end is before start, got %q", got) | ||
| } | ||
| } |
| r := New(Options{Width: 0, Plain: true}) | ||
| if r.opts.Width != defaultWidth { | ||
| t.Errorf("expected defaultWidth %d for zero-width option, got %d", defaultWidth, r.opts.Width) | ||
| } | ||
| } | ||
|
|
||
| func TestNewNonZeroWidthPreserved(t *testing.T) { | ||
| r := New(Options{Width: 100, Plain: true}) | ||
| if r.opts.Width != 100 { | ||
| t.Errorf("expected width 100 to be preserved, got %d", r.opts.Width) | ||
| } | ||
| } | ||
|
|
||
| func TestNewPlainFlagPreserved(t *testing.T) { | ||
| r := New(Options{Plain: true}) | ||
| if !r.opts.Plain { | ||
| t.Error("expected Plain=true to be preserved") | ||
| } | ||
| } | ||
|
|
||
| func TestNewDarkBackgroundPreserved(t *testing.T) { | ||
| r := New(Options{DarkBackground: true, Width: 88}) | ||
| if !r.opts.DarkBackground { | ||
| t.Error("expected DarkBackground=true to be preserved") | ||
| } | ||
| } |
| got := firstMarkdownListItem("- first item\n- second item") | ||
| if got != "first item" { | ||
| t.Errorf("expected 'first item', got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestFirstMarkdownListItemNumbered(t *testing.T) { | ||
| got := firstMarkdownListItem("1. numbered item\n2. second item") | ||
| if got != "numbered item" { | ||
| t.Errorf("expected 'numbered item', got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestFirstMarkdownListItemParagraphBeforeList(t *testing.T) { | ||
| got := firstMarkdownListItem("Some text.\n\n- the bullet") | ||
| if got != "the bullet" { | ||
| t.Errorf("expected 'the bullet', got %q", got) | ||
| } | ||
| } | ||
|
|
||
| func TestFirstMarkdownListItemEmpty(t *testing.T) { | ||
| got := firstMarkdownListItem("") |
| path := filepath.Join(t.TempDir(), "faultline.db") | ||
| st, _, err := OpenBestEffort(Config{Mode: ModeAuto, Path: path}) | ||
| if err != nil { | ||
| t.Fatalf("OpenBestEffort: %v", err) | ||
| } | ||
| defer st.Close() | ||
|
|
||
| // StartedAt zero → should be auto-populated by BeginRun. | ||
| ctx := context.Background() | ||
| handle, err := st.BeginRun(ctx, BeginRunParams{ | ||
| Surface: "analyze", | ||
| SourceKind: "log", | ||
| Source: "stdin", | ||
| InputHash: "zero-ts-input", | ||
| // StartedAt is zero value | ||
| }) | ||
| if err != nil { | ||
| t.Fatalf("BeginRun with zero timestamp: %v", err) | ||
| } | ||
| if handle.ID == 0 { | ||
| t.Error("expected non-zero run handle ID") | ||
| } | ||
| } | ||
|
|
||
| // ── openSQLite creates the directory if it doesn't exist ───────────────────── | ||
|
|
||
| func TestOpenSQLiteCreatesParentDirectory(t *testing.T) { | ||
| base := t.TempDir() | ||
| // Nested path that doesn't exist yet. | ||
| path := filepath.Join(base, "sub", "dir", "faultline.db") | ||
| st, err := openSQLite(path) | ||
| if err != nil { | ||
| t.Fatalf("openSQLite with missing parent dir: %v", err) | ||
| } | ||
| defer st.Close() | ||
|
|
||
| // Verify the file was created. | ||
| if _, err := os.Stat(path); err != nil { | ||
| t.Errorf("expected db file to exist at %s: %v", path, err) | ||
| } | ||
| } | ||
|
|
||
| // ── migrate is idempotent ───────────────────────────────────────────────────── | ||
|
|
||
| func TestMigrateIsIdempotent(t *testing.T) { | ||
| path := filepath.Join(t.TempDir(), "faultline.db") | ||
| // Open twice — second open will call migrate again on an already-migrated db. | ||
| st1, err := openSQLite(path) | ||
| if err != nil { | ||
| t.Fatalf("first openSQLite: %v", err) | ||
| } | ||
| st1.Close() | ||
|
|
||
| st2, err := openSQLite(path) | ||
| if err != nil { | ||
| t.Fatalf("second openSQLite (idempotent migrate): %v", err) | ||
| } | ||
| defer st2.Close() | ||
| } |
|
@copilot apply changes based on the comments in this thread |
Agent-Logs-Url: https://github.com/faultline-cli/faultline/sessions/c7877584-9b77-475a-9ba1-af5ae9304681 Co-authored-by: jacoblehr <16401696+jacoblehr@users.noreply.github.com>
Applied |
internal/compare,internal/coverage,internal/output,internal/trace,internal/scoring,internal/renderer,internal/storegofmtto all modified test files (trace_test.go,output_test.go,output_trace_test.go,renderer_test.go,sqlite_extra_test.go)