Skip to content

fix: make parity test resilient with workspace_root() helper - #153

Merged
avifenesh merged 3 commits into
mainfrom
fix/parity-test-resilience-90
Feb 4, 2026
Merged

fix: make parity test resilient with workspace_root() helper#153
avifenesh merged 3 commits into
mainfrom
fix/parity-test-resilience-90

Conversation

@avifenesh

Copy link
Copy Markdown
Collaborator

Summary

  • Replaces brittle .ancestors().nth(2) pattern with dynamic workspace_root() helper
  • Makes parity test resilient to different directory structures (worktrees, symlinks)
  • Uses OnceLock for caching workspace root detection

Changes

  • Added workspace_root() helper function that searches for [workspace] in ancestor Cargo.toml files
  • Updated test_repo_agents_md_matches_claude_md() to use the new helper
  • Updated get_fixtures_dir() to use the new helper

Test plan

  • All 823 tests pass
  • Clippy clean (no warnings)
  • Build succeeds
  • Tests work from worktree directory

Closes #90

Copilot AI review requested due to automatic review settings February 4, 2026 09:25
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @avifenesh, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness and reliability of path resolution within the project's test suite. By replacing a brittle, hardcoded ancestor lookup with a dynamic and cached workspace_root() helper, it ensures that tests function correctly across various development environments, including those involving worktrees and symlinks. This change improves the developer experience by preventing test failures due to environmental setup variations.

Highlights

  • Path Resolution Robustness: Replaced the fragile .ancestors().nth(2) pattern with a dynamic workspace_root() helper to ensure tests are resilient to different directory structures like worktrees and symlinks.
  • Workspace Root Detection: Introduced a new workspace_root() helper function that intelligently searches for [workspace] in ancestor Cargo.toml files to accurately determine the project's root.
  • Performance Optimization: Implemented OnceLock for the workspace_root() helper to cache the detected workspace root, preventing redundant searches and improving performance.
  • Test Refactoring: Updated test_repo_agents_md_matches_claude_md() and get_fixtures_dir() to leverage the new workspace_root() helper, simplifying path construction and improving maintainability.
Changelog
  • CHANGELOG.md
    • CLAUDE/AGENTS parity test now resilient to different directory structures (worktrees, symlinks)
    • Replaced brittle .ancestors().nth(2) with dynamic workspace root detection
    • New workspace_root() helper searches for [workspace] in ancestor Cargo.toml files
  • crates/agnix-core/src/lib.rs
    • Added a new workspace_root() helper function within the tests module. This function dynamically determines the project's workspace root by traversing ancestor directories and checking for Cargo.toml files containing [workspace] or [workspace.].
    • Integrated std::sync::OnceLock into workspace_root() to cache the result of the root detection, ensuring the search is performed only once per application run.
    • Refactored test_repo_agents_md_matches_claude_md() to utilize the new workspace_root() helper for locating the repository root, removing the previous .ancestors().nth(2) logic.
    • Updated get_fixtures_dir() to also use the workspace_root() helper, streamlining the process of finding test fixtures.
Activity
  • All 823 tests passed successfully.
  • The codebase remains Clippy clean, with no warnings introduced.
  • The project builds without any issues.
  • Tests were verified to run correctly from a worktree directory, confirming the fix for directory structure resilience.
  • This pull request addresses and closes issue Make CLAUDE/AGENTS parity test resilient #90.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a workspace_root() helper function to dynamically find the workspace root, replacing a brittle hardcoded path traversal. This is a solid improvement for making the tests more resilient to directory structure changes. The use of OnceLock for caching is also a good choice for performance. I have one suggestion to make the workspace detection logic within the new helper even more robust.

Comment on lines +497 to +499
if content.contains("[workspace]") || content.contains("[workspace.") {
return ancestor.to_path_buf();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current check using content.contains("[workspace]") is a bit brittle. It could lead to false positives if the string [workspace] appears in a comment or a string literal within Cargo.toml, or incorrectly match unrelated table names like [workspace-foo]. A more robust approach is to check for this pattern at the beginning of a trimmed line and ensure it's a valid table definition, which is how TOML tables are defined.

                    if content.lines().any(|line| {
                        let trimmed = line.trim();
                        if trimmed.starts_with("[workspace]") {
                            // Check what comes after `[workspace]` to distinguish from e.g. `[workspace-foo]`
                            let rest = &trimmed["[workspace]".len()..];
                            return rest.is_empty() || rest.starts_with(|c: char| c.is_whitespace() || c == '#' || c == '.');
                        }
                        false
                    }) {
                        return ancestor.to_path_buf();
                    }

@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR makes the CLAUDE/AGENTS parity and fixtures-based tests robust to different repository layouts by introducing a reusable workspace-root discovery helper. It replaces brittle path assumptions with dynamic detection based on ancestor Cargo.toml files containing a workspace section.

Changes:

  • Added a workspace_root() test helper in crates/agnix-core/src/lib.rs that walks ancestor directories from CARGO_MANIFEST_DIR, looking for a Cargo.toml containing [workspace] or [workspace.], caching the result with OnceLock.
  • Updated test_repo_agents_md_matches_claude_md() to use workspace_root() instead of hard-coded .ancestors().nth(2)-based repo root inference.
  • Updated get_fixtures_dir() in the same test module to derive tests/fixtures from workspace_root(), aligning fixture path resolution with the new helper.
  • Documented the behavioral fix in CHANGELOG.md under the “Fixed” section.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
crates/agnix-core/src/lib.rs Adds a cached workspace_root() helper in the test module and wires it into the CLAUDE/AGENTS parity test and fixtures-directory helper to remove brittle ancestor-based path logic.
CHANGELOG.md Notes that the CLAUDE/AGENTS parity test now uses dynamic workspace root detection and explains the new helper’s behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +488 to +508
fn workspace_root() -> &'static Path {
use std::sync::OnceLock;

static ROOT: OnceLock<PathBuf> = OnceLock::new();
ROOT.get_or_init(|| {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
for ancestor in manifest_dir.ancestors() {
let cargo_toml = ancestor.join("Cargo.toml");
if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
if content.contains("[workspace]") || content.contains("[workspace.") {
return ancestor.to_path_buf();
}
}
}
panic!(
"Failed to locate workspace root from CARGO_MANIFEST_DIR={}",
manifest_dir.display()
);
})
.as_path()
}

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

We now have three nearly identical workspace_root implementations across the test suite (this one plus the helpers in crates/agnix-cli/tests/cli_integration.rs and crates/agnix-cli/tests/fixture_family_cli.rs), which risks these definitions drifting if the workspace-detection logic ever changes. Consider centralizing this helper in a shared test utility (or reusing the existing one) so that workspace-root resolution logic is defined in a single place.

Copilot uses AI. Check for mistakes.
Comment thread crates/agnix-core/src/lib.rs Outdated
Comment on lines 566 to 567
let claude = std::fs::read_to_string(repo_root.join("CLAUDE.md")).unwrap();
let agents = std::fs::read_to_string(repo_root.join("AGENTS.md")).unwrap();

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

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

This test still uses bare unwrap() when reading CLAUDE.md and AGENTS.md, which will panic with a generic message and loses the opportunity to show which path failed and why; the repo already uses the unwrap_or_else(|e| panic!(...)) pattern for file reads in tests (for example crates/agnix-cli/tests/rule_parity.rs:53-59, 61-64) to provide clearer diagnostics. To keep failure output consistent and more actionable when these files are missing or unreadable (one of the goals in issue #90), consider switching these unwrap() calls to expect or unwrap_or_else with messages that include the resolved path and underlying error.

Suggested change
let claude = std::fs::read_to_string(repo_root.join("CLAUDE.md")).unwrap();
let agents = std::fs::read_to_string(repo_root.join("AGENTS.md")).unwrap();
let claude_path = repo_root.join("CLAUDE.md");
let claude = std::fs::read_to_string(&claude_path).unwrap_or_else(|e| {
panic!("Failed to read CLAUDE.md at {}: {e}", claude_path.display());
});
let agents_path = repo_root.join("AGENTS.md");
let agents = std::fs::read_to_string(&agents_path).unwrap_or_else(|e| {
panic!("Failed to read AGENTS.md at {}: {e}", agents_path.display());
});

Copilot uses AI. Check for mistakes.
@claude

claude Bot commented Feb 4, 2026

Copy link
Copy Markdown

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.


@avifenesh
avifenesh merged commit 3d7333b into main Feb 4, 2026
12 checks passed
@avifenesh
avifenesh deleted the fix/parity-test-resilience-90 branch February 5, 2026 12:49
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.

Make CLAUDE/AGENTS parity test resilient

2 participants