Skip to content

Propagate manifest write errors#160

Merged
leynos merged 11 commits intomainfrom
codex/refactor-write_manifest-to-return-result-type
Sep 16, 2025
Merged

Propagate manifest write errors#160
leynos merged 11 commits intomainfrom
codex/refactor-write_manifest-to-return-result-type

Conversation

@leynos
Copy link
Owner

@leynos leynos commented Sep 14, 2025

Summary

  • return io::Result from ensure_manifest_exists
  • map file system errors to add manifest path context
  • adjust process step to handle manifest helper failure

Testing

  • make fmt
  • make lint
  • make test

closes #62


https://chatgpt.com/codex/tasks/task_e_68c615a741a08322937728e45293fdd6

Summary by Sourcery

Implement error propagation in manifest creation by converting ensure_manifest_exists to return io::Result, refactor supporting helpers to use UTF-8 paths with enriched context errors, update tests and process step usage accordingly, adjust documentation formatting, and add necessary dependencies.

New Features:

  • Return io::Result from ensure_manifest_exists and propagate manifest write errors
  • Switch manifest handling functions to UTF-8 path types (Utf8Path/Utf8PathBuf)

Enhancements:

  • Map filesystem errors to include manifest path context and invalid input checks
  • Refactor manifest creation workflow into discrete helpers with detailed error mapping
  • Use cap-std fs_utf8 ambient-authority logic for parent directory creation

Build:

  • Add camino and cap-std dependencies to support UTF-8 path and fs_utf8 operations

Documentation:

  • Correct formatting in markdown docs for doctest, behavioural testing, and reliability guides

Tests:

  • Add unit tests for rejecting directory targets and read-only parent errors
  • Update process_steps test to handle UTF-8 paths and result propagation

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 14, 2025

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • Bug Fixes
    • More robust manifest creation with clear, actionable error messages.
    • Proper handling of invalid paths, directory targets, and already-existing files.
  • Tests
    • Added unit tests covering directory paths and read-only parent scenarios.
  • Documentation
    • Formatting improvements across multiple guides; no content changes.
  • Chores
    • Dependency updates to support safer path handling and I/O.

Walkthrough

Return Utf8PathBuf inside io::Result from ensure_manifest_exists; switch inputs to camino Utf8Path types; validate file name and directory state; create parent directories and persist a NamedTempFile robustly; update callers to convert and unwrap the Result.

Changes

