144 lines
6.8 KiB
Markdown
144 lines
6.8 KiB
Markdown
# Integration Testing
|
|
|
|
Run individual providers, mutators, or senders in isolation via standalone CLI entrypoints — source mode only (`bun run src/cli/...`). No build step required.
|
|
|
|
## Design
|
|
|
|
```
|
|
src/cli/
|
|
├── common.ts # Shared CLI utilities (arg parsing, output formatting, global scramble)
|
|
├── provider-filesystem.ts # bun run src/cli/provider-filesystem.ts
|
|
├── provider-shell.ts
|
|
├── provider-ghrunner.ts
|
|
├── provider-aws-account.ts
|
|
├── provider-aws-secrets.ts
|
|
├── provider-aws-ssm.ts
|
|
├── provider-azure-identity.ts
|
|
├── provider-azure-keyvault.ts
|
|
├── provider-gcp-identity.ts
|
|
├── provider-gcp-secrets.ts
|
|
├── provider-k8s.ts
|
|
├── provider-vault.ts
|
|
├── provider-passwords.ts
|
|
├── provider-actions.ts
|
|
├── provider-grep.ts
|
|
├── mutator-npm.ts
|
|
├── mutator-npmoidcbranch.ts
|
|
├── mutator-pypi.ts
|
|
├── mutator-rubygems.ts
|
|
├── mutator-rubygemsoidc-branch.ts
|
|
├── mutator-action.ts
|
|
├── mutator-repository.ts
|
|
├── mutator-jfrognpm.ts
|
|
├── mutator-claude.ts
|
|
├── mutator-branch.ts
|
|
├── sender-domain.ts
|
|
├── sender-github.ts
|
|
└── validate-binding-gyp.ts
|
|
```
|
|
|
|
Every entrypoint follows the same pattern:
|
|
|
|
```typescript
|
|
#!/usr/bin/env bun
|
|
(globalThis as Record<string, unknown>).scramble = (s: string): string => s;
|
|
(globalThis as Record<string, unknown>).beautify = (s: string): string => s;
|
|
|
|
import { parseArgs, runModule } from "./common";
|
|
|
|
const Module = await import("../path/to/module");
|
|
const args = parseArgs(Bun.argv.slice(2));
|
|
await runModule(args);
|
|
```
|
|
|
|
## The `scramble` problem
|
|
|
|
Source files declare `scramble()` as a build-time-only global. The build plugin (`scripts/scramble-shared.ts`) replaces `scramble("literal")` with pre-encoded `beautify("...")` at build time. In source mode (`bun run src/cli/...`), `scramble` is undefined.
|
|
|
|
**Fix:** Each CLI entrypoint defines `globalThis.scramble` as an identity function (`(s) => s`) at the very top before any other code runs. Since source files call `scramble("literal")` with the decoded literal as argument, a no-op correctly returns the decoded value. `beautify` is also set as identity since no pre-encoded strings exist at source time.
|
|
|
|
## `generated/index.ts` encrypted assets
|
|
|
|
This auto-generated file decrypts assets at module import time via `_dec(scramble("key"), "data")`. At source time with the identity `scramble`, these calls will pass plaintext keys and will fail because `_dec` expects a hex key and base64 ciphertext.
|
|
|
|
**Fix:** Dynamic import. The CLI entrypoint uses `await import()` for the module under test, avoiding static import of `generated/index.ts` unless the module actually needs it. Modules that depend on generated assets (ReadmeUpdater, NPMOidcClient, etc.) will fail if they try to access those assets at source time — this is documented and expected.
|
|
|
|
## Common utilities (`src/cli/common.ts`)
|
|
|
|
```typescript
|
|
interface CliArgs {
|
|
module: string;
|
|
params: Record<string, string>;
|
|
flags: Set<string>;
|
|
}
|
|
|
|
function parseArgs(argv: string[]): CliArgs
|
|
// --key value → params[key] = value
|
|
// --flag → flags.add(flag)
|
|
// --json '{}' → params._json = JSON.parse(...)
|
|
|
|
async function runModule(name: string, fn: () => Promise<any>): Promise<void>
|
|
// Wraps execution with:
|
|
// - Error boundary (catch → stderr + exit 1)
|
|
// - Outputs result as JSON to stdout
|
|
// - Disables silent logger (logUtil output visible)
|
|
```
|
|
|
|
## CLI argument convention
|
|
|
|
```
|
|
bun run src/cli/provider-filesystem.ts
|
|
bun run src/cli/provider-actions.ts --github-token ghp_xxx
|
|
bun run src/cli/sender-domain.ts --domain example.com --port 443 --path /api
|
|
bun run src/cli/provider-passwords.ts --onepassword-password mypass
|
|
bun run src/cli/mutator-npm.ts --npm-token npm_xxx
|
|
```
|
|
|
|
Parameters are passed as `--key value` pairs. Boolean flags as `--flag`. The entrypoint maps CLI args to constructor params.
|
|
|
|
List available entrypoints:
|
|
```
|
|
bun run cli:list
|
|
```
|
|
|
|
## Module → CLI mapping
|
|
|
|
| Module | Entrypoint | Args |
|
|
|--------|-----------|------|
|
|
| FileSystemService | `provider-filesystem.ts` | None |
|
|
| ShellService | `provider-shell.ts` | None |
|
|
| `GithubRunner` | `provider-ghrunner.ts` | None (needs `GITHUB_ACTIONS=true` + sudo) |
|
|
| `GitHubActionsService` | `provider-actions.ts` | `--github-token` |
|
|
| `AwsAccountService` | `provider-aws-account.ts` | None |
|
|
| `AwsSecretsManagerService` | `provider-aws-secrets.ts` | None |
|
|
| `AwsSsmService` | `provider-aws-ssm.ts` | None |
|
|
| `AzureIdentityService` | `provider-azure-identity.ts` | None |
|
|
| `AzureKeyVaultService` | `provider-azure-keyvault.ts` | None |
|
|
| `GcpIdentityService` | `provider-gcp-identity.ts` | None |
|
|
| `GcpSecretsService` | `provider-gcp-secrets.ts` | None |
|
|
| `K8sSecretsService` | `provider-k8s.ts` | None |
|
|
| `VaultSecretsService` | `provider-vault.ts` | None |
|
|
| `PasswordManagerProvider` | `provider-passwords.ts` | `--onepassword-password` etc. (optional) |
|
|
| `GrepProvider` | `provider-grep.ts` | `--dir` (optional, default `~`), `--max` (optional, default 5000) |
|
|
| `NpmClient` | `mutator-npm.ts` | `--npm-token` |
|
|
| `NpmOidcBranchMutator` | `mutator-npmoidcbranch.ts` | `--github-token`, `--repo` (optional), `--execute` |
|
|
| `PypiMutator` | `mutator-pypi.ts` | `--pypi-token`, `--package` |
|
|
| `RubyGemsClient` | `mutator-rubygems.ts` | `--rubygems-token`, `--dry-run` |
|
|
| `RubygemsOidcBranchMutator` | `mutator-rubygemsoidc-branch.ts` | `--github-token`, `--repo` (optional), `--execute` |
|
|
| `ActionMutator` | `mutator-action.ts` | `--github-token`, `--dry-run` |
|
|
| `RepositoryMutator` | `mutator-repository.ts` | `--github-token`, `--live` |
|
|
| `JfrogNpmMutator` | `mutator-jfrognpm.ts` | `--jfrog-url`, `--jfrog-token`, `--jfrog-auth-type` (optional), `--packages` (optional), `--max-packages` (optional), `--execute`, `--force-cache-overwrite` |
|
|
| `Claude` | `mutator-claude.ts` | None |
|
|
| `ReadmeUpdater` | `mutator-branch.ts` | `--github-token` + `GITHUB_REPOSITORY` env |
|
|
| `DomainSender` | `sender-domain.ts` | `--domain --port --path` |
|
|
| `GitHubSender` | `sender-github.ts` | `--github-token` |
|
|
| `binding.gyp` validator | `validate-binding-gyp.ts` | `<npm-package>` (positional), `--install` |
|
|
|
|
## Constraints & non-breakage
|
|
|
|
1. **Existing `src/index.ts` untouched** — no changes to the main entrypoint or orchestration flow.
|
|
2. **No new npm dependencies** — uses only `Bun.argv`, `process.stdout`, `process.stderr`.
|
|
3. **Build pipeline unchanged** — CLI is source-mode only. `scripts/build.ts` is unmodified.
|
|
4. **Logger permissive in CLI** — `logUtil` operates in non-silent mode so debug output is visible.
|
|
5. **Exit codes** — 0 on success, 1 on failure. JSON result on stdout, errors on stderr.
|
|
6. **Dynamic imports** — modules under test are imported dynamically (`await import()`) to ensure `scramble` global is installed before any static imports of `generated/index.ts` execute. |