Skip to content

refactor: fold the test-cases verbs together and collapse the native store - #858

Merged
lewisjared merged 10 commits into
mainfrom
refactor/test-cases-verbs-store
Aug 9, 2026
Merged

refactor: fold the test-cases verbs together and collapse the native store#858
lewisjared merged 10 commits into
mainfrom
refactor/test-cases-verbs-store

Conversation

@lewisjared

@lewisjared lewisjared commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Continues the test-cases simplification on top of #856. Two independent changes, one per commit.

Folds build and sync into run and replay

  • build was run without the execution, so it becomes run --from-slot. That reuses the native already in output/<label>/ and regenerates only the committed bundle, under the same promotion gate as a normal run.
  • sync fetched every manifest blob into a temporary directory purely for the store cache side effect. replay already does that when it materialises, so the verb and the drift workflow's sync step are both gone.

Collapses the native store into one class

NativeStore was a runtime-checkable Protocol with three implementations and a factory that picked between them. It is now a single class whose URL decides the backend: a file:// URL or bare path reads and writes locally in the two-level layout, an http(s) URL reads anonymously through pooch, and the same URL with write credentials adds the S3 upload path used by mint. Public operations, error messages and build_native_store are unchanged, boto3 stays lazy, and the digest validation that keeps a hostile digest inside the store root stays where it was.

Net effect is about 900 fewer lines across source and tests.

Summary by CodeRabbit

  • Breaking Changes

    • Removed test-cases build and test-cases sync commands.
    • Native baseline rebuilding is now available through test-cases run --from-slot.
    • Replay now handles store-cache warming automatically.
  • Improvements

    • Added clearer validation and error reporting for slot reuse.
    • Simplified native storage across local and remote locations.
    • Updated regression-baseline workflows and documentation to reflect the revised lifecycle.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@lewisjared, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0509f398-04dc-4822-8571-98f9836a884f

📥 Commits

Reviewing files that changed from the base of the PR and between 1f72f82 and 82288ec.

📒 Files selected for processing (3)
  • docs/background/regression-baselines.md
  • packages/climate-ref-core/src/climate_ref_core/regression/store.py
  • packages/climate-ref-core/tests/unit/regression/test_store.py
📝 Walkthrough

Walkthrough

The regression workflow removes test-cases build and sync. It adds run --from-slot for bundle rebuilding and unifies local and remote native storage behind NativeStore. Tests and documentation now use direct replay and materialisation.

Changes

Regression storage

