Skip to content

Conversation

@leynos
Copy link
Owner

@leynos leynos commented Sep 9, 2025

Summary

  • add Jinja is tests for file-system types and register them in the template standard library
  • cover file type tests with rstest unit tests and Cucumber features
  • document design choices and mark file-system tests complete in the roadmap

Testing

  • make fmt
  • make lint
  • make test
  • make markdownlint
  • make nixie (fails: KeyboardInterrupt)

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

Summary by Sourcery

Add and register file-system type tests in the Jinja template standard library and update related docs, tests, and dependencies

New Features:

  • Introduce Jinja is tests for dir, file, symlink, pipe, and device

Enhancements:

  • Register the new file-system tests in the manifest parser

Build:

  • Add nix crate dependency with the fs feature

Documentation:

  • Document file-system tests in design doc and roadmap and update srgn documentation formatting

Tests:

  • Add rstest unit tests for file-type predicates and Cucumber feature tests with accompanying step definitions

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Sep 9, 2025

Reviewer's Guide

This PR adds a dedicated stdlib module that registers custom Jinja is tests for file-system types, integrates them into the manifest parsing flow, supplements them with rstest unit tests and Cucumber feature tests, updates dependencies, and refreshes documentation and the roadmap to mark file-system tests complete.

Sequence diagram for registering Jinja file-system tests in manifest parsing

sequenceDiagram
    participant manifest
    participant stdlib
    participant jinja_env
    manifest->>jinja_env: add_function("env", ...)
    manifest->>jinja_env: add_function("glob", ...)
    manifest->>stdlib: register(&mut jinja_env)
    stdlib->>jinja_env: add_test("dir", ...)
    stdlib->>jinja_env: add_test("file", ...)
    stdlib->>jinja_env: add_test("symlink", ...)
    stdlib->>jinja_env: add_test("pipe", ...)
    stdlib->>jinja_env: add_test("device", ...)
Loading

ER diagram for Jinja file-system test registration and usage