Cohort / File(s) Summary
Test helper: fallible UTF‑8 API
test_support/src/lib.rs
Change signature to pub fn ensure_manifest_exists(&Utf8Path, &Utf8Path) -> io::Result<Utf8PathBuf>; validate file_name(), check NotADirectory, use temp_dir.join for relative paths, create parent dir, write to NamedTempFile in destination, persist (handle AlreadyExists), replace panics with mapped io::Error messages; add camino and cap_std imports.
Caller: convert to Utf8 and unwrap Result
tests/steps/process_steps.rs
Convert temp dir and cli file to Utf8Path, call new Result-returning helper, .expect("manifest"), then convert returned Utf8PathBuf to PathBuf for cli.file.
Manifests: crate deps
Cargo.toml, test_support/Cargo.toml
Add dev-dependency camino = "1.2.0" to top-level Cargo.toml; add camino = "1.2.0" and cap-std = { version = "3.4.4", features = ["fs_utf8"] } to test_support/Cargo.toml.
Docs: formatting-only
docs/behavioural-testing-in-rust-with-cucumber.md, docs/reliable-testing-in-rust-via-dependency-injection.md, docs/rust-doctest-dry-guide.md
Apply whitespace/linebreak and separator formatting changes only; no behavioural or API changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor TestRunner as Test Runner
  participant Run as run()
  participant Helper as ensure_manifest_exists()
  participant FS as Filesystem

  TestRunner->>Run: start
  Run->>Helper: ensure_manifest_exists(temp_dir: Utf8Path, cli_file: Utf8Path)
  alt cli_file is relative
    Note right of Helper #DDEBF7: Resolve manifest_path = temp_dir.join(cli_file)
  end
  Helper->>Helper: validate file_name -> Err(InvalidInput) if missing
  Helper->>FS: ensure_parent_directory(parent) --map_err-->
  Helper->>FS: NamedTempFile::new_in(parent) --map_err-->
  Helper->>FS: write_all(content) --map_err-->
  Helper->>FS: persist(tempfile -> manifest_path) --map_err-->
  alt any I/O fails
    Helper-->>Run: Err(io::Error) (propagated)
  else success
    Helper-->>Run: Ok(Utf8PathBuf manifest_path)
    Run->>Run: convert to std::path::PathBuf and assign
  end
  Run-->>TestRunner: continue
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A path now speaks in Utf8 tone,
Errors return — no panic groan.
Tempfile writes with careful hand,
Manifests land where checks demand.
Tests unwrap truth, tidy and known.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed Accept the title as succinct and directly descriptive of the primary change to propagate manifest write errors. Emphasise that it accurately reflects the refactor to return io::Result and improved error mapping without extraneous detail. Keep the title as-is for clarity when scanning repository history.
Linked Issues Check ✅ Passed Confirm that the PR addresses the objectives of linked issue [#62] by converting the test helper to return io::Result, propagating I/O errors with manifest-path context, and updating callers and tests to handle the Result. Verify that the process_steps usage and added unit tests exercise the new error paths and that the refactor no longer uses .expect("write manifest"). Treat the Utf8Path type changes and added dependencies as implementation details necessary to meet the issue requirements.
Out of Scope Changes Check ✅ Passed Identify no out-of-scope production-code changes; the diffs are confined to the test helper (test_support), test callers, Cargo manifests, and minor documentation formatting. Accept the added dependencies (camino and cap-std fs_utf8) as required by the switch to Utf8Path types. Flag documentation reflow and markdown formatting edits as cosmetic and not affecting runtime behaviour.
Description Check ✅ Passed Accept the PR description as directly relevant because it documents switching ensure_manifest_exists to return io::Result, mapping filesystem errors to include manifest-path context, and updating callers and tests. Note that the description includes testing commands and references closing linked issue #62. Expand the description with brief examples of expected error messages if reviewers request clearer diagnostics.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Sep 14, 2025

Reviewer's Guide

Refactor ensure_manifest_exists into a fallible API by returning io::Result and mapping filesystem errors to include manifest path context. Extract helper functions for directory creation, temp file handling, writing, and persisting with contextual error messages. Update documentation, examples, and tests to handle the new error propagation behavior, and bump dependencies to support UTF-8 path handling.

Entity relationship diagram for manifest file creation and error context

erDiagram
    MANIFEST_FILE {
        Utf8PathBuf path
        String content
    }
    ERROR_CONTEXT {
        String error_message
        Utf8PathBuf manifest_path
    }
    MANIFEST_FILE ||--o{ ERROR_CONTEXT : "error mapped to manifest path context"
Loading

Class diagram for process_steps test changes

classDiagram
    class ProcessStepsTest {
        +test_manifest_creation()
        +test_manifest_error_handling()
    }
    ProcessStepsTest : <<updated>>
    ProcessStepsTest : Now handles Result from ensure_manifest_exists
Loading

File-Level Changes

Change Details Files
Make ensure_manifest_exists fallible, returning io::Result with path context
  • Changed function signature to return io::Result
  • Updated resolve_manifest_path to return Result and validate presence of file name
  • Introduced NotADirectory error when manifest path points to a directory
test_support/src/lib.rs
Refactor manifest creation into detailed helper functions with contextual errors
  • Implemented ensure_parent_directory to locate existing ancestor and create directories with context
  • Created create_temp_file and write_manifest_content that wrap and label IO errors with the manifest path
  • Refined persist_manifest_file to treat AlreadyExists as success and propagate other errors with source and target context
test_support/src/lib.rs
Adjust documentation, examples, and tests for error propagation
  • Updated doc comments to use #Errors instead of panics and upgraded examples to Utf8Path/Utf8PathBuf with Result
  • Added unit tests for directory-as-path rejection and read-only parent failure cases
  • Modified process_steps to unwrap the new fallible ensure_manifest_exists API
test_support/src/lib.rs
tests/steps/process_steps.rs
docs/rust-doctest-dry-guide.md
docs/reliable-testing-in-rust-via-dependency-injection.md
docs/behavioural-testing-in-rust-with-cucumber.md
Add UTF-8 path and fs_utf8 dependencies for enhanced path handling
  • Bumped project Cargo.toml to include camino for UTF-8 path types
  • Added cap-std with fs_utf8 feature in test_support/Cargo.toml to enable ambient directory creation
test_support/Cargo.toml
Cargo.toml

Assessment against linked issues

Issue Objective Addressed Explanation
#62 Refactor write_manifest (ensure_manifest_exists) to return a Result type instead of panicking on write errors.
#62 Propagate errors from manifest writing instead of panicking, enabling clearer diagnostics when manifest writing fails.
#62 Update documentation and callers to reflect the new error handling and Result return type.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
test_support/src/lib.rs (1)

133-184: Add targeted tests for new failure paths.

Assert the enriched diagnostics by forcing failures (e.g., unwritable parent dir, directory-as-file path).

I can add rstest cases that:

  • pass a trailing-separator path (directory) and assert InvalidInput.
  • point dest_dir to a read-only temp dir and assert the mapped error text contains the target path.
    Do you want me to open a follow-up PR with these tests?
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7348128 and 69d9bdc.

📒 Files selected for processing (2)
  • test_support/src/lib.rs (4 hunks)
  • tests/steps/process_steps.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Use en-GB-oxendict spelling and grammar in code comments (exceptions allowed for external API names)
Function documentation must include clear examples; test documentation should omit redundant examples
Keep file size manageable: no Rust source file longer than 400 lines; split large switches/dispatch tables and move big test data to external files
Disallow Clippy warnings; fix warnings in code rather than silencing them
Extract helper functions for long functions; maintain separation of concerns and CQRS
Group many related parameters into meaningful structs
If returning a large error type, consider using Arc to reduce data copied/returned
Each Rust module must begin with a module-level //! comment stating purpose and utility
Document public APIs with Rustdoc (///) so cargo doc can generate documentation
Prefer immutable data; avoid unnecessary mut bindings
Handle errors with Result instead of panicking where feasible
Avoid unsafe code unless absolutely necessary and document any usage clearly
Place function attributes after doc comments
Do not use return in single-line functions
Use predicate functions when conditional criteria have more than two branches
Do not silence lints except as a last resort; suppressions must be tightly scoped and include a clear reason
Prefer expect over allow for lint management
Use conditional compilation (#[cfg]/#[cfg_attr]) for functions unused under specific feature sets
Prefer .expect() over .unwrap()
Use concat!() for long string literals instead of escaping newlines with backslashes
Prefer single-line function bodies when appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) })
Prefer semantic error enums (derive std::error::Error via thiserror) for inspectable conditions
Use an opaque eyre::Report only at the application boundary for human-readable logs; do not expose in public APIs
Never export eyre::Report from libraries; convert to domain error enums at API boundaries and to eyre only in main/top-level async tas...

Files:

  • tests/steps/process_steps.rs
  • test_support/src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.

  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such

    • For testing of functionality depending upon environment variables, dependency injection and the mockable crate are the preferred option.
    • If mockable cannot be used, env mutations in tests ...

Files:

  • tests/steps/process_steps.rs
  • test_support/src/lib.rs
tests/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.rs: Write unit and behavioural tests for new functionality; run before and after changes
Use rstest fixtures for shared setup and replace duplicated tests with #[rstest(...)] parameterised cases
Prefer mockall for mocks/stubs
Mock non-deterministic dependencies via dependency injection using mockable (traits like Env and Clock) where appropriate; follow docs/reliable-testing-in-rust-via-dependency-injection.md

Files:

  • tests/steps/process_steps.rs
🧬 Code graph analysis (2)
tests/steps/process_steps.rs (1)
test_support/src/lib.rs (1)
  • ensure_manifest_exists (133-184)
test_support/src/lib.rs (1)
test_support/src/env.rs (1)
  • write_manifest (167-179)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build-test (stable)
🔇 Additional comments (7)
tests/steps/process_steps.rs (1)

85-85: Make manifest failure messages actionable.

Include the error chain in the panic to speed up triage.

[ suggest_recommended_refactor ]

-        cli.file = ensure_manifest_exists(dir.path(), &cli.file).expect("manifest");
+        cli.file = ensure_manifest_exists(dir.path(), &cli.file)
+            .unwrap_or_else(|e| panic!("manifest: {}", test_support::display_error_chain(&e)));
test_support/src/lib.rs (6)

30-30: Imports update — LGTM.

Bring io into scope with Write to support fallible I/O.


115-131: Docs: switch to Errors section with updated example — LGTM.

Document failure modes and show .expect("manifest") at call site.


153-161: Temp file creation with context — LGTM.

Map NamedTempFile::new_in failures with path context.


162-170: Manifest write mapping — LGTM.

Propagate write_manifest errors with clear diagnostics.


183-184: Return Ok(manifest_path) — LGTM.

Finish with an explicit Ok to match the new signature.


133-139: Approve — call sites handle Result

Mark resolved. Both usages consume the Result via .expect(): tests/steps/process_steps.rs:85 and doc example in test_support/src/lib.rs (lines 129–130).

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
test_support/src/lib.rs (1)

141-149: Early reject of directory-like targets (no file name) is good.

This prevents persisting to paths without a terminal file component.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69d9bdc and f1ed148.

📒 Files selected for processing (1)
  • test_support/src/lib.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Each Rust module file must begin with a module-level //! comment explaining its purpose
Document public APIs using Rustdoc comments (///) so cargo doc can generate docs
Place function attributes after doc comments
Prefer immutable data; avoid unnecessary mut bindings
Handle errors with Result instead of panicking where feasible
Avoid unsafe code unless absolutely necessary and clearly document any usage
Do not use return in single-line functions
Prefer single-line function bodies where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) })
Prefer .expect() over .unwrap()
Use concat!() to combine long string literals rather than escaping newlines with a backslash
Use predicate functions for conditional criteria with more than two branches
Where a function is too long, extract meaningfully named helper functions (separation of concerns, CQRS)
Where a function has too many parameters, group related parameters into meaningfully named structs
If a function is unused with specific features selected, use conditional compilation with #[cfg] or #[cfg_attr]
Where a function returns a large error, consider using Arc to reduce returned data size
Use semantic error enums deriving std::error::Error via thiserror for inspectable conditions
Clippy warnings must be disallowed; fix issues in code rather than silencing
Do not silence lints except as a last resort; any suppression must be tightly scoped and include a clear reason
Prefer #[expect] over #[allow] when suppressing lints
Use cap-std for all filesystem operations (capability-based, sandboxed)
Use camino for path handling; avoid std::path::PathBuf directly
Avoid std::fs directly; wrap with cap-std
Comments must use en-GB-oxendict spelling and grammar (except external API names)
No single Rust source file may exceed 400 lines; break up large switches/dispatch tables by feature
Name booleans with is/has/should prefixes; use clear, descriptive names for variables and functions
Function documentation must include...