Layer / File(s) Summary
Unified native storage and validation
packages/climate-ref-core/src/climate_ref_core/regression/store.py, packages/climate-ref-core/src/climate_ref_core/regression/__init__.py, packages/climate-ref-core/tests/unit/regression/*
NativeStore now supports local storage, anonymous remote reads, and credentialed S3 reads and writes. The removed store implementations are no longer exported. Tests cover URL parsing, caching, digest checks, uploads, downloads, credentials, and preflight checks.

CLI lifecycle

Layer / File(s) Summary
Slot-based bundle rebuilding
packages/climate-ref/src/climate_ref/cli/test_cases/run.py, packages/climate-ref/tests/unit/cli/test_test_cases.py
run --from-slot validates an output slot and catalog, rebuilds the committed bundle without running the diagnostic, rejects execution-only options, and reports missing or invalid inputs.
Command removal and replay behaviour
packages/climate-ref/src/climate_ref/cli/*, packages/climate-ref/tests/unit/cli/test_root.py, packages/climate-ref/tests/unit/cli/test_test_cases.py
The build and sync commands are removed from registration and coverage. Replay and mint tests use NativeStore, including empty selections, missing manifests, integrity warnings, and reconciliation.

Workflow and lifecycle documentation

Layer / File(s) Summary
Direct replay round trips
packages/climate-ref/tests/integration/test_native_roundtrip.py
Integration tests replace the mint -> sync -> replay flow with mint -> replay and verify blob presence, materialisation, missing blobs, and reconstruction inputs through NativeStore.
Baseline lifecycle references
docs/background/regression-baselines.md, changelog/858.breaking.md
The documentation and breaking-change record describe run --from-slot, direct replay, removal of build and sync, and replacement of the former store implementations.
Nightly drift workflow
.github/workflows/regression-drift.yaml
The nightly workflow removes the separate test-cases sync step and retains native-baseline replay after catalog-path retrieval.

Sequence Diagram(s)

sequenceDiagram
  participant RunTestCase
  participant RebuildFromSlot
  participant OutputSlot
  participant BundleBuilder
  RunTestCase->>RebuildFromSlot: Request --from-slot
  RebuildFromSlot->>OutputSlot: Validate native files and catalog
  RebuildFromSlot->>BundleBuilder: Rebuild bundle without diagnostic execution
  BundleBuilder-->>RunTestCase: Return rebuilt bundle
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarises the two main changes: test-case verb consolidation and native store consolidation.
Description check ✅ Passed The description gives a detailed change summary and covers the affected commands, store design, tests, documentation, and changelog.
Docstring Coverage ✅ Passed Docstring coverage is 86.49% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/test-cases-verbs-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lewisjared
lewisjared marked this pull request as ready for review August 8, 2026 07:29
@lewisjared
lewisjared force-pushed the refactor/test-cases-verbs-store branch 2 times, most recently from 52e034b to bef0e99 Compare August 8, 2026 07:35
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.71271% with 15 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...-ref-core/src/climate_ref_core/regression/store.py 92.24% 5 Missing and 4 partials ⚠️
.../climate-ref/src/climate_ref/cli/test_cases/run.py 90.32% 2 Missing and 4 partials ⚠️
Flag Coverage Δ
core 93.41% <91.71%> (+0.06%) ⬆️
providers 87.33% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...f-core/src/climate_ref_core/regression/__init__.py 100.00% <ø> (ø)
...ckages/climate-ref/src/climate_ref/cli/__init__.py 92.24% <ø> (ø)
...ate-ref/src/climate_ref/cli/test_cases/__init__.py 100.00% <100.00%> (ø)
...mate-ref/src/climate_ref/cli/test_cases/_common.py 89.68% <100.00%> (ø)
...mate-ref/src/climate_ref/cli/test_cases/_stages.py 94.35% <ø> (ø)
...te-ref/src/climate_ref/cli/test_cases/baselines.py 86.53% <ø> (+0.71%) ⬆️
.../climate-ref/src/climate_ref/cli/test_cases/run.py 90.90% <90.32%> (+2.77%) ⬆️
...-ref-core/src/climate_ref_core/regression/store.py 92.04% <92.24%> (+1.43%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Base automatically changed from refactor/test-cases-verb-driver to main August 9, 2026 09:27
Replaces the build verb with a --from-slot option on run, which reuses the
native already in output/<label>/ instead of executing the diagnostic.

Removes the sync verb. It fetched every manifest blob into a temporary
directory purely for the store cache side effect, which replay already
triggers when it materialises.
Replaces the NativeStore protocol and its three implementations with a single
class. The store URL decides the backend: a file:// URL or bare path reads and
writes locally, an http(s) URL reads anonymously through pooch, and the same URL
with write credentials adds the S3 upload path used by mint.

Public operations, error messages and the build_native_store factory are
unchanged.
Aligns the --from-slot missing-catalog message with the one the driver gives,
makes the pooch cache directory explicit rather than defaulting to the working
directory, and refreshes the docs and tests that still described sync.
…rom-slot

The guard arrived on the base branch as a build test.
Retargets it at the option that replaced build.
@lewisjared
lewisjared force-pushed the refactor/test-cases-verbs-store branch from 7a524df to f0a6103 Compare August 9, 2026 09:33
- Rejects --from-slot alongside the options that only apply to an execution,
  so a rebuild can no longer be skipped silently by --only-missing.
- Moves the S3 endpoint, bucket and credentials into an S3WriteConfig,
  so a store no longer carries five fields that mean nothing unless it writes.
- Validates the digest at the point of use rather than behind a key builder.
- Drops the exit_if_empty message argument, which lost its only caller with sync.
- Restores build's wording for an empty slot and matches the two catalog errors.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/climate-ref/tests/integration/test_native_roundtrip.py (1)

773-782: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale assertion message.

The test is renamed to test_missing_blob_detected_by_has, but the assertion at Line 790 still reads "Sync must detect the missing blob". The sync verb no longer exists. Change the message to name store.has.

🧹 Nitpick comments (5)
packages/climate-ref-core/src/climate_ref_core/regression/store.py (1)

390-417: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

put writes the blob non-atomically.

shutil.copy2 writes directly to the canonical content-addressed path. If the process is interrupted, a truncated file remains at that path, and every later has call reports it as present. fetch detects the corruption through _verify_hash_matches, so the failure is loud rather than silent, but a re-mint cannot repair the store because put skips the existing path.

Copy to a temporary name in the same directory, then os.replace it into place.

♻️ Proposed change
             blob.parent.mkdir(parents=True, exist_ok=True)
-            shutil.copy2(str(path), str(blob))
+            tmp = blob.with_name(f"{blob.name}.{os.getpid()}.tmp")
+            shutil.copy2(str(path), str(tmp))
+            os.replace(str(tmp), str(blob))
packages/climate-ref-core/tests/unit/regression/test_store.py (1)

20-49: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Clear the _pooch_manager cache between tests.

_pooch_manager is decorated with functools.cache, and it is keyed by (base_url, cache_dir). _patch_pooch_create replaces pooch.create, but a cached manager from an earlier test bypasses the patch. The current tests pass only because each one uses a distinct tmp_path cache directory. A future test that reuses a cache directory would silently get the real pooch manager and attempt a network call.

Add _pooch_manager.cache_clear() inside _patch_pooch_create to make the isolation explicit.

packages/climate-ref/tests/unit/cli/test_test_cases.py (1)

2307-2369: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for --from-slot with a named --label.

The new tests cover the default latest slot, the option conflicts, the missing slot, the missing catalog, and a failed rebuild. No test proves --from-slot reads the slot named by --label. The label is the main input of this verb, and a wrong-slot regression would still pass the current suite.

Seed a slot with run --label before, then assert run --from-slot --label before rebuilds output/before/regression/.

docs/background/regression-baselines.md (1)

322-322: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the drift wording with the table.

Line 261 states the job "replays every baseline". Line 322 states it replays "every referenced native blob". replay operates on a test case baseline; the native blobs are the inputs it materialises. Use the same term in both places.

✏️ Proposed fix
-A scheduled (and manually dispatchable) job `replay`s every referenced native blob against the committed bundle.
+A scheduled (and manually dispatchable) job `replay`s every baseline, materialising its referenced native blobs and comparing against the committed bundle.

Line 323 also reads "a baseline that no longer reproduce"; correct it to "reproduces".

packages/climate-ref/tests/integration/test_native_roundtrip.py (1)

834-838: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The helper duplicates the store's blob layout.

_blob_path reimplements root / digest[:2] / digest, which NativeStore._blob_path already defines. If the layout changes, this helper diverges from the implementation it is meant to probe. Delegate to the store instead.

♻️ Proposed change
 def _blob_path(store: NativeStore, digest: str) -> Path:
     """Return the on-disk path of a blob in a local store, for tests that corrupt or delete one."""
     root = store.root
     assert root is not None
-    return root / digest[:2] / digest
+    return store._blob_path(digest, root)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 67c27709-dbad-4d19-990d-844de48ddb1a

📥 Commits

Reviewing files that changed from the base of the PR and between 7a895dd and 1f72f82.

📒 Files selected for processing (17)
  • .github/workflows/regression-drift.yaml
  • changelog/858.breaking.md
  • docs/background/regression-baselines.md
  • packages/climate-ref-core/src/climate_ref_core/regression/__init__.py
  • packages/climate-ref-core/src/climate_ref_core/regression/store.py
  • packages/climate-ref-core/tests/unit/regression/test_capture.py
  • packages/climate-ref-core/tests/unit/regression/test_store.py
  • packages/climate-ref/src/climate_ref/cli/__init__.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/__init__.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/_common.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/_stages.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/baselines.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/run.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/store.py
  • packages/climate-ref/tests/integration/test_native_roundtrip.py
  • packages/climate-ref/tests/unit/cli/test_root.py
  • packages/climate-ref/tests/unit/cli/test_test_cases.py
💤 Files with no reviewable changes (4)
  • packages/climate-ref/src/climate_ref/cli/init.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/store.py
  • .github/workflows/regression-drift.yaml
  • packages/climate-ref/tests/unit/cli/test_root.py

Comment thread docs/background/regression-baselines.md Outdated
Comment thread packages/climate-ref-core/src/climate_ref_core/regression/store.py
Comment thread packages/climate-ref-core/src/climate_ref_core/regression/store.py
Comment thread packages/climate-ref-core/src/climate_ref_core/regression/store.py
…uched

An unvalidated digest was joined onto the pooch cache directory and used as the
fetch URL suffix, so a path such as ../../etc/passwd escaped the cache. Validates
once at the top of has and fetch instead of per branch.

Also keeps the R2 credentials out of the generated repr, reads a single-character
URL scheme as a Windows drive letter, and fixes the verb agreement in the docs.
@lewisjared
lewisjared merged commit bb2d5da into main Aug 9, 2026
28 checks passed
@lewisjared
lewisjared deleted the refactor/test-cases-verbs-store branch August 9, 2026 11:06
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