A comprehensive testing framework for Codify plugins. This package provides utilities to test the complete lifecycle of Codify plugins including initialization, validation, planning, applying, importing, and destroying resources.
npm install --save-dev @codifycli/plugin-testimport{PluginTester}from'@codifycli/plugin-test';import{ResourceConfig,ResourceOs}from'@codifycli/schemas';// Define the resources you want to testconstconfigs: ResourceConfig[]=[{type: 'homebrew',os: [ResourceOs.MACOS]}];// Run a full lifecycle testawaitPluginTester.fullTest('/path/to/your/plugin/index.js',configs,{validatePlan: (plans)=>{// Assert that the plan is correctexpect(plans[0].operation).toBe('create');},validateApply: (plans)=>{// Verify the resource was created successfully}});- Complete Lifecycle Testing: Test all plugin operations (initialize, validate, plan, apply, import, destroy)
- IPC Communication: Spawns plugins as child processes and communicates via IPC, just like the Codify CLI
- OS Filtering: Automatically filters and runs tests based on OS compatibility
- Interactive Command Execution: Execute commands in PTY for realistic testing
- Security Testing: Validates that plugins properly request elevated permissions
- Modify Testing: Test resource modification scenarios
The main API for testing plugins.
Runs a complete plugin lifecycle test including validate, plan, apply, import, modify (optional), and destroy.
Parameters:
pluginPath(string): Absolute path to the plugin entry pointconfigs(ResourceConfig[]): Array of resource configurations to testoptions(object, optional):skipUninstall(boolean): Skip the destroy phaseskipImport(boolean): Skip the import phasevalidatePlan(function): Callback to validate plan resultsvalidateApply(function): Callback to validate apply resultsvalidateDestroy(function): Callback to validate destroy resultsvalidateImport(function): Callback to validate import resultstestModify(object): Configuration for testing modificationsmodifiedConfigs(ResourceConfig[]): Modified configurationsvalidateModify(function): Callback to validate modify results
Example:
awaitPluginTester.fullTest('/path/to/plugin',configs,{validatePlan: (plans)=>{expect(plans).toHaveLength(1);expect(plans[0].operation).toBe('create');},testModify: {modifiedConfigs: [{type: 'my-resource',newParam: 'updated-value'}],validateModify: (plans)=>{expect(plans[0].operation).toBe('modify');}}});Tests only the installation flow (validate → plan → apply).
Parameters:
pluginPath(string): Absolute path to the plugin entry pointconfigs(ResourceConfig[]): Array of resource configurations to install
Example:
awaitPluginTester.install('/path/to/plugin',[{type: 'homebrew',os: [ResourceOs.MACOS]}]);Tests the destroy operation for resources.
Parameters:
pluginPath(string): Absolute path to the plugin entry pointconfigs(ResourceConfig[]): Array of resource configurations to destroyoptions(object, optional):validateDestroy(function): Callback to validate destroy results
Example:
awaitPluginTester.uninstall('/path/to/plugin',configs,{validateDestroy: (plans)=>{expect(plans[0].operation).toBe('destroy');}});Low-level API for managing individual plugin processes.
Example:
import{PluginProcess}from'@codifycli/plugin-test';constplugin=newPluginProcess('/path/to/plugin');try{// Initialize the pluginconstinitResult=awaitplugin.initialize();// Validate configsconstvalidateResult=awaitplugin.validate({configs: [{core: {type: 'my-resource'},parameters: {}}]});// Create a planconstplan=awaitplugin.plan({core: {type: 'my-resource'},desired: {param: 'value'},isStateful: false,state: undefined});// Apply the planawaitplugin.apply({planId: plan.planId});}finally{plugin.kill();}Utility functions for common testing scenarios.
Sends an IPC message to a plugin process and waits for the response.
TestUtils.getShell(): Returns the current shell ('bash' or 'zsh')TestUtils.getPrimaryShellRc(): Returns the path to the primary shell RC fileTestUtils.getSourceCommand(): Returns the source command for the shell RC fileTestUtils.getShellCommand(command): Returns a command that sources the shell RC file firstTestUtils.getInteractiveCommand(command): Returns an interactive shell command
TestUtils.isMacOS(): Returns true if running on macOSTestUtils.isLinux(): Returns true if running on Linux
TestUtils.ensureHomebrewInstalledOnMacOs(pluginPath): Ensures Homebrew is installedTestUtils.ensureXcodeInstalledOnMacOs(pluginPath): Ensures Xcode tools are installed
Execute a command interactively in a PTY (for testing).
Parameters:
command(string): Command to executeoptions(object, optional):cwd(string): Working directoryenv(object): Environment variablesinteractive(boolean): Run in interactive mode (default: true)requiresRoot(boolean): Run with sudostdin(boolean): Enable stdin passthroughthrows(boolean): Throw on non-zero exit code
Example:
import{testSpawn}from'@codifycli/plugin-test';constresult=awaittestSpawn('which brew');if(result.status===SpawnStatus.SUCCESS){console.log('Homebrew is installed at:',result.data);}Tests automatically filter by OS using the os field:
constconfigs=[{type: 'homebrew',os: [ResourceOs.MACOS]},// Only runs on macOS{type: 'apt',os: [ResourceOs.LINUX]},// Only runs on Linux{type: 'git'}// Runs on all platforms];awaitPluginTester.fullTest('/path/to/plugin',configs);Test that your plugin properly handles resource modifications:
awaitPluginTester.fullTest('/path/to/plugin',[{type: 'my-resource',version: '1.0.0'}],{testModify: {modifiedConfigs: [{type: 'my-resource',version: '2.0.0'}],validateModify: (plans)=>{expect(plans[0].operation).toBe('modify');expect(plans[0].changes).toContain('version');}}});Test multiple resources in a single test run:
constconfigs=[{type: 'homebrew',name: 'homebrew-main'},{type: 'homebrew-package',name: 'wget',formula: 'wget'},{type: 'homebrew-package',name: 'curl',formula: 'curl'}];awaitPluginTester.fullTest('/path/to/plugin',configs,{validateApply: (plans)=>{expect(plans).toHaveLength(3);}});Ensure prerequisites are installed before running tests:
import{TestUtils,PluginTester}from'@codifycli/plugin-test';// Ensure Homebrew is installed before testing Homebrew packagesawaitTestUtils.ensureHomebrewInstalledOnMacOs('/path/to/core-plugin');awaitPluginTester.fullTest('/path/to/plugin',[{type: 'homebrew-package',formula: 'wget'}]);DEBUG: Enable plugin debug mode with breakpoint at--inspect-brk=9221VITE_CODIFY_TEST_JWT: JWT token for tests that require Codify credentials
npm testnpx vitest src/test-utils.test.tsnpm run prepublishOnlyThe testing framework spawns your plugin as a child process (using Node's fork()) and communicates with it via IPC messages, exactly how the Codify CLI does. This ensures your tests accurately reflect real-world plugin behavior.
When your plugin requests elevated permissions or needs to execute commands, the framework intercepts these requests and handles them automatically, allowing you to test plugin behavior in a controlled environment.
- Node.js >= 18.0.0
- Works on macOS, Linux, and Windows
ISC