Skip to content

test: CLI integration test suite with JWT agent support (TC-001–TC-028)#334

Closed
m-golovchin wants to merge 80 commits into
codemie-ai:mainfrom
m-golovchin:test/cli-integration-tests
Closed

test: CLI integration test suite with JWT agent support (TC-001–TC-028)#334
m-golovchin wants to merge 80 commits into
codemie-ai:mainfrom
m-golovchin:test/cli-integration-tests

Conversation

@m-golovchin

@m-golovchin m-golovchin commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces a comprehensive integration test suite for the CodeMie Code CLI, covering CLI management commands, SSO/JWT-authenticated agent sessions, interactive PTY sessions, and budget/model queries (TC-014 through TC-028). It also includes the minimal production code changes required to make those test scenarios work end-to-end.


Production Source Code Changes

1. src/utils/auth.ts — JWT branch in getAuthenticatedClient

What: Added a JWT-specific code path at the top of getAuthenticatedClient. When config.authMethod === 'jwt', the function reads the token from the configured env var (defaulting to CODEMIE_JWT_TOKEN) or from config.jwtConfig.token, constructs an AuthConfig with tokenGetter: async () => token, and instantiates all SDK services, composing them into a CodeMieClient-shaped object.

Why: getAuthenticatedClient was SSO-only. Any CLI command that called it would attempt a browser-session cookie flow and fail in CI environments that only carry a JWT token.

Why this approach: The tokenGetter pattern was already in use in src/cli/commands/assistants/chat/index.ts. Adopting it in auth.ts follows established codebase precedent. The JWT branch only executes when authMethod === 'jwt', leaving the entire SSO path untouched.


2. src/cli/commands/assistants/chat/index.ts + types.ts--jwt-token flag on assistants chat

What: Added --jwt-token <token> as a Commander option. When the flag (or CODEMIE_JWT_TOKEN env var) is present, the command builds a minimal AssistantService-only client inline with tokenGetter, bypassing getAuthenticatedClient entirely.


3. src/providers/plugins/jwt/jwt.models.ts + index.tsJWTModelProxy

What: New class JWTModelProxy implementing ProviderModelFetcher. Registers itself against the bearer-auth provider so codemie models list works for JWT users (TC-022).


4. src/cli/commands/models.ts — Remove bearer-auth from UNSUPPORTED_PROVIDERS

What: Removed 'bearer-auth' from the UNSUPPORTED_PROVIDERS set. Required alongside JWTModelProxy so the guard no longer exits early for JWT profiles.


5. src/providers/plugins/jwt/jwt.template.tsagentHooks for extension install and --plugin-dir

What: Added agentHooks block with *.beforeRun (installs CodeMie extension) and claude.enrichArgs (injects --plugin-dir). JWT profiles were missing the extension-install wiring the SSO template already had.


6. src/utils/config.tsCODEMIE_HOME respected in loadAssistantsByScope

What: Changed global-scope base directory from os.homedir() to process.env.CODEMIE_HOME ?? os.homedir() so integration tests using isolated temp dirs see test-injected assistant configs instead of the developer's real ones.


7. src/utils/errors.ts — Guard against empty wrapText result in formatErrorForUser

What: Added a guard so an empty wrapText result falls back to context.error.message || '(unknown error)' instead of printing undefined.


8. src/env/types.tsauthServerUrl and authRealm on ProviderProfile

What: Added two optional fields to ProviderProfile for potential future use.


Test Infrastructure

Auth model — CI_IS_LOCAL_RUN dual-mode

Tests gate on the CI_IS_LOCAL_RUN env flag:

Value Mode Auth mechanism
true (default) SSO / local dev Existing sso-autotest profile in ~/.codemie
false JWT / CI pipeline Bearer token fetched from CI_CODEMIE_AUTH_URL

Integration test files

