Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

👍 Show stack-trace on plugin error #429

Merged
merged 3 commits into from
Nov 28, 2024
Merged

Conversation

lambdalisue
Copy link
Member

@lambdalisue lambdalisue commented Nov 26, 2024

Continue from #428

The After shows that the original error was occcurred in dummy_dispatcher_plugin.ts:19:13 but Before. This stack-trace will help developers to find a reason of issues.

Before
Failed to call 'fail' API in 'dummy': Dummy failure
After
Failed to call 'fail' API in 'dummy': Error: Dummy failure
    at Object.fail (file:///Users/alisue/ogh/vim-denops/denops.vim/tests/denops/testdata/dummy_dispatcher_plugin.ts:19:13)
    at eventLoopTick (ext:core/01_core.js:214:9)
    at async Plugin.call (file:///Users/alisue/ogh/vim-denops/denops.vim/denops/@denops-private/service.ts:276:14)
    at async Service.#dispatch (file:///Users/alisue/ogh/vim-denops/denops.vim/denops/@denops-private/service.ts:117:12)
    at async Service.dispatchAsync (file:///Users/alisue/ogh/vim-denops/denops.vim/denops/@denops-private/service.ts:141:17)
    at async dispatch (https://jsr.io/@lambdalisue/messagepack-rpc/2.4.0/dispatcher.ts:36:12)
    at async Session.#dispatch (https://jsr.io/@lambdalisue/messagepack-rpc/2.4.0/session.ts:255:22)
    at async Session.#handleNotificationMessage (https://jsr.io/@lambdalisue/messagepack-rpc/2.4.0/session.ts:310:25)

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a new fail method in the dispatcher to simulate error scenarios.
  • Bug Fixes

    • Enhanced error handling in test cases for dispatcher methods, providing clearer error messages for various failure scenarios.
  • Tests

    • Added new test cases to validate error handling for dispatcher method failures in the test suite.
    • Improved error reporting in existing tests for better clarity on failures.

Copy link

coderabbitai bot commented Nov 26, 2024

Walkthrough

The changes introduce new test cases and modify existing ones for the denops#request_async() and denops#request() functions. Specifically, the tests now handle scenarios where the dispatcher method throws an error, ensuring that the functions return appropriately and invoke failure callbacks as needed. Additionally, a new fail method is added to the dummy dispatcher plugin to simulate failure scenarios, enhancing the robustness and clarity of error handling in the test suite.

Changes

File Path Change Summary
tests/denops/runtime/functions/denops/request_async_test.ts Added a test case for denops#request_async() to handle dispatcher method errors, verifying return behavior and failure callback invocation.
tests/denops/runtime/functions/denops/request_test.ts Modified denops#request() tests to include a new case for dispatcher method errors and updated error messages for clarity.
tests/denops/testdata/dummy_dispatcher_plugin.ts Introduced a new fail method in the dispatcher to simulate an error scenario during testing.
denops/@denops-private/service.ts Enhanced error handling in the call method of the Plugin class to prefer the stack property of errors for more detailed reporting.

Possibly related PRs

  • 🌿 add/improve tests for interrupt #422: The changes in this PR enhance the tests for the denops#interrupt() function, which is related to the error handling improvements made in the main PR for the denops#request_async() function, as both involve handling scenarios where functions may fail or throw errors.

Poem

In the land of Denops, where bunnies play,
New tests hop in, brightening the day.
With a fail method added, errors in sight,
Our code now dances, robust and bright!
So let’s celebrate with a joyful cheer,
For testing's a journey, and we hold it dear! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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.

Copy link

codecov bot commented Nov 26, 2024

Codecov Report

Attention: Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.

Project coverage is 95.42%. Comparing base (0c72261) to head (95a8dad).
Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
denops/@denops-private/service.ts 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #429      +/-   ##
==========================================
- Coverage   95.55%   95.42%   -0.14%     
==========================================
  Files          25       25              
  Lines        1418     1420       +2     
  Branches      180      180              
==========================================
  Hits         1355     1355              
- Misses         60       62       +2     
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@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: 0

🧹 Outside diff range and nitpick comments (3)
tests/denops/testdata/dummy_dispatcher_plugin.ts (1)

17-20: LGTM! Consider making the error message configurable.

The implementation is clean and follows the existing patterns. The addition of this method will help improve test coverage for error handling scenarios.

Consider making the error message configurable to support different test scenarios:

-    fail: async () => {
+    fail: async (message = "Dummy failure") => {
       await delay(MIMIC_DISPATCHER_METHOD_DELAY);
-      throw new Error("Dummy failure");
+      throw new Error(message);
     },
tests/denops/runtime/functions/denops/request_test.ts (1)

69-84: LGTM! Consider expanding error scenarios.

The test case properly verifies error propagation from the dispatcher method.

Consider adding more test scenarios:

 await t.step("if the dispatcher method throws an error", async (t) => {
   await t.step("throws an error", async () => {
     await assertRejects(
       () =>
         host.call(
           "denops#request",
           "dummy",
           "fail",
           ["foo"],
         ),
       Error,
       "Failed to call 'fail' API in 'dummy': Dummy failure",
     );
   });
+  await t.step("throws with custom error message", async () => {
+    await assertRejects(
+      () =>
+        host.call(
+          "denops#request",
+          "dummy",
+          "fail",
+          ["custom error"],
+        ),
+      Error,
+      "Failed to call 'fail' API in 'dummy': custom error",
+    );
+  });
 });
tests/denops/runtime/functions/denops/request_async_test.ts (1)

118-150: Consider adding additional test assertions for completeness.

To make the error handling test more comprehensive, consider:

  1. Verifying that the success callback is never called
  2. Adding assertions to ensure the error type consistency across different failure scenarios

Example addition:

 await t.step("calls failure callback", async () => {
   await wait(() => host.call("eval", "len(g:__test_denops_events)"));
+  // Verify success callback was not called
+  const events = await host.call("eval", "g:__test_denops_events") as unknown[];
+  assertEquals(
+    events.filter((e) => 
+      Array.isArray(e) && e[0] === "TestDenopsRequestAsyncSuccess:Called"
+    ).length,
+    0
+  );
   assertObjectMatch(
-    await host.call("eval", "g:__test_denops_events") as unknown[],
+    events,
     {
         "TestDenopsRequestAsyncFailure:Called",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0c72261 and 74715fa.

📒 Files selected for processing (3)
  • tests/denops/runtime/functions/denops/request_async_test.ts (1 hunks)
  • tests/denops/runtime/functions/denops/request_test.ts (2 hunks)
  • tests/denops/testdata/dummy_dispatcher_plugin.ts (1 hunks)
🔇 Additional comments (4)
tests/denops/runtime/functions/denops/request_test.ts (2)

96-96: LGTM! Error message improvement.

The updated error message provides better clarity by specifically indicating that the dispatcher method is not a function.


Line range hint 69-96: Verify corresponding implementation changes.

The test changes look good, but let's ensure the implementation properly supports these error scenarios.

✅ Verification successful

Implementation properly supports the test scenarios

The implementation in service.ts correctly handles the error scenarios being tested:

  1. In the Plugin.call() method (line 271-280), it properly wraps dispatcher method errors with the exact error message format being tested:
async call(fn: string, ...args: unknown[]): Promise<unknown> {
  try {
    return await this.#denops.dispatcher[fn](...args);
  } catch (err) {
    const errMsg = err instanceof Error ? err.message : String(err);
    throw new Error(
      `Failed to call '${fn}' API in '${this.name}': ${errMsg}`,
    );
  }
}

This implementation correctly handles both test cases:

  • When the dispatcher method throws an error (the "Dummy failure" case)
  • When the method doesn't exist (the "not a function" case)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation of error handling in the source code
rg -A 5 "Failed to call.*API in.*:" --glob '!tests/**'

Length of output: 349


Script:

#!/bin/bash
# Let's examine the implementation in service.ts to understand the error handling
ast-grep --pattern 'class $_ {
  $$$
  async request($$$) {
    $$$
  }
  $$$
}'

Length of output: 82


Script:

#!/bin/bash
# Let's try a different approach to find the request implementation
cat denops/@denops-private/service.ts

Length of output: 9716

tests/denops/runtime/functions/denops/request_async_test.ts (2)

118-150: LGTM! Well-structured test case for error handling.

The test case effectively verifies both immediate return behavior and proper failure callback invocation. The structure follows the established patterns and maintains consistency with other tests in the file.


122-122: Verify the dummy dispatcher implementation.

The test calls the 'fail' method, which should be implemented in the dummy dispatcher plugin.

✅ Verification successful

The 'fail' method is properly implemented in the dummy dispatcher plugin

The verification shows that the 'fail' method is implemented in tests/denops/testdata/dummy_dispatcher_plugin.ts as an async function that throws a "Dummy failure" error after a delay, which is exactly what's needed for testing the failure callback in the test case.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the 'fail' method implementation in the dummy dispatcher plugin

# Find and check the content of the dummy dispatcher plugin
rg -A 5 "fail.*=.*async.*function" "$(fd -t f dummy_dispatcher_plugin.ts)"

Length of output: 123


Script:

#!/bin/bash
# Let's first check if the file exists and its content
fd -t f dummy_dispatcher_plugin.ts

# Let's also try to find any file containing 'dummy' in its name
fd -t f dummy

# Let's check for any file containing 'fail' method implementation
rg -A 5 "fail.*=" 

Length of output: 13074

Copy link

@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: 0

🧹 Outside diff range and nitpick comments (2)
denops/@denops-private/service.ts (2)

278-283: Consider unifying error handling with toVimError

The toVimError utility function already handles stack traces, but it's only used in the dispatch method. Consider using it in the call method as well to maintain consistency in error handling across sync and async operations.

Here's a suggested refactor:

    } catch (err) {
-      const errMsg = err instanceof Error
-        ? err.stack ?? err.message // Prefer 'stack' if available
-        : String(err);
+      const errMsg = toVimError(err);
      throw new Error(
        `Failed to call '${fn}' API in '${this.name}': ${errMsg}`,
      );
    }

Also applies to: 391-397


Line range hint 146-159: Consider standardizing error format between sync and async dispatch

The async dispatch uses toErrorObject while sync dispatch uses toVimError, which could lead to inconsistent error reporting. Consider standardizing the error format to provide a consistent debugging experience.

Some options to consider:

  1. Use toVimError consistently across both sync and async paths
  2. Create a new utility function that handles both cases uniformly
  3. Document the intentional difference if it serves a specific purpose
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 74715fa and 95a8dad.

📒 Files selected for processing (3)
  • denops/@denops-private/service.ts (1 hunks)
  • tests/denops/runtime/functions/denops/request_async_test.ts (3 hunks)
  • tests/denops/runtime/functions/denops/request_test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/denops/runtime/functions/denops/request_async_test.ts
  • tests/denops/runtime/functions/denops/request_test.ts
🔇 Additional comments (1)
denops/@denops-private/service.ts (1)

278-280: LGTM! Enhanced error reporting implementation.

The implementation correctly prioritizes the stack trace while falling back to the error message, which aligns with the PR objective of improving error reporting. The use of the nullish coalescing operator ensures graceful fallback.

Let's verify the consistency of error handling across the codebase:

✅ Verification successful

Based on the search results, I can now generate the final response:

Error handling is consistent across the codebase

The codebase already follows consistent error handling patterns. The enhanced error reporting with stack traces in the reviewed code is unique to the call method as it's a critical error boundary where detailed error information is valuable for plugin debugging. Other error instances are primarily for control flow and specific error conditions where stack traces aren't as crucial.

Key observations:

  • Most error constructions are for specific, known error conditions (e.g., "Service closed", "No host is bound")
  • Test files appropriately use simple error messages
  • The enhanced error reporting is correctly placed at the plugin execution boundary where stack traces are most valuable
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for similar error handling patterns that might benefit from the same enhancement

# Search for Error message construction patterns
rg "new Error\(" -A 2

# Search for error handling in catch blocks
ast-grep --pattern 'catch (err) {
  $$$
}'

Length of output: 9384

@lambdalisue lambdalisue merged commit e3fd503 into main Nov 28, 2024
11 of 13 checks passed
@lambdalisue lambdalisue deleted the stacktrace-on-error branch November 28, 2024 04:01
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.

2 participants