Skip to content

Add ERT tests, CI workflow, and shared command helpers#1

Merged
chocoelho merged 7 commits intomainfrom
add-tests-and-ci
Jan 23, 2026
Merged

Add ERT tests, CI workflow, and shared command helpers#1
chocoelho merged 7 commits intomainfrom
add-tests-and-ci

Conversation

@chocoelho
Copy link
Owner

@chocoelho chocoelho commented Jan 22, 2026

Summary by Sourcery

Add automated testing and CI for the cursor-agent Emacs package and refactor command execution helpers for better ANSI-aware output handling.

New Features:

  • Introduce ERT-based unit tests for cursor-agent in cursor-agent-test.el, including configuration, helper, and command presence checks.

Enhancements:

  • Refactor repeated shell/compilation invocations into shared helpers that run commands in compilation buffers with ANSI color handling and optional installation checks.
  • Improve robustness of setup and status checks by guarding calls to authentication functions and avoiding unused lambda parameters.
  • Document how to run the test suite locally and note that tests run automatically in CI.

CI:

  • Add a GitHub Actions workflow to lint, byte-compile, and run ERT tests for the package on pushes, pull requests, and manual triggers.

@chocoelho chocoelho requested a review from Copilot January 22, 2026 21:08
@chocoelho chocoelho self-assigned this Jan 22, 2026
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 22, 2026

Reviewer's Guide

Adds an ERT-based test suite and GitHub Actions CI for the cursor-agent Emacs package, introduces shared helpers for running CLI commands in compilation buffers with ANSI color handling and installation checks, and refines readiness/auth checks and docs to support the new workflow.

Class diagram for cursor-agent command helpers and list commands

classDiagram
    class CursorAgentModule {
      +cursor-agent-interactive(initial-prompt)
      +cursor-agent-process-region(start end)
      +cursor-agent-resume(chat-id)
      +cursor-agent-list-sessions()
      +cursor-agent-status()
      +cursor-agent-list-models()
      +cursor-agent-mcp-list()
      +cursor-agent-login()
      +cursor-agent-installed-p()
      +cursor-agent-ready-p()
      +cursor-agent-open-readme()
      -cursor-agent--run-command-in-compilation-buffer(command buffer-name header-text)
      -cursor-agent--run-list-command(command buffer-name header-text)
      -cursor-agent--install-sentinel(buffer-name)
    }

    class CursorAgentCLI {
      +ls
      +status
      +models
      +mcp_list
      +login
    }

    CursorAgentModule ..> CursorAgentCLI : uses

    CursorAgentModule ..> CursorAgentModule : cursor-agent--run-list-command uses cursor-agent-installed-p
    CursorAgentModule ..> CursorAgentModule : cursor-agent--run-list-command uses cursor-agent--run-command-in-compilation-buffer

    CursorAgentModule ..> CursorAgentModule : cursor-agent-list-sessions uses cursor-agent--run-list-command
    CursorAgentModule ..> CursorAgentModule : cursor-agent-status uses cursor-agent--run-list-command
    CursorAgentModule ..> CursorAgentModule : cursor-agent-list-models uses cursor-agent--run-list-command
    CursorAgentModule ..> CursorAgentModule : cursor-agent-mcp-list uses cursor-agent--run-list-command

    CursorAgentModule ..> CursorAgentModule : cursor-agent-interactive uses cursor-agent--run-command-in-compilation-buffer
    CursorAgentModule ..> CursorAgentModule : cursor-agent-process-region uses cursor-agent--run-command-in-compilation-buffer
    CursorAgentModule ..> CursorAgentModule : cursor-agent-ready-p uses cursor-agent-installed-p
Loading

File-Level Changes

Change Details Files
Introduce shared helpers for running cursor-agent shell commands in compilation buffers with ANSI color handling and reuse them across interactive commands.
  • Add cursor-agent--run-command-in-compilation-buffer to centralize async-shell-command usage with compilation-mode and compilation-filter for ANSI colors.
  • Add cursor-agent--run-list-command to wrap list-style CLI invocations with an installation check and shared buffer setup.
  • Refactor cursor-agent-prompt and cursor-agent-region to use the new helper instead of duplicating compilation buffer setup logic.
  • Refactor cursor-agent-list-sessions, cursor-agent-status, cursor-agent-list-models, and cursor-agent-mcp-list to use cursor-agent--run-list-command and update their docstrings to document ANSI color handling.
  • Adjust cursor-agent-login to set the process filter to compilation-filter so its compilation buffer also handles ANSI colors.
cursor-agent.el
Harden installation/authentication flow and minor internals for better robustness and tooling cleanliness.
  • Update cursor-agent-check-readiness to guard cursor-agent-check-auth with fboundp so readiness checks don’t error when the function is unavailable.
  • Ignore the process argument in cursor-agent--install-sentinel using a _proc parameter to avoid unused variable warnings.
  • Tidy control flow/paren structure in cursor-agent-check-auth and cursor-agent-readme without changing behavior.
cursor-agent.el
Add an ERT-based test suite that validates installation detection, authentication checks, configuration defaults, helper utilities, and command availability, plus an interactive test runner.
  • Create cursor-agent-test.el with unit tests for cursor-agent-installed-p and cursor-agent-check-auth using cl-letf-based mocks.
  • Add tests for configuration defaults (cursor-agent-command, cursor-agent-default-output-format, cursor-agent-use-force).
  • Add tests for helper functions such as cursor-agent--local-bin-in-path-p and cursor-agent--get-path-instruction for bash/zsh.
  • Add loading/bootstrapping helpers to robustly require or load cursor-agent.el in varying environments, including force-loading specific defuns if needed.
  • Add integration-style tests to assert that all expected interactive commands are defined and commandp, plus an interactive cursor-agent-test-run-all test runner.
  • Provide the cursor-agent-test feature for easy loading from Emacs and CI.
cursor-agent-test.el
Document how to run the new test suite locally and from the command line.
  • Add a Testing section to README.md describing how to load cursor-agent-test.el and run ERT tests interactively via M-x ert.
  • Document a non-interactive CLI invocation using emacs --batch with ert-run-tests-batch-and-exit, suitable for local scripting or CI.
  • Mention that tests run automatically in GitHub Actions.
README.md
Add a GitHub Actions workflow to lint, byte-compile, and run the ERT test suite on pushes, pull requests, and manual triggers.
  • Create .github/workflows/test.yml with a Test job running on push, pull_request, and workflow_dispatch.
  • Use purcell/setup-emacs to install Emacs 29.1 on ubuntu-latest.
  • Run leotaku/elisp-check with the melpa check on cursor-agent.el for linting and byte compilation.
  • Run leotaku/elisp-check with the ert check on cursor-agent-test.el to execute the ERT suite in CI.
.github/workflows/test.yml

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

@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 - I've found 2 issues, and left some high level feedback:

  • The four commands that list sessions/status/models/MCP now share nearly identical logic for setting up the buffer, mode, and async shell command; consider factoring this into a small helper to avoid duplication and keep future changes to the behavior in one place.
  • The new safety check in cursor-agent-verify-setup uses fboundp on cursor-agent-installed-p; if this function is guaranteed to exist whenever cursor-agent is loaded, you might prefer failing earlier via a require or simplifying the check to avoid raising a runtime error in normal usage.
  • In the tests, mocking core functions like executable-find and shell-command-to-string with fset can have surprising global side effects (especially under parallel test execution); consider using cl-letf or letf to temporarily rebind these functions in a more scoped way.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The four commands that list sessions/status/models/MCP now share nearly identical logic for setting up the buffer, mode, and async shell command; consider factoring this into a small helper to avoid duplication and keep future changes to the behavior in one place.
- The new safety check in `cursor-agent-verify-setup` uses `fboundp` on `cursor-agent-installed-p`; if this function is guaranteed to exist whenever `cursor-agent` is loaded, you might prefer failing earlier via a `require` or simplifying the check to avoid raising a runtime error in normal usage.
- In the tests, mocking core functions like `executable-find` and `shell-command-to-string` with `fset` can have surprising global side effects (especially under parallel test execution); consider using `cl-letf` or `letf` to temporarily rebind these functions in a more scoped way.

## Individual Comments

### Comment 1
<location> `cursor-agent.el:459-461` </location>
<code_context>
+          (with-current-buffer (get-buffer-create buffer-name)
+            (erase-buffer)
+            (insert "Test content")
+            (compilation-mode))
+          (should (buffer-live-p (get-buffer buffer-name)))
+          (should (eq major-mode 'compilation-mode)))
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `compilation-mode` with `async-shell-command` likely won’t run `compilation-filter-hook`, so ANSI color handling may not work as expected.

Because `async-shell-command` installs `shell-command--command-output-filter` instead of `compilation-filter`, `compilation-filter-hook` is never run and ANSI escape sequences will not be processed. To ensure `compilation-mode`’s hooks (including ANSI handling) actually apply, consider using `compilation-start` instead of `async-shell-command`, or explicitly set the process filter to `compilation-filter`. The same pattern shows up in the status/models/MCP commands and likely needs a shared fix.
</issue_to_address>

### Comment 2
<location> `README.md:353` </location>
<code_context>
+M-x ert RET cursor-agent-test-.* RET
+```
+
+Or from command line:
+
+```bash
</code_context>

<issue_to_address>
**nitpick (typo):** Consider changing "from command line" to "from the command line" for grammatical correctness.

Rephrase this line to: `Or from the command line:` to improve grammar and readability.

```suggestion
Or from the command line:
```
</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.

This comment was marked as outdated.

This comment was marked as outdated.

- Add comprehensive ERT test suite for cursor-agent
- Add testing section to README
- Update .gitignore for test artifacts
- Add GitHub Actions workflow for Emacs testing
- Support manual workflow triggering
- Configure linting and compilation checks
- Use purcell/setup-emacs and leotaku/elisp-check actions
- Factor out shared logic for list commands into helper function
- Fix ANSI color handling by using compilation-filter
- Add installation check for list commands
- Improve error handling for list-sessions command
@chocoelho
Copy link
Owner Author

@sourcery-ai summary

@chocoelho
Copy link
Owner Author

@sourcery-ai review

@chocoelho
Copy link
Owner Author

@sourcery-ai title

@sourcery-ai sourcery-ai bot changed the title Add Tests and CI/CD Pipeline Add ERT tests, CI workflow, and shared command helpers Jan 22, 2026
Copy link

@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 - I've found 3 issues, and left some high level feedback:

  • For cursor-agent-login, consider reusing cursor-agent--run-command-in-compilation-buffer to avoid duplicating the async command + compilation-filter setup and keep behavior consistent with the other command entry points.
  • The switch of the list-style commands (cursor-agent-list-sessions, cursor-agent-status, cursor-agent-list-models, cursor-agent-mcp-list) from synchronous shell-command-to-string with special-mode to asynchronous compilation-mode changes both timing and buffer keybindings; it may be worth confirming this UX change is intentional or gating it behind a user option.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- For `cursor-agent-login`, consider reusing `cursor-agent--run-command-in-compilation-buffer` to avoid duplicating the async command + `compilation-filter` setup and keep behavior consistent with the other command entry points.
- The switch of the list-style commands (`cursor-agent-list-sessions`, `cursor-agent-status`, `cursor-agent-list-models`, `cursor-agent-mcp-list`) from synchronous `shell-command-to-string` with `special-mode` to asynchronous `compilation-mode` changes both timing and buffer keybindings; it may be worth confirming this UX change is intentional or gating it behind a user option.

## Individual Comments

### Comment 1
<location> `cursor-agent.el:132-141` </location>
<code_context>
+      (setq cursor-agent-authenticated-p nil)
+      nil))))
+
+(defun cursor-agent--run-command-in-compilation-buffer (command buffer-name header-text)
+  "Run COMMAND in a compilation buffer with proper ANSI color handling.
+COMMAND is the shell command to execute.
+BUFFER-NAME is the name of the buffer to use.
+HEADER-TEXT is the text to insert at the start of the buffer.
+Sets the process filter to compilation-filter to ensure ANSI colors are handled."
+  (let ((buffer (get-buffer-create buffer-name)))
+    (with-current-buffer buffer
+      (erase-buffer)
+      (when header-text
+        (insert header-text))
+      (compilation-mode))
+    ;; Use async-shell-command but set the filter to compilation-filter
+    ;; to ensure compilation-filter-hook runs, which handles ANSI color codes
+    (let ((process (async-shell-command command buffer-name)))
+      (when process
+        (set-process-filter process 'compilation-filter)))
</code_context>

<issue_to_address>
**suggestion:** Consider reusing this helper in `cursor-agent-login` to avoid duplicating process/filter setup logic.

The same process start + `compilation-mode` + `set-process-filter` sequence exists in `cursor-agent-login`. Calling `cursor-agent--run-command-in-compilation-buffer` from there (after any login-specific preamble) would centralize the async/ANSI handling and keep future behavior changes in one place.

Suggested implementation:

```
(defun cursor-agent-login ()
  "Login to Cursor Agent."
  (interactive)
  (let* ((buffer-name "*Cursor Agent Login*")
         (header-text (concat
                       "Cursor Agent Login\n\n"
                       "Follow the instructions in this buffer to log in.\n\n")))
    (cursor-agent--run-command-in-compilation-buffer
     "cursor-agent login"
     buffer-name
     header-text)))

```

1. If the actual implementation of `cursor-agent-login` differs from the SEARCH block above (e.g., different buffer name, different header text, extra checks like `cursor-agent-installed-p` or environment validation), adjust the replacement to preserve that existing behavior while delegating the process setup to `cursor-agent--run-command-in-compilation-buffer`.
2. Ensure that any login-specific preamble (such as additional `insert` calls, instructions, or state setup) is incorporated into the `header-text` argument before calling `cursor-agent--run-command-in-compilation-buffer`, or executed just before that call in the function body.
3. If `cursor-agent-login` previously used a different shell command (e.g., included flags or environment variables), update the `"cursor-agent login"` string accordingly to match the original command.
</issue_to_address>

### Comment 2
<location> `cursor-agent.el:151-160` </location>
<code_context>
+        (set-process-filter process 'compilation-filter)))
+    (pop-to-buffer buffer-name)))
+
+(defun cursor-agent--run-list-command (command buffer-name header-text)
+  "Helper function to run a list command with installation check.
+COMMAND is the shell command to execute.
+BUFFER-NAME is the name of the buffer to use.
+HEADER-TEXT is the text to insert at the start of the buffer.
+Checks if cursor-agent is installed before running the command."
+  (unless (cursor-agent-installed-p)
+    (if (y-or-n-p "Cursor Agent CLI not found. Would you like to install it now? ")
+        (cursor-agent-install)
+      (user-error "Cursor Agent CLI not found.  Run 'M-x cursor-agent-install' to install")))
+  (cursor-agent--run-command-in-compilation-buffer
+   command
+   buffer-name
+   header-text))

 (defun cursor-agent--local-bin-in-path-p ()
</code_context>

<issue_to_address>
**question:** Listing commands no longer put buffers into `special-mode`, which changes interaction semantics.

These commands now yield buffers in `compilation-mode` instead of `special-mode`, so they’ve lost the previous read-only/navigation/quit behavior. If ANSI handling is the main reason for this change, consider re-enabling a read-only mode (e.g. `special-mode`) after the process completes, or explicitly marking the buffer read-only, so the prior interaction model is preserved while still using `compilation-filter`.
</issue_to_address>

### Comment 3
<location> `cursor-agent.el:609` </location>
<code_context>
           (view-mode)
           (message "Press 'q' to quit view mode"))
-      (user-error "README.md not found.  Please ensure the package is properly installed"))))
+      (user-error "README.md not found.  Please ensure the package is properly installed")))))

 (provide 'cursor-agent)
</code_context>

<issue_to_address>
**issue (bug_risk):** There appears to be one extra closing parenthesis at the end of `cursor-agent-open-readme`.

This extra `)` will unbalance the surrounding forms and prevent the file from reading/byte-compiling. The closing parens here should match the previous version (remove one `)`).
</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.

(view-mode)
(message "Press 'q' to quit view mode"))
(user-error "README.md not found. Please ensure the package is properly installed"))))
(user-error "README.md not found. Please ensure the package is properly installed")))))
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): There appears to be one extra closing parenthesis at the end of cursor-agent-open-readme.

This extra ) will unbalance the surrounding forms and prevent the file from reading/byte-compiling. The closing parens here should match the previous version (remove one )).

- Add Doom Emacs / Straight subsection: autoload behavior (run
  cursor-agent-install once to expose all commands) and warning not to
  unload/reload in config (recursive load).
- Use backtick quoting in docstrings: 'agent', 'agent models',
  'text'/'json'/'stream-json'.
…talled, check-auth and login

- cursor-agent-auto-install: nil/ask/auto/skip/once for install checks
- cursor-agent-list-commands-use-special-mode: list buffers switch to
  special-mode after completion (default t)
- cursor-agent--ensure-installed: central helper for install checks;
  commands (interactive, resume, login, shell-mode, update, run-list-command,
  verify-setup) use it
- cursor-agent--run-command-in-compilation-buffer: optional use-special-mode,
  process sentinel to switch to special-mode and set read-only; robust
  handling of async-shell-command return (process vs window)
- cursor-agent--run-list-command: pass use-special-mode to run-command
- cursor-agent-install: y-or-n-p confirm before installing
- cursor-agent-verify-setup: respect cursor-agent-auto-install and skip;
  fix (not installed) message branch
- cursor-agent-check-auth: (interactive), messages for OK/WARNING/ERROR,
  match "Logged in" in status; (let* ...)
- cursor-agent-login: use cursor-agent--run-command-in-compilation-buffer,
  sentinel to run cursor-agent-check-auth after login
…first)

- Known issues: Doom/Straight commands unavailable until first use (run
  cursor-agent-install once); do not unload/reload in after! (recursive load)
- Doom Method 2: use after! only for setq and keybindings; link to Known issues
- Doom Method 3: :build (:not compile), do not unload/reload, run
  cursor-agent-install once with :local-repo
- Doom Emacs Tips: after! only for setq/keybindings, not unload/load
- Added entry for the GitHub Actions local runner (/bin/act) to .gitignore
- Retained existing rule to ignore all log files (*.log)
@chocoelho chocoelho merged commit fdda0d0 into main Jan 23, 2026
2 checks passed
@chocoelho chocoelho deleted the add-tests-and-ci branch January 23, 2026 13:00
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