File Coverage
agent-task.test.ts TC-016: --task exit code and stdout
agent-task-session.test.ts Session and metrics artefact validation
agent-model.test.ts TC-020, TC-021, TC-022, TC-024: model selection, metrics, models list, /model PTY
agent-skills.test.ts TC-025: skill slash command invocation (PTY)
agent-assistant.test.ts TC-014, TC-015, TC-026: assistant setup wizard and chat
agent-jwt-token.test.ts TC-017, TC-027: --profile + --jwt-token override [JWT-only]
agent-jwt-budget.test.ts TC-028: all-budget project profile [JWT-only]
agent-negative.test.ts TC-018: invalid JWT [JWT-only]; TC-019: no profile/token [dual-mode]
agent-shortcuts.test.ts Slash command smoke tests
cli-commands/*.test.ts Doctor, help, version, list, profile, skills, workflow, error-handling

Test helpers

File Purpose
tests/helpers/jwt-auth.ts fetchJwtToken(), writeJwtProfile(), jwtCleanEnv()
tests/helpers/sso-auth.ts writeSsoProfile(), ssoCleanEnv(), copySsoCredentials(), setup/teardown
tests/helpers/pty-session.ts PTY wrapper around node-pty for interactive session testing
tests/helpers/metrics.ts getLatestMetricsRecord() helper
tests/helpers/test-env.ts getTestEnvFlagOrDefault()
tests/helpers/session-poll.ts pollForSession() with timeout

Test setup / config

File Purpose
tests/setup/load-test-env.ts Loads .env.test.local before each test file
tests/setup/agent-build-setup.ts Vitest globalSetup: npm run build, claude install, SSO credential validation with auto-login
env.test.local.example Template for local developer test configuration

Vitest workspace config

vitest.config.ts uses defineConfig + defineProject to define three named projects in a single file:

Project Includes GlobalSetup
unit src/**/*.test.ts none
cli tests/integration/**/*.test.ts (excl. agent-*) none
agent tests/integration/agent-*.test.ts agent-build-setup.ts (build + SSO auth)

npm scripts

Script What it runs
npm run test:unit unit project only
npm run test:integration cli project only (no network auth)
npm run test:integration:cli cli project only
npm run test:integration:agent agent project (requires SSO or JWT credentials)
npm run test:run unit + cli (no agent)
npm run test:all unit → cli → agent sequentially

Test Plan

  • npm run test:unit — all unit tests pass (no credential requirement)
  • npm run test:integration:cli — CLI management tests pass (no credential requirement)
  • For SSO (local dev): ensure valid sso-autotest profile in ~/.codemie or run — credentials are auto-validated and browser login is triggered if expired
  • For JWT (CI): populate .env.test.local per env.test.local.example with CI_IS_LOCAL_RUN=false and JWT credentials
  • npm run test:integration:agent — agent tests pass against a live CodeMie environment

Comment thread src/cli/commands/assistants/chat/index.ts
Comment thread src/env/types.ts Outdated
Comment thread src/providers/plugins/jwt/jwt.models.ts Outdated
Comment thread src/providers/plugins/jwt/jwt.models.ts Outdated
Comment thread src/providers/plugins/jwt/jwt.models.ts Outdated
Comment thread src/providers/plugins/jwt/jwt.models.ts Outdated
Comment thread src/providers/plugins/jwt/jwt.template.ts Outdated
Comment thread src/utils/auth.ts Outdated
Comment thread src/utils/auth.ts Outdated
@m-golovchin m-golovchin requested a review from Dark-Sun June 8, 2026 09:13
@m-golovchin m-golovchin force-pushed the test/cli-integration-tests branch 3 times, most recently from 15f80e0 to 4a9cd33 Compare June 10, 2026 15:27
MaksymHolovchyn and others added 21 commits June 10, 2026 18:36
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Guard against missing env vars and bad HTTP status in fetchJwtToken; fix
waitForOutput race condition (always reject on close), add stdout null
guard, and wrap SIGTERM/SIGKILL in try/catch for already-dead processes.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…ture

Adds a dedicated Vitest config for agent integration tests with a globalSetup
that builds dist/ once per session, plus two new npm scripts for targeted test runs.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Replace deprecated `poolOptions.threads.maxThreads/minThreads` with top-level `maxWorkers: 4` as required by Vitest 4.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…file)

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Refactor TC-003 describe block to call spawnSync once in beforeAll and
reuse the cached result in both it blocks, eliminating duplicate CLI invocations.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Replace the 2-test stub with a comprehensive suite covering profile list,
switch, delete (inactive + active), rename, status with no profiles, and
negative paths (switch/rename to non-existent/conflicting names).

Key implementation notes:
- runCLI sets cwd=codemieHome so no local .codemie/ config interferes
- NODE_ENV=test disables CLI auto-update during subprocess invocations
- CODEMIE_DEBUG=true surfaces logger.error() messages to stderr for
  negative-path assertions
- TC-006 uses 'profile' list (not 'profile status') to avoid auth prompts
  in non-TTY environments
- TC-008 assertions updated to match actual CLI behaviour: deleting the
  active profile succeeds with "No profiles remaining" rather than erroring

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Refactored TC-006, TC-007, and TC-009 describe blocks so that the
profile switch, delete, and rename CLI calls are executed once in
beforeAll and their results stored, eliminating inter-it side-effect
dependencies.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Extends skills.test.ts with two JWT-gated describe blocks:
- TC-012: full add/list/remove/list lifecycle against a real marketplace source
- TC-013: error-path validation for a nonexistent skill source