Files:

  • test_support/src/lib.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.

  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such

    • For testing of functionality depending upon environment variables, dependency injection and the mockable crate are the preferred option.
    • If mockable cannot be used, env mutations in tests ...

Files:

  • test_support/src/lib.rs
**/src/lib.rs

📄 CodeRabbit inference engine (AGENTS.md)

Never export the opaque eyre::Report type from a library; convert to domain error enums at API boundaries

Files:

  • test_support/src/lib.rs
🧬 Code graph analysis (1)
test_support/src/lib.rs (1)
test_support/src/env.rs (1)
  • write_manifest (167-179)
🔇 Additional comments (5)
test_support/src/lib.rs (5)

30-30: Import io and Write is appropriate for fallible I/O.

Proceed; this enables io::Result and trait bounds used below.


161-169: Temp-file creation error mapping is clear.

Message includes the target context; keep as-is.


170-178: Write step correctly propagates errors with path context.

LGTM; the message is specific and stable for tests.


192-193: Returning Ok(manifest_path) is correct and stable.

This preserves absolute semantics for relative inputs joined to temp_dir.


129-131: Approve and merge — call-sites handle the new Result

Found a single usage at tests/steps/process_steps.rs:85 which already calls .expect("manifest"); no other usages found.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
Copy link
Owner Author