erDiagram
    ENVIRONMENT {
        string id
    }
    JINJA_TEST {
        string name
        function predicate
    }
    ENVIRONMENT ||--o{ JINJA_TEST : registers
    JINJA_TEST }|--|{ FILE_SYSTEM_TYPE : tests
    FILE_SYSTEM_TYPE {
        string type_name
    }
Loading

File-Level Changes

Change Details Files
Implement stdlib module with file-system Jinja tests and integrate it
  • Create src/stdlib.rs with register() and is_file_type() functions
  • Add unit tests for each file type in src/stdlib.rs
  • Expose stdlib module in src/lib.rs
  • Invoke crate::stdlib::register in manifest.rs to register tests
  • Add nix fs feature dependency in Cargo.toml
src/stdlib.rs
src/lib.rs
src/manifest.rs
Cargo.toml
Add Cucumber feature and step definitions for file-system tests
  • Create tests/steps/fs_steps.rs to prepare file-system fixtures
  • Register fs_steps module in tests/steps/mod.rs
  • Add tests/data/jinja_is.yml with sample manifests
  • Add tests/features/fs_tests.feature to validate passing and failing scenarios
tests/steps/fs_steps.rs
tests/steps/mod.rs
tests/data/jinja_is.yml
tests/features/fs_tests.feature
Update documentation and roadmap for file-system tests
  • Document design and behavior of new fs tests in docs/netsuke-design.md
  • Refresh ellipses formatting in docs/srgn.md
  • Mark basic file-system tests complete in docs/roadmap.md
docs/netsuke-design.md
docs/srgn.md
docs/roadmap.md

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

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.

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4ae807f and 7ace05d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • Cargo.toml (2 hunks)
  • docs/netsuke-design.md (8 hunks)
  • src/stdlib.rs (1 hunks)
  • tests/cucumber.rs (1 hunks)
  • tests/data/jinja_is.yml (1 hunks)
  • tests/features_unix/fs_tests.feature (1 hunks)
  • tests/steps/fs_steps.rs (1 hunks)
  • tests/steps/mod.rs (1 hunks)

Summary by CodeRabbit

  • New Features
    • Added standard library tests in templates to detect path types (directory, file, symlink, pipe, device) for conditional manifests.
  • Documentation
    • Expanded design notes on file-system test behaviour and linked relevant references.
    • Updated roadmap to mark basic file-system tests as completed.
    • Minor formatting and clarity improvements across documentation.
  • Tests
    • Added feature scenarios and test fixtures covering path-type detection and missing-path handling.
  • Chores
    • Updated development dependencies to support test-only file-system entities.

Walkthrough

Add a public stdlib module and register it in manifest parsing to expose five Jinja tests for Unix file types (dir, file, symlink, pipe, device). Add dev-dependency on nix for tests. Introduce test data, Cucumber steps, and feature scenarios. Update documentation and roadmap to describe and mark these tests as complete.

Changes

Cohort / File(s) Summary
Build & deps
Cargo.toml
Add dev-dependency nix = { version = "0.30.1", default-features = false, features = ["fs"] }.
Core stdlib exposure
src/lib.rs, src/stdlib.rs, src/manifest.rs
Add pub mod stdlib;. Implement stdlib::register(env) to add Jinja tests: dir, file, symlink, pipe, device. Call crate::stdlib::register(&mut jinja) during manifest parsing.
Documentation
docs/netsuke-design.md, docs/roadmap.md, docs/srgn.md
Document file-system tests behaviour; mark roadmap items done; reflow and formatting tweaks (renumbering, ellipses, headings, references).
Feature tests & data
tests/data/jinja_is.yml, tests/features/fs_tests.feature
Add manifest data using env('<PATH>') is <type> conditions. Add scenarios validating detection and missing-path behaviour.
Cucumber step implementations
tests/steps/mod.rs, tests/steps/fs_steps.rs
Register fs_steps. Implement workspace setup creating dir, file, symlink, FIFO, and device path; export paths via environment variables for tests.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CLI as CLI / Tests
    participant Parser as Manifest Parser
    participant Jinja as Jinja Environment
    participant Stdlib as stdlib::register
    participant FS as OS File System

    CLI->>Parser: parse(manifest)
    Parser->>Jinja: create Environment
    Parser->>Jinja: register env/glob helpers
    Parser->>Stdlib: register(&mut Environment)
    Stdlib->>Jinja: add tests: dir, file, symlink, pipe, device
    Parser-->>CLI: parsed manifest (tests available)
Loading
sequenceDiagram
    autonumber
    participant Eval as Template Eval
    participant Test as file-type test
    participant FS as OS File System

    Eval->>Test: evaluate env(PATH) is <type>
    Test->>FS: symlink_metadata(PATH)
    alt path exists
        Test->>FS: md.file_type().is_<type>()
        Test-->>Eval: true/false
    else path missing (NotFound)
        Test-->>Eval: false
    else other I/O error
        Test-->>Eval: template error (InvalidOperation)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Five little tests in a Jinja hive,
dir, file, link—then pipe and device arrive.
stdlib wakes, the parser nods,
Paths confess their Unix gods.
Docs align, the roadmap sings—
Green ticks bloom on testing wings.

Use this prompt to ask the AI for a concise changelog entry or to summarise code-level impacts.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/implement-file-system-tests-with-validation

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

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 found some issues that need to be addressed.

  • The nix crate is only used in tests—move it to dev-dependencies to avoid pulling it into production builds.
  • The file-system tests rely on Unix-only APIs; consider adding conditional compilation or alternative implementations to support Windows or document the Unix-only limitation.
  • In is_file_type, wrapping IO errors into ErrorKind::InvalidOperation loses original error context—consider preserving the source error for better debugging.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The nix crate is only used in tests—move it to dev-dependencies to avoid pulling it into production builds.
- The file-system tests rely on Unix-only APIs; consider adding conditional compilation or alternative implementations to support Windows or document the Unix-only limitation.
- In is_file_type, wrapping IO errors into ErrorKind::InvalidOperation loses original error context—consider preserving the source error for better debugging.

## Individual Comments

### Comment 1
<location> `src/stdlib.rs:17` </location>
<code_context>
+    env.add_test("pipe", |path: String| {
+        is_file_type(&path, |ft| ft.is_fifo())
+    });
+    env.add_test("device", |path: String| {
+        is_file_type(&path, |ft| ft.is_block_device() || ft.is_char_device())
+    });
+}
</code_context>