Both suites are skipped unless INCLUDE_JWT_TESTS=true is set.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Adds integration test for 'codemie models list' guarded behind INCLUDE_JWT_TESTS,
caching the spawnSync result in beforeAll to avoid double invocation.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Add agent-jwt-basic.test.ts covering JWT token auth flows, invalid token
negative cases, missing profile/token negative case, and agent health check.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Replace the credential-leaking cleanEnv() implementation (which copied
full process.env and deleted 2 keys) with a minimal allowlist that only
passes through PATH and NODE_PATH, preventing accidental credential
exposure in child processes.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…rge TC-015 its

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…tderr guards

Add ?? '' fallbacks to PATH/NODE_PATH in cleanEnv() and guard all
result.stdout + result.stderr concatenations with nullish coalescing.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Implements TC-020 (session model matches profile model for sonnet and
haiku) and TC-021 (all three tier models populated and distinct) under
describe.runIf(INCLUDE_JWT_TESTS).

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Implements interactive agent session tests: /model slash-command switch
(TC-024), skill slash-command invocation in a running session (TC-025),
and non-interactive assistant chat PONG assertion (TC-026). All gated
under INCLUDE_JWT_TESTS=true and skip cleanly without credentials.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Add combined stdout+stderr as the Vitest failure message on the exit-code
assertion in TC-028 so CI shows what the agent printed when the test fails.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Add a beforeAll guard that throws a clear error when CI_CODEMIE_ASSISTANT_ID
is absent, preventing the empty-regex assertion from silently passing.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Add an early-exit guard in TC-026's beforeAll that throws a descriptive
error when assistantId is empty, preventing a confusing CLI failure and
ensuring a clean skip when INCLUDE_JWT_TESTS=true but the env var is absent.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
@m-golovchin m-golovchin force-pushed the test/cli-integration-tests branch from 4a9cd33 to 0669d4c Compare June 10, 2026 15:39
MaksymHolovchyn and others added 2 commits June 10, 2026 18:43
- Add agent JWT integration tests (TC-001–TC-028) for basic, budget, models, and interactive flows
- Add PTY session helper, metrics helper, and JWT auth helper for integration test infrastructure
- Add JWTModelProxy to bearer-auth provider for `models list` support
- Add --jwt-token option to assistants chat command
- Add load-test-env setup and env.test.local.example for local test configuration
- Exclude agent/JWT integration tests from lint-staged pre-commit vitest run
- Skip egress-shim exit-code tests on Windows (NODE_OPTIONS --require causes access violation)
- Increase error-handling test beforeAll timeout to 30s for concurrent load
- Fix TC-014, TC-022, TC-024, TC-026 guards for missing CI env vars

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Documents the test tier strategy, authentication approach, and
TC-001–TC-028 test case catalogue for the JWT/agent integration tests.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
MaksymHolovchyn and others added 28 commits June 22, 2026 18:37
The waitFor timeout already includes last 20 PTY lines in the error
message; the pty-debug.txt write was redundant and deleted by afterAll.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…lifecycle

Extract TC-025 into agent-skills.test.ts with SSO/JWT dual-mode auth.
Dynamically create and delete the test skill via SDK (createSkill/deleteSkill)
instead of relying on a static CI_CODEMIE_SKILL_NAME env var.
TC-024 is now standalone in agent-interactive-session.test.ts.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Rename reflects the file's actual scope (TC-024 /model switch test only).
Update header comment, outer describe label, temp dir prefix, and vitest config.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…ent-model-switch

Rename agent-model-switch.test.ts to agent-model.test.ts and absorb TC-020
and TC-021 from agent-jwt-models.test.ts. Both tests converted from
JWT-only (INCLUDE_JWT_TESTS gate) to SSO/JWT dual-mode via CI_IS_LOCAL_RUN.
Adds writeProfileWithModel() helper for model-specific SSO/JWT profiles.
Also fixes TC-020's missing haikuHome cleanup in afterAll.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Remove INCLUDE_JWT_TESTS gate, add CI_IS_LOCAL_RUN dual-mode support.
Fix __dirname to use fileURLToPath. Add stdout/stderr to exit-code
failure message for easier debugging.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Move codemie-claude health check out of the JWT-only agent-jwt-basic gate
into a standalone cli-commands/health.test.ts file. The health subcommand
requires no auth, so the test runs unconditionally without any SSO/JWT setup.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…ofile test

