|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +This is `@codifycli/plugin-test`, a testing framework for Codify plugins. It provides utilities to test plugin lifecycle operations (initialize, validate, plan, apply, import, destroy) by spawning plugin processes and communicating via IPC messages. |
| 8 | + |
| 9 | +## Build and Test Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Run tests |
| 13 | +npm test |
| 14 | + |
| 15 | +# Build the project |
| 16 | +npm run prepublishOnly |
| 17 | + |
| 18 | +# Start the project (for testing) |
| 19 | +npm start |
| 20 | +``` |
| 21 | + |
| 22 | +### Running Single Tests |
| 23 | + |
| 24 | +Tests use Vitest. To run a specific test file: |
| 25 | +```bash |
| 26 | +npx vitest src/test-utils.test.ts |
| 27 | +``` |
| 28 | + |
| 29 | +To run tests matching a pattern: |
| 30 | +```bash |
| 31 | +npx vitest --grep "pattern" |
| 32 | +``` |
| 33 | + |
| 34 | +## Architecture |
| 35 | + |
| 36 | +### Core Components |
| 37 | + |
| 38 | +**PluginTester** (`src/plugin-tester.ts`) |
| 39 | +- Main API for testing plugins end-to-end |
| 40 | +- `fullTest()`: Executes complete plugin lifecycle (validate → plan → apply → import → modify → destroy) |
| 41 | +- `install()`: Tests only installation flow (validate → plan → apply) |
| 42 | +- `uninstall()`: Tests destroy operations |
| 43 | +- Automatically filters configs by OS using `ResourceConfig.os` field |
| 44 | +- Handles multiple configs by adding unique names when needed |
| 45 | + |
| 46 | +**PluginProcess** (`src/plugin-process.ts`) |
| 47 | +- Manages individual plugin process lifecycle |
| 48 | +- Spawns plugin using `fork()` with tsx loader (`--import tsx/esm`) |
| 49 | +- Handles bidirectional IPC communication |
| 50 | +- Responds to plugin requests: `COMMAND_REQUEST`, `PRESS_KEY_TO_CONTINUE_REQUEST`, `CODIFY_CREDENTIALS_REQUEST` |
| 51 | +- Methods mirror plugin API: `initialize()`, `validate()`, `plan()`, `apply()`, `import()` |
| 52 | +- Debug mode: Set `DEBUG` env var to enable `--inspect-brk=9221` |
| 53 | + |
| 54 | +**Spawn Utilities** (`src/spawn.ts`) |
| 55 | +- `testSpawn()`: Execute commands interactively in PTY (for testing) |
| 56 | +- `spawnSafe()`: Low-level command execution with security checks |
| 57 | +- Blocks direct `sudo` usage (must use `requiresRoot` option) |
| 58 | +- Strips ANSI codes from output |
| 59 | +- Supports stdin passthrough, custom env vars, cwd |
| 60 | + |
| 61 | +**TestUtils** (`src/test-utils.ts`) |
| 62 | +- `sendMessageAndAwaitResponse()`: Helper for IPC request/response pattern |
| 63 | +- Shell utilities: Detects bash/zsh, provides shell rc paths and source commands |
| 64 | +- Platform helpers: `isMacOS()`, `isLinux()` |
| 65 | +- Prerequisite installers: `ensureHomebrewInstalledOnMacOs()`, `ensureXcodeInstalledOnMacOs()` |
| 66 | + |
| 67 | +### IPC Communication Flow |
| 68 | + |
| 69 | +1. Test creates `PluginProcess` which forks plugin with IPC enabled |
| 70 | +2. Test sends IPC messages (e.g., `{cmd: 'plan', data: {...}, requestId: '...'}` |
| 71 | +3. Plugin processes request and may send request messages back (e.g., `COMMAND_REQUEST`) |
| 72 | +4. `PluginProcess` intercepts requests, executes via `spawnSafe()`, sends response |
| 73 | +5. Plugin completes and sends final response matching original `requestId` |
| 74 | + |
| 75 | +### Plugin Testing Pattern |
| 76 | + |
| 77 | +Typical test structure: |
| 78 | +```typescript |
| 79 | +// Filter configs by OS automatically |
| 80 | +const configs: ResourceConfig[] = [ |
| 81 | + { type: 'my-resource', param: 'value', os: [ResourceOs.MACOS] } |
| 82 | +]; |
| 83 | + |
| 84 | +await PluginTester.fullTest('/path/to/plugin', configs, { |
| 85 | + validatePlan: (plans) => { |
| 86 | + // Custom assertions on plan results |
| 87 | + }, |
| 88 | + validateApply: (plans) => { |
| 89 | + // Custom assertions after apply |
| 90 | + }, |
| 91 | + testModify: { |
| 92 | + modifiedConfigs: [/* updated configs */], |
| 93 | + validateModify: (plans) => { |
| 94 | + // Verify modify operation |
| 95 | + } |
| 96 | + } |
| 97 | +}); |
| 98 | +``` |
| 99 | + |
| 100 | +### OS Filtering |
| 101 | + |
| 102 | +Tests automatically filter configs by OS: |
| 103 | +- Configs with `os: [ResourceOs.MACOS]` only run on macOS |
| 104 | +- Configs with `os: [ResourceOs.LINUX]` only run on Linux |
| 105 | +- No `os` field means runs on all platforms |
| 106 | +- Implemented in `PluginTester.fullTest()` using `getPlatformOs()` |
| 107 | + |
| 108 | +## Key Technical Details |
| 109 | + |
| 110 | +- **Module System**: ESM with NodeNext module resolution |
| 111 | +- **TypeScript**: Strict mode with decorators support |
| 112 | +- **Output**: Compiled to `dist/` directory |
| 113 | +- **Node Version**: Requires Node 18+ (configured via `codify.json` for NVM) |
| 114 | +- **IPC Validation**: All messages validated against schemas from `@codifycli/schemas` using AJV |
| 115 | +- **Security**: Plugins spawned without direct sudo access; must request via `COMMAND_REQUEST` |
| 116 | +- **PTY Support**: Uses `@homebridge/node-pty-prebuilt-multiarch` for interactive command execution |
| 117 | +- **Shell History**: Commands run with `HISTORY_IGNORE` (bash) or `HISTIGNORE` (zsh) set |
| 118 | + |
| 119 | +## Dependencies |
| 120 | + |
| 121 | +Runtime dependencies (`@codifycli/schemas`, validation libraries, PTY) vs dev dependencies (`@codifycli/plugin-core` for development). |
| 122 | + |
| 123 | +## Environment Variables |
| 124 | + |
| 125 | +- `DEBUG`: Enable plugin debug mode with breakpoint |
| 126 | +- `VITE_CODIFY_TEST_JWT`: Required for tests that need Codify credentials |
0 commit comments