Skip to content

fix(import): junit import — stop overwriting + restore test→artifact link#302

Merged
avrabe merged 1 commit into
mainfrom
fix/junit-import-overwrite-and-linkage
May 18, 2026
Merged

fix(import): junit import — stop overwriting + restore test→artifact link#302
avrabe merged 1 commit into
mainfrom
fix/junit-import-overwrite-and-linkage

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 18, 2026

User-reported regressions

Two bugs against `rivet import-results --format junit`:

  1. Silent overwrite on re-import. Same testsuite name → same `run_id` (`junit-{safe_name}`) → identical filename → second import wipes the first.
  2. Test→artifact link dropped on cargo-nextest output. The `artifact_id_for` fallback emits a literal `"classname.name"` concat that can never join back to an artifact. Most cargo-nextest output hits this path.

Fixes

Bug #1 — `suite_to_run` now appends a disambiguator to `run_id`:

  • If `` is set, slugify it: `2026-05-17T06-35-44Z`.
  • Otherwise, hash the suite's case list (name, classname, outcome variant) and append as 16-hex DefaultHasher digest. Idempotent on re-import of same content, distinguishes new content.

Bug #2 — new public `parse_junit_xml_with_markers(xml, markers)` adds a 5th heuristic to `artifact_id_for`. When the existing fallback fires, look up a marker whose `test_name` matches the case name. The CLI (`cmd_import_results_junit`) scans the project's `src/`+`tests/` for `// rivet: verifies REQ-NNN` markers before parsing. Bracketed/direct-classname IDs short-circuit (existing behavior).

Existing `parse_junit_xml` kept working — delegates to the new path with empty markers.

Test plan

  • `cargo test --workspace --lib` — 1003 pass (was 996, +7).
  • `cargo test -p rivet-core --lib junit::` — 22 pass (16 existing + 6 new).
  • `cargo clippy --workspace --all-targets` — clean.
  • `cargo fmt --check` — clean.
  • New tests:
    • `run_id_includes_timestamp_when_present`
    • `run_id_stable_hash_when_no_timestamp`
    • `run_id_different_hash_when_content_differs`
    • `marker_lookup_supplies_artifact_id_when_fallback_concat`
    • `marker_lookup_does_not_override_explicit_bracket`
    • `marker_lookup_returns_fallback_when_no_match`

NOT in this PR

  • No schema changes (`TestRunFile`/`RunMetadata`/`TestResult` shape unchanged; only the values flowing through).
  • No version bump, no CHANGELOG entry.
  • Performance is O(N×M) over cases × markers — fine for typical suite sizes. Hashmap pre-index would be a separate optimisation PR.

🤖 Generated with Claude Code

…link

User-reported regressions against `rivet import-results --format junit`:

**Bug #1: silent overwrite on re-import.** `suite_to_run` built
`run_id = format!("junit-{safe_name}")` from the testsuite name only,
so two CI runs of the same suite produced identical filenames and the
second import wiped the first.

Fix: append a disambiguator to the run_id. When the JUnit
`<testsuite timestamp="...">` attribute is present (most CI tooling
emits it), slugify it: `2026-05-17T06-35-44Z`. When absent, hash the
suite's case list (name, classname, outcome variant) and append as
16-hex DefaultHasher digest. Identical re-imports of the same artefact
remain idempotent (same hash → same filename → no churn); different
content distinguishes itself.

**Bug #2: test→artifact link dropped on cargo-nextest output.**
`artifact_id_for` has 4 heuristics; the fallback emits a literal
`"classname.name"` concatenation that the test-coverage report cannot
join back to any artifact. cargo-nextest doesn't bracket
`[REQ-NNN]` or use the artifact ID as classname, so most rivet-on-rust
projects hit the fallback.

Fix: hook the JUnit importer into `test_scanner`. New public
`parse_junit_xml_with_markers(xml, markers)` adds a 5th heuristic —
when the existing fallback fires, look up a marker whose `test_name`
matches the case name (exact or suffix with separator). The CLI
(`cmd_import_results_junit`) scans the project's `src/`+`tests/` for
`// rivet: verifies REQ-NNN` markers before parsing the XML, then
passes them to the new function. Bracketed and direct-classname IDs
are preserved (they short-circuit before the marker lookup).

Existing `parse_junit_xml` kept working unchanged (delegates to the
new path with an empty marker slice). No schema changes.

Tests added (6):
- run_id_includes_timestamp_when_present
- run_id_stable_hash_when_no_timestamp
- run_id_different_hash_when_content_differs
- marker_lookup_supplies_artifact_id_when_fallback_concat
- marker_lookup_does_not_override_explicit_bracket
- marker_lookup_returns_fallback_when_no_match

Workspace: 1003 lib tests pass (was 996, +7). Clippy clean. Format clean.

Trace: skip
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

📐 Rivet artifact delta

No artifact changes in this PR. Code-only changes (renderer, CLI wiring, tests) don't touch the artifact graph.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

❌ Patch coverage is 88.94737% with 21 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rivet-core/src/junit.rs 88.94% 21 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit 0a21a57 into main May 18, 2026
17 of 39 checks passed
@avrabe avrabe deleted the fix/junit-import-overwrite-and-linkage branch May 18, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant