Skip to content

Improve diagnostics for GraphQL errors#40

Merged
leynos merged 6 commits intomainfrom
codex/find-cause-of-badresponseserde-error
Jul 30, 2025
Merged

Improve diagnostics for GraphQL errors#40
leynos merged 6 commits intomainfrom
codex/find-cause-of-badresponseserde-error

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jul 29, 2025

Summary

  • add serde_path_to_error for better error context
  • read GITHUB_GRAPHQL_URL for custom API endpoints
  • include failing JSON path and snippet in BadResponseSerde
  • cover deserialisation failure with a new async test
  • add end-to-end test for missing reviewThreads nodes

Testing

  • make fmt
  • make lint
  • make test (fails: test e2e_missing_nodes_reports_path hangs)

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

Summary by Sourcery

Enhance GraphQL error diagnostics by including failing JSON path and snippet, support custom API endpoints via env var, and add tests for deserialization failures

Enhancements:

  • Use serde_path_to_error to enrich GraphQL deserialization errors with JSON path and snippet
  • Support custom GitHub GraphQL endpoints via GITHUB_GRAPHQL_URL environment variable
  • Limit JSON snippet length in error messages to improve readability

Build:

  • Add serde_path_to_error dependency

Tests:

  • Add async unit test for reporting missing reviewThreads nodes path
  • Add end-to-end test to verify error includes path and snippet for missing nodes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 29, 2025

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 38 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 8857b4f and 830c945.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • Cargo.toml (2 hunks)
  • docs/vk-design.md (1 hunks)
  • src/main.rs (5 hunks)
  • tests/e2e.rs (4 hunks)
  • tests/utils.rs (1 hunks)

Summary by CodeRabbit

  • New Features
    • Enhanced error reporting for GraphQL responses, including detailed JSON path and snippet in error messages.
    • Environment variable support for custom GraphQL endpoint configuration.
  • Bug Fixes
    • Improved consistency in error message truncation.
  • Tests
    • Added new tests to verify improved error reporting for missing GraphQL nodes.
    • Modularised proxy server setup for end-to-end tests with proper resource cleanup.
  • Documentation
    • Added a section explaining GraphQL error handling with a sequence diagram.
  • Chores
    • Updated and added dependencies to support new features and testing utilities.

Walkthrough

Add new dependencies serde_path_to_error and hyper to Cargo.toml. Refactor src/main.rs to improve GraphQL error reporting using snippet truncation and JSON path diagnostics. Modularise MITM proxy setup for end-to-end tests, introduce a new error-path reporting test, and extract test server logic into tests/utils.rs.

Changes

Cohort / File(s) Change Summary
Dependency Additions
Cargo.toml
Add serde_path_to_error to main dependencies and hyper to dev-dependencies.
GraphQL Client Error Handling & Env Config
src/main.rs
Add snippet truncation helper and constants. Update GraphQLClient::new to read endpoint from environment. Refactor run_query to use serde_path_to_error for deserialisation, improving error messages with JSON path and truncated value snippets. Added async test for missing node error path reporting.
Test Refactor and New Test
tests/e2e.rs
Remove inline MITM server logic and use new start_mitm() utility. Update existing test for new handler. Add new async test for missing node error path reporting. Ensure proxy task is aborted after each test.
Test Utility Extraction
tests/utils.rs
Add start_mitm function to launch a Hyper-based HTTP server with a shared mutable handler and task handle. Expose type alias for handler and shutdown control.
Documentation Update
docs/vk-design.md
Add a new section on GraphQL error handling with a Mermaid sequence diagram illustrating client-server interaction and error reporting via serde_path_to_error.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Function
    participant MITM as start_mitm()
    participant Server as Hyper Server
    participant Handler as Request Handler

    Test->>MITM: Call start_mitm()
    MITM->>Server: Start server on local port
    Server->>Handler: On request, call handler closure
    Handler-->>Server: Return response
    Server-->>Test: Serve responses for test duration
    Test->>MITM: Abort server task after test
Loading
sequenceDiagram
    participant GraphQLClient
    participant Env as Environment
    participant serde as serde_path_to_error
    participant User as User

    User->>GraphQLClient: new(token, transcript)
    GraphQLClient->>Env: Check GITHUB_GRAPHQL_URL
    Env-->>GraphQLClient: Return endpoint or default

    User->>GraphQLClient: run_query(query, vars)
    GraphQLClient->>serde: Deserialize response with path tracking
    serde-->>GraphQLClient: Error with path if failure
    GraphQLClient-->>User: Error message with path and snippet
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Possibly related PRs

  • Improve BadResponse error context #35: Enhances error context in VkError::BadResponse and error reporting in run_query, closely intersecting with this PR’s changes to error handling and reporting.
  • Fix GraphQL decode failure #34: Refactors run_query deserialisation logic, introducing error mapping, directly related to this PR’s enhanced error diagnostics in the same function.
  • Add e2e test harness #36: Enhances GraphQL client and test infrastructure, adding transcript logging and MITM proxy for HTTP traffic, closely related to this PR’s client and test improvements.

Poem

New crates arrive, the tests refactored neat,
Errors now show their JSON path and seat.
MITM proxy modular, spun up with flair,
Snippets trimmed, no more despair.
GraphQL’s secrets, easier to debug and meet!
🦀✨

✨ 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/find-cause-of-badresponseserde-error

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 29, 2025

Reviewer's Guide

Integrate serde_path_to_error for detailed GraphQL deserialization diagnostics, add support for custom GraphQL endpoints via GITHUB_GRAPHQL_URL, and cover the new behavior with both unit and end-to-end tests.

Sequence diagram for GraphQLClient deserialization error reporting

sequenceDiagram
    participant Client as GraphQLClient
    participant Server as GraphQL API
    participant Serde as serde_path_to_error
    Client->>Server: Send GraphQL query
    Server-->>Client: Return JSON response
    Client->>Serde: Attempt to deserialize response
    alt Deserialization fails
        Serde-->>Client: Return error with path
        Client-->>Client: Format error with path and snippet
        Client-->>Caller: Return VkError::BadResponseSerde
    else Deserialization succeeds
        Serde-->>Client: Return deserialized object
        Client-->>Caller: Return object
    end
Loading

Class diagram for improved GraphQLClient error diagnostics

classDiagram
    class GraphQLClient {
        +new(token: &str, transcript: Option<PathBuf>) Result<Self, io::Error>
        +with_endpoint(token: &str, endpoint: &str, transcript: Option<PathBuf>) Result<Self, io::Error>
        +fetch_and_deserialize<T>(...) Result<T, VkError>
    }
    class VkError {
        BadResponse(String)
        BadResponseSerde(String)
    }
    GraphQLClient --> VkError : uses
Loading

File-Level Changes

Change Details Files
Allow custom GraphQL API endpoint via environment variable
  • read GITHUB_GRAPHQL_URL env var with fallback to default
  • pass dynamic endpoint into GraphQLClient::with_endpoint
src/main.rs
Provide granular deserialization errors with JSON path and snippet
  • replace serde_json::from_value with serde_path_to_error::deserialize
  • format and truncate a pretty-printed JSON snippet
  • include failing JSON path and snippet in VkError::BadResponseSerde
src/main.rs
Add serde_path_to_error dependency
  • update Cargo.toml to include serde_path_to_error = "0.1"
Cargo.toml
Cover missing nodes deserialization failure in a unit test
  • add tokio async test run_query_missing_nodes_reports_path
  • spin up a local hyper server and assert error mentions reviewThreads path
src/main.rs
Add end-to-end test for missing reviewThreads nodes
  • create tests/e2e_missing_nodes.rs using ThirdWheel mitm proxy
  • run CLI binary and assert stderr contains JSON path and snippet
tests/e2e_missing_nodes.rs

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

Copy link
Copy Markdown
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 @leynos - I've reviewed your changes - here's some feedback:

  • Extract the snippet truncation logic (including the magic 200 length) into a helper or constant to improve readability and maintainability.
  • The two tests for missing reviewThreads (one in the main tests module and one in tests/e2e_missing_nodes.rs) are redundant—consider consolidating them into a single, clear end-to-end test.
  • The spawned HTTP server in the e2e test never shuts down, which can cause the test to hang—add a shutdown signal or a timeout to ensure the server is torn down after the assertion.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Extract the snippet truncation logic (including the magic `200` length) into a helper or constant to improve readability and maintainability.
- The two tests for missing `reviewThreads` (one in the main tests module and one in `tests/e2e_missing_nodes.rs`) are redundant—consider consolidating them into a single, clear end-to-end test.
- The spawned HTTP server in the e2e test never shuts down, which can cause the test to hang—add a shutdown signal or a timeout to ensure the server is torn down after the assertion.

## Individual Comments

### Comment 1
<location> `tests/e2e_missing_nodes.rs:15` </location>
<code_context>
+
+type Handler = Arc<Mutex<Box<dyn FnMut(&Request<Body>) -> Response<Body> + Send>>>;
+
+fn generate_ca() -> CertificateAuthority {
+    use std::process::Command as PCommand;
+    let dir = tempfile::tempdir().expect("tempdir");
</code_context>

<issue_to_address>
Consider extracting the CA/proxy setup into a reusable helper or switching to a mock server crate to simplify and clarify your test code.

Here’s a way to both slim down that nested setup *and* keep your MITM-based tests if you need them, by extracting all of the CA/​proxy wiring into a small helper, and then letting each test only declare its stub and its assertions. You can also toss in a simpler `wiremock` example if you’re open to a new dev-dependency.

1) Extract your helper into e.g. `tests/utils.rs`:

```rust
// tests/utils.rs
use std::{net::SocketAddr, sync::{Arc, Mutex}};
use third_wheel::{
    CertificateAuthority, MitmProxy, ThirdWheel,
    hyper::{Body, Request, Response, StatusCode},
    mitm_layer,
};

pub type Handler = Arc<Mutex<Box<dyn FnMut(&Request<Body>) -> Response<Body> + Send>>>;

pub fn start_mitm() -> (SocketAddr, Handler) {
    let handler: Handler = Arc::new(Mutex::new(Box::new(|_req| {
        Response::builder()
            .status(StatusCode::NOT_FOUND)
            .body(Body::from("No handler"))
            .unwrap()
    })));
    let handler_clone = handler.clone();

    // generate_ca() can live here, or inline it if you use it only once
    let ca = {
        let dir = tempfile::tempdir().unwrap();
        let cert = dir.path().join("cert.pem");
        let key  = dir.path().join("key.pem");
        std::process::Command::new("openssl")
            .args(&[
                "req","-x509","-newkey","rsa:4096",
                "-keyout", key.to_str().unwrap(),
                "-out", cert.to_str().unwrap(),
                "-days","1","-passout","pass:third-wheel",
                "-subj","/C=US/ST=test/L=test/O=vk/CN=vk.test",
            ])
            .status().unwrap();
        CertificateAuthority::load_from_pem_files_with_passphrase_on_key(
            cert, key, "third-wheel"
        ).unwrap()
    };

    let layer = mitm_layer(move |req: Request<Body>, _tw: ThirdWheel| {
        let resp = (handler_clone.lock().unwrap())(&req);
        Box::pin(async move { Ok(resp) })
    });
    let proxy = MitmProxy::builder(layer, ca).build();
    let (addr, fut) = proxy.bind("127.0.0.1:0".parse().unwrap());
    tokio::spawn(fut);
    (addr, handler)
}
```

2) Then your test in `tests/e2e.rs` becomes super‐focused:

```rust
use assert_cmd::Command;
use predicates::str::contains;
use serde_json::json;
use tests::utils::start_mitm;

#[tokio::test]
async fn e2e_missing_nodes_reports_path() {
    let (addr, handler) = start_mitm();

    // just swap in your JSON stub
    *handler.lock().unwrap() = Box::new(move |_req| {
        let body = json!({
          "data": {
            "repository": {
              "pullRequest": {
                "reviewThreads": {
                  "pageInfo": {
                    "hasNextPage": false,
                    "endCursor": null
                  }
                }
              }
            }
          }
        })
        .to_string();

        Response::builder()
            .status(200)
            .header("Content-Type", "application/json")
            .body(Body::from(body))
            .unwrap()
    });

    Command::cargo_bin("vk").unwrap()
        .env("GITHUB_GRAPHQL_URL", format!("http://{}", addr))
        .env("GITHUB_TOKEN", "dummy")
        .args(&["pr", "https://github.com/leynos/cmd-mox/pull/25"])
        .assert()
        .failure()
        .stderr(contains("repository.pullRequest.reviewThreads"))
        .stderr(contains("snippet:"));
}
```

— or, if you’re OK pulling in a mock‐server crate, you can drop *all* of that MITM/CA logic and instead do this with `wiremock` (or `httpmock`):

```rust
use wiremock::{MockServer, Mock, ResponseTemplate};
use wiremock::matchers::{method, any};
use serde_json::json;

#[tokio::test]
async fn e2e_missing_nodes_reports_path() {
    let server = MockServer::start().await;
    let body = json!({
      "data": { /* … */ }
    }).to_string();

    Mock::given(any())
        .respond_with(
          ResponseTemplate::new(200)
            .set_header("Content-Type", "application/json")
            .set_body_string(body),
        )
        .mount(&server)
        .await;

    Command::cargo_bin("vk").unwrap()
        .env("GITHUB_GRAPHQL_URL", server.uri())
        .env("GITHUB_TOKEN", "dummy")
        .args(&["pr", "https://github.com/leynos/cmd-mox/pull/25"])
        .assert()
        .failure()
        .stderr(contains("repository.pullRequest.reviewThreads"))
        .stderr(contains("snippet:"));
}
```

Both approaches keep your test intent crystal‐clear and remove deep nesting in each test.
</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.

Comment thread tests/e2e_missing_nodes.rs Outdated
Copy link
Copy Markdown

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 965bd31 and 255e7e0.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml (1 hunks)
  • src/main.rs (3 hunks)
  • tests/e2e_missing_nodes.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
Cargo.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
Mandate caret requirements for all dependencies: All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3").
Prohibit unstable version specifiers: The use of wildcard (*), or open-ended inequality (>=) version requirements are strictly forbidden in Cargo.toml. Tilde requirements (~) should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.

Files:

  • tests/e2e_missing_nodes.rs
  • src/main.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.
  • 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.

Files:

  • tests/e2e_missing_nodes.rs
  • src/main.rs
🧬 Code Graph Analysis (1)
tests/e2e_missing_nodes.rs (1)
src/main.rs (1)
  • new (143-147)
🔇 Additional comments (5)
Cargo.toml (1)

12-12: LGTM on dependency addition.

The serde_path_to_error dependency follows the required caret requirement format and directly supports the enhanced error diagnostics functionality.

tests/e2e_missing_nodes.rs (2)

44-62: LGTM on mock server implementation.

The handler setup with Arc<Mutex<Box>> allows for runtime handler replacement which is perfect for the test's needs. The server binding and spawning is handled correctly.


64-99: Verify test assertions cover the enhanced error reporting.

The test correctly sets up a mock response missing the nodes field and verifies both the JSON path and snippet are present in the error output, which aligns perfectly with the PR objectives.

The test effectively validates the enhanced error diagnostics by checking for both repository.pullRequest.reviewThreads path and snippet: in the stderr output.

src/main.rs (2)

144-146: LGTM on environment variable support.

The implementation correctly reads GITHUB_GRAPHQL_URL and falls back to the constant, enabling custom endpoints for testing as demonstrated in the e2e test.


226-240: Excellent enhancement to error diagnostics.

The switch from direct serde_json::from_value to serde_path_to_error::deserialize provides significantly better error context. The error message format includes the inner error, JSON path, and a truncated snippet, which will greatly improve debugging experience.

The 200-character truncation limit prevents excessively long error messages whilst still providing useful context.

Comment thread src/main.rs
Comment thread tests/e2e_missing_nodes.rs Outdated
Comment thread tests/e2e_missing_nodes.rs Outdated
Copy link
Copy Markdown

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 255e7e0 and 79d42fd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml (2 hunks)
  • src/main.rs (4 hunks)
  • tests/e2e.rs (3 hunks)
  • tests/utils.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
Cargo.toml

📄 CodeRabbit Inference Engine (AGENTS.md)

Cargo.toml: Use explicit version ranges in Cargo.toml and keep dependencies up-to-date.
Mandate caret requirements for all dependencies: All crate versions specified in Cargo.toml must use SemVer-compatible caret requirements (e.g., some-crate = "1.2.3").
Prohibit unstable version specifiers: The use of wildcard (*), or open-ended inequality (>=) version requirements are strictly forbidden in Cargo.toml. Tilde requirements (~) should only be used where a dependency must be locked to patch-level updates for a specific, documented reason.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.rs: Every module must begin with a module level (//!) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Prefer semantic error enums: Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary: Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library: Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.
Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.

Files:

  • tests/utils.rs
  • src/main.rs
  • tests/e2e.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.
  • 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.

Files:

  • tests/utils.rs
  • src/main.rs
  • tests/e2e.rs
🔇 Additional comments (8)
Cargo.toml (1)

12-12: Remove verification comment for serde_path_to_error

serde_path_to_error = "0.1" resolves to v0.1.17 (released 2025-03-03) with no security advisories and meets the SemVer caret requirement. No action required.

src/main.rs (4)

115-126: LGTM!

The constants and snippet function are well-implemented. The function correctly handles Unicode character boundaries and provides consistent truncation with ellipsis indication.


157-159: LGTM!

Reading the endpoint from an environment variable provides the flexibility needed for testing whilst maintaining a sensible default.


222-224: LGTM!

Consistent use of the snippet function improves error message formatting.


234-247: Excellent enhancement to error diagnostics.

The integration of serde_path_to_error significantly improves debugging by providing the exact JSON path where deserialization fails, along with a contextual snippet. The error message format is clear and informative.

tests/e2e.rs (3)

4-7: LGTM!

The import reorganisation and introduction of the utils module improves code organisation and reusability.


22-45: Good refactoring with proper cleanup.

The migration to start_mitm() improves code reuse and the explicit handle.abort() ensures proper resource cleanup after the test.


46-87: Well-implemented test with proper timeout and cleanup.

The new test effectively validates the enhanced error reporting functionality. The timeout prevents hanging issues mentioned in the PR description, and the assertions correctly verify both the JSON path and snippet presence in error output.

The test addresses the concerns raised in the past review comment by using proper timeout handling and more specific error assertions that check for both the JSON path and snippet content.

Comment thread Cargo.toml Outdated
Comment thread tests/utils.rs Outdated
Comment thread tests/utils.rs Outdated
Copy link
Copy Markdown

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79d42fd and e171a99.

📒 Files selected for processing (1)
  • docs/vk-design.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
docs/**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

docs/**/*.md: Use the markdown files within the docs/ directory as a knowledge base and source of truth for project requirements, dependency choices, and architectural decisions.
Proactively update the relevant file(s) in the docs/ directory to reflect the latest state when new decisions are made, requirements change, libraries are added/removed, or architectural patterns evolve.

Files:

  • docs/vk-design.md
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

**/*.md: Documentation must use en-GB-oxendict spelling and grammar, except for the naming of the "LICENSE" file.
Validate Markdown files using make markdownlint.
Run make fmt after any documentation changes to format all Markdown files and fix table markup.
Validate Mermaid diagrams in Markdown files by running make nixie.
Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks in Markdown must be wrapped at 120 columns.
Tables and headings in Markdown must not be wrapped.
Use dashes (-) for list bullets in Markdown.
Use GitHub-flavoured Markdown footnotes ([^1]) for references and footnotes.

Files:

  • docs/vk-design.md

⚙️ CodeRabbit Configuration File

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

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • 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/vk-design.md

Comment thread docs/vk-design.md
@github-actions
Copy link
Copy Markdown

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 29, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@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

🔭 Outside diff range comments (1)
tests/e2e.rs (1)

31-50: Good refactoring to use the shared MITM utility.

The test properly uses the new utility and ensures cleanup. For consistency with Rust HTTP libraries, use StatusCode::OK instead of the literal 200.

-            .status(200)
+            .status(hyper::StatusCode::OK)
♻️ Duplicate comments (3)
Cargo.toml (1)

48-51: Well done on updating hyper to version 1.6.0.

The dependency updates properly address the previous review feedback and include the necessary supporting crates for the test infrastructure.

tests/utils.rs (2)

1-17: Module setup follows all guidelines.

The documentation clearly describes the module's purpose, and all imports are appropriately used.


16-31: Clean implementation of handler type and graceful shutdown.

The thread-safe handler type and shutdown mechanism properly address the previous review feedback about resource cleanup.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e171a99 and a16dc4d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml (2 hunks)
  • tests/e2e.rs (3 hunks)
  • tests/utils.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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.
  • 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.

Files:

  • tests/e2e.rs
  • tests/utils.rs
🧬 Code Graph Analysis (1)
tests/e2e.rs (2)
tests/utils.rs (2)
  • start_mitm (46-89)
  • shutdown (27-30)
src/main.rs (1)
  • new (156-160)
🔇 Additional comments (3)
tests/e2e.rs (2)

1-6: Module documentation properly added.

The documentation clearly explains the module's purpose and follows the required format.


52-93: Excellent test for GraphQL error path reporting.

The test effectively verifies the enhanced error diagnostics, uses appropriate timeout handling, and ensures proper cleanup. The mock response correctly simulates missing nodes to trigger the error path.

tests/utils.rs (1)

67-76: Service implementation handles requests correctly.

The mutex locking with expect() is appropriate for test code, and ignoring connection errors prevents test flakiness from transient network issues.

Comment thread Cargo.toml Outdated
Comment thread tests/e2e.rs Outdated
Comment thread tests/utils.rs Outdated
Comment thread tests/utils.rs
@leynos leynos force-pushed the codex/find-cause-of-badresponseserde-error branch from 8857b4f to 830c945 Compare July 30, 2025 00:40
@leynos leynos merged commit 35e6a76 into main Jul 30, 2025
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Aug 11, 2025
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.

1 participant