- agent-task.test.ts: converts TC-016 to SSO/JWT dual-mode; asserts --task
  exits 0 and agent response appears in stdout (non-interactive output path
  not covered by PTY tests)
- agent-jwt-token.test.ts: new TC-027, JWT-only; tests --jwt-token with no
  pre-written profile and empty CODEMIE_HOME; skipped label in describe name
  makes the reason visible in test output
- agent-jwt-basic.test.ts: remove TC-016 (now in agent-task.test.ts)
- vitest.agent.config.ts: update file list comment with TC references

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Move negative agent test cases out of the INCLUDE_JWT_TESTS gate into a
dedicated file. TC-018 (invalid JWT token) stays JWT-only with a skip label;
TC-019 (no profile/no auth) is converted to dual-mode since an empty
CODEMIE_HOME fails the same way in both SSO and JWT modes.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
…verride

Replace the simple profile+token run in TC-017 with a more meaningful test:
write a config with an SSO profile as active and a JWT profile as non-active,
then run with --profile <jwt> --jwt-token <token> to verify that both the
active profile and the auth method are overridden by the CLI flags.

TC-017 and TC-027 are now both in agent-jwt-token.test.ts. agent-jwt-basic
is deleted — all its tests have moved to purpose-specific files.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
The provider field in the session comes from the profile config, not from
the --jwt-token flag at runtime. Restoring bearer-auth on profile-jwt-override
makes the session provider the observable proof that both --profile and
--jwt-token overrides worked: bearer-auth confirms the non-active profile was
selected and SSO was not used.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Remove the original May 2026 spec files (2026-05-19 and 2026-05-27) and
replace with a current design document that reflects the dual-mode auth
model (CI_IS_LOCAL_RUN), the updated file layout, the full TC map, and
the SSO/PTY helper layer added during this branch.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
codemie has no 'health' subcommand and codemie-claude health requires
the claude binary to be installed, which fails in CI. TC-031 is retired;
existing doctor, list, and version tests cover no-auth CLI smoke checks.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
cli-commands/models.test.ts used hybrid auth (SSO + JWT) which doesn't
belong in the no-auth cli-commands suite. Move TC-022 into agent-model.test.ts
alongside the other dual-mode model tests (TC-020/021/024).

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
- Replace vitest.config.ts + vitest.agent.config.ts with a single
  defineConfig/defineProject workspace (unit, cli, agent projects)
- Add test:run (unit+cli), test:all (unit+cli+agent sequential) scripts
- Remove test:e2e (no e2e tests exist), deduplicate test:all entry
- Fix TC-019: add cwd:testHome to spawnSync so ConfigLoader does not
  pick up .codemie/codemie-cli.config.json from the repo root
- Remove agent test steps from CI pipeline (unit+cli only in pipeline)
- Update agent-task-session.test.ts run comment to use npm script

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
- TC-029 (agent-setup): SSO setup wizard PTY test — walks the full
  interactive wizard, creates a profile, and verifies the written config
- TC-030 (self-update): verify --check exits 0 and reports current version
- pty-session: waitFor and onData now also check the incomplete tail line
  so input prompts (which never emit a trailing \n) are detectable
- spec: add TC-030 row and self-update.test.ts to directory layout

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
… in agent integration tests

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Remove per-file setupSsoAutotestProfile/teardownSsoAutotestProfile calls
from agent-model.test.ts outer beforeAll/afterAll. The global setup in
agent-build-setup.ts already owns the full SSO profile lifecycle; the
per-file calls write to ~/.codemie/codemie-cli.config.json concurrently
with other workers that use ~/.codemie directly, causing a race condition
that prevents TC-020/TC-021 from writing metrics in parallel runs.

Also quote the engine binary path in validate-secrets.js to handle
paths with spaces on Windows.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
When -y/--yes is supplied the action computed interactive=false but did not
forward it to runSkillsCli, which defaulted to interactive=true.  In
interactive mode the close handler calls process.stderr.write on the
buffered stderr, which raises EPIPE when the parent process has a piped
stderr (e.g. integration test spawnSync).  The uncaught write error caused
Node to exit with code 1 instead of propagating the real upstream exit code.

Fix: pass interactive to runSkillsCli so non-interactive runs use piped
stdio and avoid the write.  Also forward result.stderr explicitly in
non-interactive failure paths so egress-blocked and other markers are still
visible to the caller's stderr stream.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
@codemie-ai

Copy link
Copy Markdown
Owner

[AUTO_CLOSE_WARNING] 🔒 This pull request was automatically closed after being older than 30 days with no activity. Contact maintainers to reopen if needed.

@codemie-ai codemie-ai closed this Jul 5, 2026
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.

4 participants