leynos commented Sep 15, 2025

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix:

test_support/src/lib.rs
Comment on lines +137 to +139
pub fn ensure_manifest_exists(temp_dir: &Utf8Path, cli_file: &Utf8Path) -> io::Result {
let manifest_path: Utf8PathBuf = if cli_file.is_absolute() {
cli_file.to_owned()
@codescene-delta-analysis codescene-delta-analysis bot 46 minutes ago

❌ New issue: Bumpy Road Ahead
ensure_manifest_exists has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function

Suppress
Comment
DeepWiki

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

leynos and others added 6 commits September 15, 2025 20:31
…fest helper

Rebased onto origin/main. Merged helper refactor with Result-based errors and capability FS ops. Cleaned conflict artifacts and ensured Utf8Path usage across tests. Ran make check-fmt, make test, and make lint: all passing.
@leynos leynos force-pushed the codex/refactor-write_manifest-to-return-result-type branch from 36620a6 to eca6741 Compare September 15, 2025 20:46
@leynos
Copy link
Owner Author

leynos commented Sep 15, 2025

@sourcery-ai review

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `test_support/src/lib.rs:281-290` </location>
<code_context>
+    use std::io;
+    use tempfile::TempDir;
+
+    #[test]
+    fn existing_directory_manifest_path_is_rejected() {
+        let temp = TempDir::new().expect("temp dir");
+        let temp_path = Utf8Path::from_path(temp.path()).expect("utf-8 path");
+        let dir = temp.path().join("dir");
+        fs::create_dir(&dir).expect("create dir");
+
+        let err = ensure_manifest_exists(temp_path, Utf8Path::new("dir"))
+            .expect_err("existing directory");
+        assert_eq!(err.kind(), io::ErrorKind::NotADirectory);
+        let msg = err.to_string();
+        assert!(msg.contains(dir.to_str().expect("utf-8")), "message: {msg}");
+    }
+
+    #[test]
+    fn read_only_parent_reports_target_path() {
+        let temp = TempDir::new().expect("temp dir");
+        let temp_path = Utf8Path::from_path(temp.path()).expect("utf-8 path");
</code_context>

<issue_to_address>
**suggestion (testing):** Suggestion to add a test for manifest creation when the parent directory does not exist.

Add a test case for manifest creation when the parent directory is missing, ensuring correct error handling and messaging for directory creation failures.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@leynos
Copy link
Owner Author

leynos commented Sep 15, 2025

@coderabbitai pause

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1ed148 and eca6741.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • Cargo.toml (1 hunks)
  • docs/behavioural-testing-in-rust-with-cucumber.md (1 hunks)
  • docs/reliable-testing-in-rust-via-dependency-injection.md (3 hunks)
  • docs/rust-doctest-dry-guide.md (2 hunks)
  • test_support/Cargo.toml (1 hunks)
  • test_support/src/lib.rs (2 hunks)
  • tests/steps/process_steps.rs (2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

**/Cargo.toml: Use explicit caret SemVer requirements for all dependencies
Disallow wildcard (*) and open-ended (>=) requirements; use ~ only with a documented reason

Files:

  • Cargo.toml
  • test_support/Cargo.toml
docs/**/*.{rs,md}

📄 CodeRabbit inference engine (docs/rust-doctest-dry-guide.md)

In fenced code blocks for docs, explicitly mark code fences with rust (```rust) for clarity

Files:

  • docs/reliable-testing-in-rust-via-dependency-injection.md
  • docs/behavioural-testing-in-rust-with-cucumber.md
  • docs/rust-doctest-dry-guide.md
docs/reliable-testing-in-rust-via-dependency-injection.md

📄 CodeRabbit inference engine (AGENTS.md)

Follow the project’s guide for reliable testing in Rust via dependency injection

Files:

  • docs/reliable-testing-in-rust-via-dependency-injection.md
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Use the docs/ markdown files as the reference knowledge base and update them proactively when decisions or requirements change

Files:

  • docs/reliable-testing-in-rust-via-dependency-injection.md
  • docs/behavioural-testing-in-rust-with-cucumber.md
  • docs/rust-doctest-dry-guide.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar (LICENSE name excluded)
Validate Markdown with make markdownlint and run make fmt to format Markdown (including fixing table markup)
Validate Mermaid diagrams in Markdown by running make nixie
Wrap Markdown paragraphs and bullet points at 80 columns
Do not wrap tables and headings in Markdown
Wrap code blocks in Markdown at 120 columns
Use dashes (-) for list bullets in Markdown
Use GitHub-flavoured Markdown footnotes ([^1])

Files:

  • docs/reliable-testing-in-rust-via-dependency-injection.md
  • docs/behavioural-testing-in-rust-with-cucumber.md
  • docs/rust-doctest-dry-guide.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/reliable-testing-in-rust-via-dependency-injection.md
  • docs/behavioural-testing-in-rust-with-cucumber.md
  • docs/rust-doctest-dry-guide.md
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Each Rust module file must begin with a module-level //! comment explaining its purpose
Document public APIs using Rustdoc comments (///) so cargo doc can generate docs
Place function attributes after doc comments
Prefer immutable data; avoid unnecessary mut bindings
Handle errors with Result instead of panicking where feasible
Avoid unsafe code unless absolutely necessary and clearly document any usage
Do not use return in single-line functions
Prefer single-line function bodies where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) })
Prefer .expect() over .unwrap()
Use concat!() to combine long string literals rather than escaping newlines with a backslash
Use predicate functions for conditional criteria with more than two branches
Where a function is too long, extract meaningfully named helper functions (separation of concerns, CQRS)
Where a function has too many parameters, group related parameters into meaningfully named structs
If a function is unused with specific features selected, use conditional compilation with #[cfg] or #[cfg_attr]
Where a function returns a large error, consider using Arc to reduce returned data size
Use semantic error enums deriving std::error::Error via thiserror for inspectable conditions
Clippy warnings must be disallowed; fix issues in code rather than silencing
Do not silence lints except as a last resort; any suppression must be tightly scoped and include a clear reason
Prefer #[expect] over #[allow] when suppressing lints
Use cap-std for all filesystem operations (capability-based, sandboxed)
Use camino for path handling; avoid std::path::PathBuf directly
Avoid std::fs directly; wrap with cap-std
Comments must use en-GB-oxendict spelling and grammar (except external API names)
No single Rust source file may exceed 400 lines; break up large switches/dispatch tables by feature
Name booleans with is/has/should prefixes; use clear, descriptive names for variables and functions
Function documentation must include...

Files:

  • test_support/src/lib.rs
  • tests/steps/process_steps.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.

  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

  • Files must not exceed 400 lines in length

    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such

    • For testing of functionality depending upon environment variables, dependency injection and the mockable crate are the preferred option.
    • If mockable cannot be used, env mutations in tests ...

Files:

  • test_support/src/lib.rs
  • tests/steps/process_steps.rs
**/src/lib.rs

📄 CodeRabbit inference engine (AGENTS.md)

Never export the opaque eyre::Report type from a library; convert to domain error enums at API boundaries

Files:

  • test_support/src/lib.rs
tests/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.rs: Large blocks of test data should be moved to external data files
Use rstest fixtures for shared setup
Replace duplicated tests with #[rstest(...)] parameterised cases
Prefer mockall for mocks and stubs
Mock non-deterministic dependencies (Env, Clock) via dependency injection using mockable crate
Test documentation should omit examples that merely reiterate test logic

Files:

  • tests/steps/process_steps.rs
🧬 Code graph analysis (2)
test_support/src/lib.rs (1)
test_support/src/env.rs (1)
  • write_manifest (167-179)
tests/steps/process_steps.rs (1)
test_support/src/lib.rs (1)
  • ensure_manifest_exists (137-156)
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build-test (ubuntu-latest, stable)
🔇 Additional comments (8)
docs/reliable-testing-in-rust-via-dependency-injection.md (1)

25-25: LGTM! Formatting change follows guidelines.

The separator update to underscores improves consistency.

docs/rust-doctest-dry-guide.md (1)

231-232: LGTM! Formatting changes are consistent.

The split lines improve readability whilst maintaining correct markdown structure.

tests/steps/process_steps.rs (1)

86-92: LGTM! Proper Utf8Path conversion with clear error messages.

The conversion to Utf8Path types aligns with the updated API and includes appropriate error messages for debugging.

test_support/src/lib.rs (5)

137-156: LGTM! Clean API refactoring with proper error handling.

The function now correctly:

  • Uses Utf8Path types throughout
  • Validates directory targets early
  • Returns descriptive errors instead of panicking
  • Handles the happy path efficiently

This aligns well with the PR objectives to propagate filesystem errors properly.


158-173: LGTM! Well-structured path resolution.

The helper correctly validates that the path includes a filename and returns appropriate errors.


175-181: Extract complexity to meet cyclomatic complexity guidelines.

The create_manifest_file orchestrates multiple operations but remains clear. However, the PR comments flagged nested complexity in the overall flow. This decomposition helps address those concerns.


204-218: LGTM! Robust persist handling with race condition mitigation.

The function correctly handles AlreadyExists as a success case, preventing race-induced failures in concurrent test environments.


273-312: LGTM! Comprehensive test coverage.

The tests properly verify:

  • Directory rejection with correct error kind
  • Error messages containing the target path
  • Read-only parent handling

Good defensive testing for edge cases.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

✅ Actions performed

Reviews paused.

Add a test that verifies ensure_manifest_exists creates a manifest and its
parent directories when the target path’s parent does not exist. Also checks
that minimal manifest content is written (contains ).
When the resolved manifest parent path exists but is not a directory,
return an AlreadyExists error that explicitly mentions the target
manifest path. This makes the failure clearer and matches test
expectations when a file occupies the would-be parent directory.

Rationale:
- Prevents later, confusing I/O errors at temp-file creation time.
- Produces a precise, actionable error for callers.
- Use `netsuke::…` paths in ninja_gen doctests so they resolve when built
  as part of the workspace.
- Expose `runner::doc` helpers unconditionally (still doc-hidden) so
  doctests can import them without cfg(doctest) gating.
- Update stdlib doctest to use `add_template`/`get_template` API and a
  deterministic current working directory path.
- Reword long markdown line in doctest guide to satisfy MD013.

These changes make all doctests compile and pass under `cargo test --doc`.
codescene-delta-analysis[bot]

This comment was marked as outdated.

Correct inline code spans, punctuation, and sentence spacing around
file names (, ).
Improves readability and complies with markdown lint rules.
Refactor ensure_parent_directory by extracting the logic that finds the
first existing ancestor into find_existing_ancestor. This reduces nesting
and clarifies intent, while preserving prior behaviour (including the
non-directory parent error case).
@leynos leynos merged commit 8949e7a into main Sep 16, 2025
7 checks passed
@leynos leynos deleted the codex/refactor-write_manifest-to-return-result-type branch September 16, 2025 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve error handling in write_manifest test helper function

1 participant