<issue_to_address>
The 'device' test combines block and char devices, which may be ambiguous for template users.

Consider adding distinct tests for block and character devices, or update documentation to clarify how 'device' should be used.

Suggested implementation:

```rust
    env.add_test("pipe", |path: String| {
        is_file_type(&path, |ft| ft.is_fifo())
    });
    env.add_test("block_device", |path: String| {
        is_file_type(&path, |ft| ft.is_block_device())
    });
    env.add_test("char_device", |path: String| {
        is_file_type(&path, |ft| ft.is_char_device())
    });
    // The 'device' test is retained for backward compatibility, but users are encouraged to use 'block_device' or 'char_device' for clarity.
    env.add_test("device", |path: String| {
        is_file_type(&path, |ft| ft.is_block_device() || ft.is_char_device())
    });
}

```

If you have documentation for these tests (e.g., in comments or external docs), update it to mention the new 'block_device' and 'char_device' tests and clarify the meaning of 'device'.
</issue_to_address>

### Comment 2
<location> `src/stdlib.rs:6` </location>
<code_context>
+use std::{fs, io};
+
+/// Register standard library helpers with the Jinja environment.
+pub fn register(env: &mut Environment<'_>) {
+    env.add_test("dir", |path: String| is_file_type(&path, |ft| ft.is_dir()));
+    env.add_test("file", |path: String| {
</code_context>

<issue_to_address>
Consider refactoring the repeated test registration logic into a loop over a static list of test names and predicates.

```rust
use minijinja::{Environment, Error, ErrorKind};
use std::{fs, io};
use std::os::unix::fs::{FileTypeExt, FileType};

/// Register standard library helpers with the Jinja environment.
pub fn register(env: &mut Environment<'_>) {
    // a static list of (test_name, predicate) pairs
    const TESTS: &[(&str, fn(FileType) -> bool)] = &[
        ("dir", FileType::is_dir),
        ("file", FileType::is_file),
        ("symlink", FileTypeExt::is_symlink),
        ("pipe", FileTypeExt::is_fifo),
        ("device", |ft| ft.is_block_device() || ft.is_char_device()),
    ];

    for (name, pred) in TESTS {
        // call the same helper with each predicate
        env.add_test(*name, move |path: String| is_file_type(&path, *pred));
    }
}

/// Determine whether `path` matches the given file type predicate.
///
/// Returns `Ok(false)` if the path does not exist.
fn is_file_type<F>(path: &str, predicate: F) -> Result<bool, Error>
where
    F: Fn(fs::FileType) -> bool,
{
    match fs::symlink_metadata(path) {
        Ok(md) => Ok(predicate(md.file_type())),
        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
        Err(err) => Err(Error::new(ErrorKind::InvalidOperation, err.to_string())),
    }
}
```
</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.

use std::{fs, io};

/// Register standard library helpers with the Jinja environment.
pub fn register(env: &mut Environment<'_>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (complexity): Consider refactoring the repeated test registration logic into a loop over a static list of test names and predicates.

use minijinja::{Environment, Error, ErrorKind};
use std::{fs, io};
use std::os::unix::fs::{FileTypeExt, FileType};

/// Register standard library helpers with the Jinja environment.
pub fn register(env: &mut Environment<'_>) {
    // a static list of (test_name, predicate) pairs
    const TESTS: &[(&str, fn(FileType) -> bool)] = &[
        ("dir", FileType::is_dir),
        ("file", FileType::is_file),
        ("symlink", FileTypeExt::is_symlink),
        ("pipe", FileTypeExt::is_fifo),
        ("device", |ft| ft.is_block_device() || ft.is_char_device()),
    ];

    for (name, pred) in TESTS {
        // call the same helper with each predicate
        env.add_test(*name, move |path: String| is_file_type(&path, *pred));
    }
}

/// Determine whether `path` matches the given file type predicate.
///
/// Returns `Ok(false)` if the path does not exist.
fn is_file_type<F>(path: &str, predicate: F) -> Result<bool, Error>
where
    F: Fn(fs::FileType) -> bool,
{
    match fs::symlink_metadata(path) {
        Ok(md) => Ok(predicate(md.file_type())),
        Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
        Err(err) => Err(Error::new(ErrorKind::InvalidOperation, err.to_string())),
    }
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

Has this now been resolved?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, this has been resolved! The current implementation in src/stdlib.rs now uses a static TESTS array containing the test names and their corresponding predicates, and registers them in a loop:

const TESTS: &[FileTest] = &[
    ("dir", is_dir),
    ("file", is_file),
    ("symlink", is_symlink),
    // ...
];

for (name, pred) in TESTS {
    env.add_test(*name, move |path: String| {
        is_file_type(Utf8Path::new(&path), *pred)
    });
}

This matches the refactoring suggestion and removes the previous repetition. Let me know if you need any further details!

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

Codex Review: Here are some suggestions.

Reply with @codex fix comments to fix any unresolved comments.

About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you open a pull request for review, mark a draft as ready, or comment "@codex review". If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex fix this CI failure" or "@codex address that feedback".

Comment on lines 1 to 4
use minijinja::{Environment, Error, ErrorKind};
use std::os::unix::fs::FileTypeExt;
use std::{fs, io};

Choose a reason for hiding this comment

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

[P1] Gate Unix-specific file tests for Windows compatibility

This module imports std::os::unix::fs::FileTypeExt and registers pipe/device tests without any #[cfg(unix)] guarding. On Windows the std::os::unix namespace is absent, so the crate fails to build before the tests run. Other Unix-only code in the project is conditionally compiled; these helpers should be similarly gated or given cross-platform fallbacks so Windows builds continue to succeed.

Useful? React with 👍 / 👎.

Comment on lines 3 to 7
use crate::CliWorld;
use cucumber::given;
use nix::{sys::stat::Mode, unistd::mkfifo};
use std::{os::unix::fs as unix_fs, path::PathBuf};
use test_support::env::set_var;

Choose a reason for hiding this comment

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

[P1] Restrict Unix-only Cucumber steps

The file-system step definitions unconditionally use nix::unistd::mkfifo and std::os::unix::fs, but the test harness is compiled on every target. Neither of those modules exists on Windows and the nix dev-dependency does not compile there, so cargo test will fail outright. These steps and their module inclusion should be wrapped in #[cfg(unix)] (or an alternative implementation provided) to avoid breaking non-Unix CI.

Useful? React with 👍 / 👎.

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: 14

Caution

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

⚠️ Outside diff range comments (2)
docs/srgn.md (1)

691-713: Convert reference list to GFM footnotes and remove placeholder dates

  • Replace the reference list in docs/srgn.md (lines 691–713) with GitHub-Flavoured Markdown footnotes and drop entries using “accessed on January 1, 1970”.
  • Run markdownlint to confirm no lint errors:
make markdownlint
tests/steps/fs_steps.rs (1)

1-33: Align CI with Unix-only scope.

Restrict the test matrix to Unix runners or exclude Windows for jobs that compile test steps, to avoid spurious failures.

Add or adjust your GitHub Actions job:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]  # exclude windows/macos if stdlib uses Unix-only tests
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo test --all-features
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65af86d and 0d7ff39.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • Cargo.toml (1 hunks)
  • docs/netsuke-design.md (8 hunks)
  • docs/roadmap.md (1 hunks)
  • docs/srgn.md (3 hunks)
  • src/lib.rs (1 hunks)
  • src/manifest.rs (1 hunks)
  • src/stdlib.rs (1 hunks)
  • tests/data/jinja_is.yml (1 hunks)
  • tests/features/fs_tests.feature (1 hunks)
  • tests/steps/fs_steps.rs (1 hunks)
  • tests/steps/mod.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Comment why, not what; document assumptions, edge cases, trade-offs, and complexity without restating obvious code
Extract reusable logic into functions; prefer composition and declarative constructs when readable
Keep functions small, single-responsibility, and observe command/query separation
Use precise, descriptive names; boolean names should start with is/has/should
Use en-GB-oxendict spelling/grammar in comments (except external API references)
Function docs must include clear usage examples; avoid redundant examples in test docs
No Rust source file may exceed 400 lines; split long switches/tables; move large test data to external files
Fix test warnings in code; do not silence them
Extract helpers when functions are too long; group many parameters into well-named structs
Consider using Arc when returning large errors to reduce data movement
Document public APIs using Rustdoc (///) so cargo doc can generate documentation
Prefer immutable data; avoid unnecessary mut bindings
Prefer Result-based error handling over 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 helper functions when conditionals 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
Prefer .expect() over .unwrap()
Use conditional compilation (#[cfg]/#[cfg_attr]) for functions unused under specific feature sets
Use concat!() to join long string literals rather than using backslash-newline escapes
Prefer single-line function bodies where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) })
Prefer semantic error enums deriving std::error::Error via thiserror for caller-inspectable conditions

Files:

  • src/manifest.rs
  • src/lib.rs
  • tests/steps/mod.rs
  • src/stdlib.rs
  • tests/steps/fs_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:

  • src/manifest.rs
  • src/lib.rs
  • tests/steps/mod.rs
  • src/stdlib.rs
  • tests/steps/fs_steps.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Every module must begin with a //! module-level comment explaining its purpose and utility
Never export eyre::Report from library code; convert to domain error enums at API boundaries

Files:

  • src/manifest.rs
  • src/lib.rs
  • src/stdlib.rs
tests/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.rs: 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 (e.g., Env/Clock traits) using mockable crate; follow docs/reliable-testing-in-rust-via-dependency-injection.md

Files:

  • tests/steps/mod.rs
  • tests/steps/fs_steps.rs
docs/**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.md: Treat docs/ markdown as the reference source of truth for requirements and decisions; keep updated with changes
Use en-GB-oxendict spelling/grammar in documentation (LICENSE name unchanged)

Files:

  • docs/roadmap.md
  • docs/netsuke-design.md
  • docs/srgn.md
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

**/*.md: Validate Markdown with make markdownlint
Wrap Markdown paragraphs and bullet points at 80 columns; do not wrap tables or headings
Wrap code blocks in Markdown at 120 columns
Use dashes (-) for list bullets
Use GitHub-flavoured Markdown footnotes ([^1]) for references
Validate Mermaid diagrams in Markdown using make nixie

Files:

  • docs/roadmap.md
  • docs/netsuke-design.md
  • docs/srgn.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/roadmap.md
  • docs/netsuke-design.md
  • docs/srgn.md
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/roadmap.md
  • docs/netsuke-design.md
  • docs/srgn.md
Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Cargo.toml: Use explicit version ranges and keep dependencies up to date
Mandate SemVer caret requirements for all dependencies (e.g., "1.2.3")
Prohibit wildcard (*) and open-ended (>=) version specifiers; use ~ only with a specific, documented reason

Files:

  • Cargo.toml
🧬 Code graph analysis (1)
src/manifest.rs (1)
src/stdlib.rs (1)
  • register (6-20)
🔍 Remote MCP

Relevant points for review:

  • The new tests in src/stdlib.rs and tests/steps/fs_steps.rs use std::os::unix::fs::FileTypeExt to detect special file types (FIFO, socket, block/char devices). That trait is available only on Unix platforms, so these tests (and the added register code that calls is_file_type) will fail to compile on Windows unless they’re gated behind #[cfg(unix)] or the CI matrix is restricted to Unix runners .

  • The FIFO creation in the test fixture uses nix::unistd::mkfifo, which is provided only when the fs feature of the nix crate is enabled. The PR correctly adds nix = { version = "0.30.1", default-features = false, features = ["fs"] }, satisfying this requirement .

  • Cucumber feature tests refer to /dev/null for device detection; this path and the Unix-specific APIs will not exist or behave the same on Windows, causing functional test failures on non-Unix CI runners.

Recommendations:

  • Add #[cfg(unix)] to the Unix-specific modules/tests (or isolate them into a Unix-only test suite).

  • Ensure the GitHub Actions (or other CI) workflow either excludes Windows runners or accounts for these OS-specific tests.

⏰ 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). (1)
  • GitHub Check: build-test (stable)
🔇 Additional comments (6)
docs/roadmap.md (1)

157-158: LGTM: Roadmap accurately marks the basic file-system tests as done.

src/lib.rs (1)

15-15: LGTM: Expose stdlib publicly.

Public surface matches intended registration in manifest processing.

src/manifest.rs (1)

541-541: Verify Unix API gating and Windows build
Verify manually that all Unix-specific stdlib calls are gated by cfg(unix) and confirm successful Windows compilation.

tests/data/jinja_is.yml (2)

3-22: LGTM: concise test data covers the five file-type predicates.

Targets are minimal and focused; commands are harmless; expressions exercise dir, file, symlink, pipe, and device.


1-1: Confirm netsuke_version "0.1.0" remains valid
The parser deserialises netsuke_version using semver::Version, which accepts “0.1.0” without error, so the Jinja tests will execute as intended.

tests/steps/fs_steps.rs (1)

20-30: Environment lock and cleanup confirmed
set_var acquires the global EnvLock to serialise mutations and CliWorld’s Drop invokes restore_many to revert all saved variables, preventing cross-scenario leakage.

Comment on lines 2 to 5
Scenario: file system tests detect path types
Given a file-type test workspace
When the manifest file "tests/data/jinja_is.yml" is parsed
Then the manifest has targets 5
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Assert target identities, not just the count.

Validate the resolved target names to catch false positives where unrelated targets inflate the count. If the step library lacks such an assertion, add a “Then the manifest has targets named …” step.

🤖 Prompt for AI Agents
In tests/features/fs_tests.feature around lines 2 to 5, the scenario only
asserts the manifest target count which can miss false positives; update the
feature to assert specific target identities by adding a step like "Then the
manifest has targets named ..." with the expected list (e.g. the five exact
target names), and if the step definition is missing implement a step in the
step library that parses the manifest result and compares the set/order of
target names to the expected values, failing on any mismatch.

Comment on lines 7 to 15
Scenario: file system tests return false for missing paths
Given a file-type test workspace
And the environment variable "DIR_PATH" is set to "/no/such"
And the environment variable "FILE_PATH" is set to "/no/such"
And the environment variable "SYMLINK_PATH" is set to "/no/such"
And the environment variable "PIPE_PATH" is set to "/no/such"
And the environment variable "DEVICE_PATH" is set to "/no/such"
When the manifest file "tests/data/jinja_is.yml" is parsed
Then the manifest has targets 0
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Make the “missing path” assertions OS-neutral.

Avoid hard-coding /no/such. Drive non-existence via the workspace (e.g., set each var to <workspace>/__missing__/<type> and ensure it does not exist in the fixture step), making behaviour consistent across platforms.

🤖 Prompt for AI Agents
In tests/features/fs_tests.feature around lines 7 to 15, the scenario hard-codes
non-existent paths as "/no/such" which is platform-dependent; change each
environment variable assignment to point inside the test workspace (e.g., set
"DIR_PATH" to "<workspace>/__missing__/dir", "FILE_PATH" to
"<workspace>/__missing__/file", "SYMLINK_PATH" to
"<workspace>/__missing__/symlink", "PIPE_PATH" to
"<workspace>/__missing__/pipe", "DEVICE_PATH" to
"<workspace>/__missing__/device") and update the "Given a file-type test
workspace" fixture to ensure those <workspace>/__missing__/* entries do not
exist (remove or never create them) so the manifest parsing asserts missing
paths in an OS-neutral way.

Comment on lines 11 to 20
let temp = tempfile::tempdir().expect("tempdir");
let dir = temp.path().join("dir");
std::fs::create_dir(&dir).expect("dir");
let file = temp.path().join("file");
std::fs::write(&file, b"data").expect("file");
let link = temp.path().join("link");
unix_fs::symlink(&file, &link).expect("symlink");
let pipe = temp.path().join("pipe");
mkfifo(&pipe, Mode::S_IRUSR | Mode::S_IWUSR).expect("fifo");
let entries = [
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve .expect() messages for faster failure diagnosis.

Make failure causes explicit to speed up triage.

Apply this diff:

-    let temp = tempfile::tempdir().expect("tempdir");
+    let temp = tempfile::tempdir().expect("create tempdir for file-type workspace");
-    let dir = temp.path().join("dir");
-    std::fs::create_dir(&dir).expect("dir");
+    let dir = temp.path().join("dir");
+    std::fs::create_dir(&dir).expect("create dir fixture");
-    let file = temp.path().join("file");
-    std::fs::write(&file, b"data").expect("file");
+    let file = temp.path().join("file");
+    std::fs::write(&file, b"data").expect("write file fixture");
-    let link = temp.path().join("link");
-    unix_fs::symlink(&file, &link).expect("symlink");
+    let link = temp.path().join("link");
+    unix_fs::symlink(&file, &link).expect("create symlink fixture");
-    let pipe = temp.path().join("pipe");
-    mkfifo(&pipe, Mode::S_IRUSR | Mode::S_IWUSR).expect("fifo");
+    let pipe = temp.path().join("pipe");
+    mkfifo(&pipe, Mode::S_IRUSR | Mode::S_IWUSR).expect("create fifo fixture");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let temp = tempfile::tempdir().expect("tempdir");
let dir = temp.path().join("dir");
std::fs::create_dir(&dir).expect("dir");
let file = temp.path().join("file");
std::fs::write(&file, b"data").expect("file");
let link = temp.path().join("link");
unix_fs::symlink(&file, &link).expect("symlink");
let pipe = temp.path().join("pipe");
mkfifo(&pipe, Mode::S_IRUSR | Mode::S_IWUSR).expect("fifo");
let entries = [
let temp = tempfile::tempdir().expect("create tempdir for file-type workspace");
let dir = temp.path().join("dir");
std::fs::create_dir(&dir).expect("create dir fixture");
let file = temp.path().join("file");
std::fs::write(&file, b"data").expect("write file fixture");
let link = temp.path().join("link");
unix_fs::symlink(&file, &link).expect("create symlink fixture");
let pipe = temp.path().join("pipe");
mkfifo(&pipe, Mode::S_IRUSR | Mode::S_IWUSR).expect("create fifo fixture");
let entries = [
🤖 Prompt for AI Agents
In tests/steps/fs_steps.rs around lines 11 to 20, the chained .expect(...) calls
use generic messages like "tempdir", "dir", "file", "symlink", and "fifo" which
hinder quick diagnosis; update each .expect() to include a clear, specific
message that describes the attempted operation and the path/variable involved
(e.g., "creating tempdir", "creating dir at {dir:?}", "writing file at
{file:?}", "creating symlink from {file:?} to {link:?}", "creating fifo at
{pipe:?}") so test failures show actionable context.

codescene-delta-analysis[bot]

This comment was marked as outdated.

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: 1

♻️ Duplicate comments (4)
src/stdlib.rs (4)

6-20: Gate Unix-only tests and provide cross-platform stubs.

The pipe and device tests use Unix-only FileTypeExt methods that don't exist on Windows. Register them conditionally to maintain template compatibility across platforms.

Apply this diff:

 pub fn register(env: &mut Environment<'_>) {
     env.add_test("dir", |path: String| is_file_type(&path, |ft| ft.is_dir()));
     env.add_test("file", |path: String| {
         is_file_type(&path, |ft| ft.is_file())
     });
     env.add_test("symlink", |path: String| {
         is_file_type(&path, |ft| ft.is_symlink())
     });
-    env.add_test("pipe", |path: String| {
-        is_file_type(&path, |ft| ft.is_fifo())
-    });
-    env.add_test("device", |path: String| {
-        is_file_type(&path, |ft| ft.is_block_device() || ft.is_char_device())
-    });
+    
+    // Unix-only: FIFO and block/char devices
+    #[cfg(unix)]
+    {
+        env.add_test("pipe", |path: String| is_file_type(&path, |ft| ft.is_fifo()));
+        env.add_test("device", |path: String| {
+            is_file_type(&path, |ft| ft.is_block_device() || ft.is_char_device())
+        });
+    }
+    
+    // Non-Unix: expose tests but always return false
+    #[cfg(not(unix))]
+    {
+        env.add_test("pipe", |_path: String| Ok(false));
+        env.add_test("device", |_path: String| Ok(false));
+    }
 }

37-37: Gate test module for Unix platforms only.

The test module uses Unix-specific features (FIFOs, /dev/null) and will fail to compile on Windows.

Apply this diff:

-#[cfg(test)]
+#[cfg(all(test, unix))]

25-25: Use generic predicate parameter for flexibility.

The concrete function pointer restricts callers to function pointers only. A generic parameter allows closures without coercion.

Apply this diff:

-fn is_file_type(path: &str, predicate: fn(fs::FileType) -> bool) -> Result<bool, Error> {
+fn is_file_type<F>(path: &str, predicate: F) -> Result<bool, Error>
+where
+    F: Fn(fs::FileType) -> bool,
+{

42-42: Remove redundant FileTypeExt import in tests.

This import duplicates line 2 and creates an unused import warning when the module import is conditionally compiled.

Apply this diff:

-    use std::os::unix::fs::FileTypeExt;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca84e1 and 4ae807f.

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

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Comment why, not what; document assumptions, edge cases, trade-offs, and complexity without restating obvious code
Extract reusable logic into functions; prefer composition and declarative constructs when readable
Keep functions small, single-responsibility, and observe command/query separation
Use precise, descriptive names; boolean names should start with is/has/should
Use en-GB-oxendict spelling/grammar in comments (except external API references)
Function docs must include clear usage examples; avoid redundant examples in test docs
No Rust source file may exceed 400 lines; split long switches/tables; move large test data to external files
Fix test warnings in code; do not silence them
Extract helpers when functions are too long; group many parameters into well-named structs
Consider using Arc when returning large errors to reduce data movement
Document public APIs using Rustdoc (///) so cargo doc can generate documentation
Prefer immutable data; avoid unnecessary mut bindings
Prefer Result-based error handling over 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 helper functions when conditionals 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
Prefer .expect() over .unwrap()
Use conditional compilation (#[cfg]/#[cfg_attr]) for functions unused under specific feature sets
Use concat!() to join long string literals rather than using backslash-newline escapes
Prefer single-line function bodies where appropriate (e.g., pub fn new(id: u64) -> Self { Self(id) })
Prefer semantic error enums deriving std::error::Error via thiserror for caller-inspectable conditions

Files:

  • src/stdlib.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:

  • src/stdlib.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Every module must begin with a //! module-level comment explaining its purpose and utility
Never export eyre::Report from library code; convert to domain error enums at API boundaries

Files:

  • src/stdlib.rs
🔍 Remote MCP

Additional context for reviewing PR #151

  • nix 0.30.1 features
    The fs feature in nix 0.30.1 is empty and not enabled by default; enabling it does not pull in unrelated modules. Explicitly specifying

    nix = { version = "0.30.1", default-features = false, features = ["fs"] }

    in [dev-dependencies] correctly gates only the filesystem APIs needed for tests, without extra dependencies (docs.rs).

  • mkfifo availability
    The function

    pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()>

    lives in nix::unistd and is only available when the crate feature fs is enabled. This matches the PR’s use of nix::unistd::mkfifo(...) to create named pipes in tests (docs.rs).

  • Unix file‐type detection (FileTypeExt)
    On Unix, std::fs::symlink_metadata returns a FileType that implements std::os::unix::fs::FileTypeExt, which provides:

    • is_block_device()
    • is_char_device()
    • is_fifo()
    • is_socket()

    The PR’s helper is_file_type can rely on these methods to detect pipes (is_fifo), devices (is_char_device), symlinks, directories, and files (web.mit.edu).

  • MiniJinja custom tests registration
    According to MiniJinja docs, tests are registered on an Environment via

    env.add_test("name", test_fn);

    where test_fn returns bool. The PR’s stdlib::register function adding tests dir, file, symlink, pipe, and device follows this documented pattern for custom “is” tests (docs.rs).

⏰ 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). (1)
  • GitHub Check: build-test (stable)

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

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.

2 participants