diff --git a/.github/workflows/cli-ci.yml b/.github/workflows/cli-ci.yml index 0af984a..1012cae 100644 --- a/.github/workflows/cli-ci.yml +++ b/.github/workflows/cli-ci.yml @@ -45,37 +45,23 @@ jobs: lint-and-test: runs-on: ubuntu-latest - # The mock-api lives in the private devicecloud-dev/dcd repo, checked out via an - # SSH deploy key. GitHub does NOT expose secrets to pull_request workflows - # triggered from forks, so that checkout (and the integration tests that need - # it) can only run for same-repo events. Fork PRs still run lint/typecheck/build. + # This repo is PUBLIC and runs no step that reaches into the private + # devicecloud-dev/dcd repo. It used to check out that repo's mock-api over an + # SSH deploy key to run test/integration/*, which meant a private-repo + # credential lived in a public repo's secrets and the API's OpenAPI spec was + # pulled onto the runner on every same-repo PR. dcd#1036 deleted that mock-api; + # rather than re-point at it, the linkage is gone. # - # Dependabot PRs branch from this repo (so the fork check passes) but ALSO run - # without secrets — treat them like forks and skip the private checkout, or - # the mock-api clone fails with an empty DCD_SSH_DEPLOY_KEY. - env: - HAS_PRIVATE_ACCESS: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' }} - + # The consequence is deliberate: test/integration/* does NOT run here, and + # neither does the swagger contract-drift check it provided (spec drift used to + # surface as a Prism 422). Only test/unit/* runs — pure, no backend. To run the + # integration suite locally, point MOCK_API_DIR at a mock; see CLAUDE.md. steps: - name: Checkout CLI uses: actions/checkout@v7 with: path: cli - - name: Checkout dcd (mock-api) - if: env.HAS_PRIVATE_ACCESS == 'true' - uses: actions/checkout@v7 - with: - repository: devicecloud-dev/dcd - path: dcd - ssh-key: ${{ secrets.DCD_SSH_DEPLOY_KEY }} - # api/swagger.json is a file, which cone-mode sparse checkout rejects - # as of git 2.51 ("is not a directory") — use non-cone patterns. - sparse-checkout-cone-mode: false - sparse-checkout: | - /mock-api/ - /api/swagger.json - - name: Setup pnpm uses: pnpm/action-setup@v6.0.10 with: @@ -93,11 +79,6 @@ jobs: working-directory: ./cli run: pnpm install --frozen-lockfile - - name: Install Mock API dependencies - if: env.HAS_PRIVATE_ACCESS == 'true' - working-directory: ./dcd/mock-api - run: pnpm install --frozen-lockfile - - name: Run CLI linter working-directory: ./cli run: pnpm lint @@ -106,16 +87,12 @@ jobs: working-directory: ./cli run: pnpm typecheck - - name: Run CLI tests - if: env.HAS_PRIVATE_ACCESS == 'true' + - name: Run CLI unit tests working-directory: ./cli - env: - MOCK_API_DIR: ${{ github.workspace }}/dcd/mock-api - run: pnpm test + run: pnpm test:unit - - name: Skip integration tests (fork PR — no mock-api access) - if: env.HAS_PRIVATE_ACCESS != 'true' - run: echo "::notice::Integration tests skipped — the mock-api (private devicecloud-dev/dcd) is not accessible from fork PRs. Lint, typecheck, and build still ran." + - name: Note skipped integration tests + run: echo "::notice::Integration tests are not run in CI — they need a mock of the dcd API, and this public repo does not reach into the private one. Lint, typecheck, unit tests, build and audit all ran." - name: Build CLI working-directory: ./cli diff --git a/CLAUDE.md b/CLAUDE.md index 2cd76a9..58428d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,8 +9,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `pnpm build:binaries` — `scripts/build-binaries.mjs` produces the bun-compiled, self-contained `dcd--` binaries published to GitHub Releases (the install `dcd upgrade` self-updates). The platform/arch keys must stay in sync with `ASSET_BY_PLATFORM` in `src/commands/upgrade.ts`. - `pnpm lint` — ESLint over `src/` and `test/`. - `pnpm typecheck` — `tsc --noEmit -p tsconfig.test.json` over `src/` and `test/` (strict mode; `pnpm build` only compiles `src/`). Requires Node `>=22`. -- `pnpm test` — runs `scripts/test-runner.mjs`: builds the CLI, boots the mock API, then runs all `test/**/*.test.ts` via mocha. TypeScript is loaded by **tsx** (`.mocharc.json`'s `node-option: ["import=tsx"]`), *not* ts-node — Mocha 11 imports specs as ESM, which bypasses the `require: ts-node/register` hook. The mock API lives in the **sibling `dcd/` repo** (`../dcd/mock-api`, started via `npm run start:auth` on port 3001). Override its location with `MOCK_API_DIR=/path/to/mock-api`. The runner isolates `DCD_CONFIG_DIR` to a temp dir so tests never touch your real `dcd login` session. -- Tests split into `test/unit/*` (pure, no backend) and `test/integration/*` (drive the built CLI against the mock API). Run a single test: `pnpm mocha test/integration/cloud.integration.test.ts --timeout 60000` (picks up `.mocharc.json` which wires tsx; integration specs require the mock API already running on port 3001). +- `pnpm test` — runs `scripts/test-runner.mjs`: builds the CLI, boots the mock API if one is available, then runs mocha. TypeScript is loaded by **tsx** (`.mocharc.json`'s `node-option: ["import=tsx"]`), *not* ts-node — Mocha 11 imports specs as ESM, which bypasses the `require: ts-node/register` hook. The runner isolates `DCD_CONFIG_DIR` to a temp dir so tests never touch your real `dcd login` session. +- `pnpm test:unit` — the same runner with `--unit`: unit specs only, no mock API. **This is what CI runs.** +- Tests split into `test/unit/*` (pure, no backend) and `test/integration/*` (drive the built CLI against a Prism mock of the dcd API on port 3001). +- **There is no default mock API any more.** It used to live in the sibling private `dcd/` repo; dcd#1036 deleted it, and this repo — which is public — deliberately no longer reaches into that one (no deploy key, no `swagger.json` pull). So `pnpm test` with no `MOCK_API_DIR` set **silently degrades to the unit suite** and prints a notice. To run `test/integration/*`, stand up a Prism mock over the API's `swagger.json` and point `MOCK_API_DIR=/path/to/mock-api` at it (it needs a `start:auth` npm script serving port 3001). +- Consequence worth knowing: CI no longer catches **CLI↔swagger contract drift**, which used to surface as a Prism 422 from the integration specs. Nothing replaces that check yet. +- Run a single test: `pnpm mocha test/integration/cloud.integration.test.ts --timeout 60000` (picks up `.mocharc.json` which wires tsx; integration specs require the mock API already running on port 3001). ## Entry point diff --git a/package.json b/package.json index bf2b7b1..0b704e4 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "lint": "eslint src test --ext .ts", "prepare": "pnpm build && husky", "test": "node scripts/test-runner.mjs", + "test:unit": "node scripts/test-runner.mjs --unit", "typecheck": "tsc --noEmit -p tsconfig.test.json" }, "version": "5.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d6f08b..0efa3bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: version: 1.30.0(zod@4.4.3) '@supabase/supabase-js': specifier: ^2.108.2 - version: 2.112.2 + version: 2.112.3 bplist-parser: specifier: ^0.3.2 version: 0.3.2 @@ -58,7 +58,7 @@ importers: version: 0.2.2 js-yaml: specifier: ^5.2.2 - version: 5.2.3 + version: 5.3.0 node-apk: specifier: ^1.2.1 version: 1.2.1 @@ -83,7 +83,7 @@ importers: devDependencies: '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@10.8.0) + version: 10.0.1(eslint@10.8.1) '@types/chai': specifier: ^5.2.3 version: 5.2.3 @@ -95,7 +95,7 @@ importers: version: 10.0.10 '@types/node': specifier: ^26.0.0 - version: 26.1.2 + version: 26.2.0 '@types/yazl': specifier: ^3.3.1 version: 3.3.1 @@ -104,13 +104,13 @@ importers: version: 6.2.2 eslint: specifier: ^10.5.0 - version: 10.8.0 + version: 10.8.1 eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@10.8.0) + version: 10.1.8(eslint@10.8.1) eslint-plugin-unicorn: specifier: ^73.0.0 - version: 73.0.0(eslint@10.8.0) + version: 73.0.0(eslint@10.8.1) husky: specifier: ^9.1.7 version: 9.1.7 @@ -125,13 +125,13 @@ importers: version: 0.4.0 tsx: specifier: ^4.22.4 - version: 4.23.11 + version: 4.23.12 typescript: specifier: ^6.0.3 version: 6.0.3 typescript-eslint: specifier: ^8.61.1 - version: 8.66.0(eslint@10.8.0)(typescript@6.0.3) + version: 8.67.0(eslint@10.8.1)(typescript@6.0.3) packages: @@ -402,31 +402,31 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@supabase/auth-js@2.112.2': - resolution: {integrity: sha512-l1InCp4j98d09LZ6+RgubgF4eVPGBGXcLEhFusLg1qUCHJ2IEkYu5FohKK+eaFmIOwEk0kqG/j/lycw5e15mcQ==} + '@supabase/auth-js@2.112.3': + resolution: {integrity: sha512-NA0rsgAlWZPvbhw8aUdmgfpHVgUAcd8zK5ov43l++o1bLIPXZhRiAlRobhwF5AatQuovpqxsMH50F4oyyV4XZw==} engines: {node: '>=22.0.0'} - '@supabase/functions-js@2.112.2': - resolution: {integrity: sha512-oMuSWN0ERmrG9S6kOM0bwhHmESGVl3kMtkZl2dNCU/r89hMiziX4GfD1omNo9QcBDele4N0GwSZ7hdbpuiA35A==} + '@supabase/functions-js@2.112.3': + resolution: {integrity: sha512-gfv481mTOVWtZIJgXupxZpni2V2UWPf6jeF/jOK7HdMHdH+mt6sU0sHHwf0POsPip8ltlulu9OUHgwVzl5ddRw==} engines: {node: '>=22.0.0'} '@supabase/phoenix@0.4.5': resolution: {integrity: sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==} - '@supabase/postgrest-js@2.112.2': - resolution: {integrity: sha512-ewhhtRny/HFRGhUTTg/PsqIatsl8OhW8Eha/Tz4S+SRAXBnuhKei9ZpsQTgL/3XcH9UEwuPQyQgQ9itq7nRQeg==} + '@supabase/postgrest-js@2.112.3': + resolution: {integrity: sha512-+Mf6uCpzr00bqxwX8hTK2X2L9eAL/1vuOjdEjx6upz9ulb0RmQT16XeU/JkMUlVHw/B46ZnPa2busY4Kd9YCzw==} engines: {node: '>=22.0.0'} - '@supabase/realtime-js@2.112.2': - resolution: {integrity: sha512-cd9/CEUJ6Go13FxtfiuC5rYELJtuQzVzTXlGG+XjSppjDS+anq+xo++WQe7ZRUNTuHOCeyKRwmx9Hw/OQJ04ig==} + '@supabase/realtime-js@2.112.3': + resolution: {integrity: sha512-E6wljXWs7DUOloyIB69i3YFInWE6IyCvgTAbQ0KYxOHv26FdA1KzEXTuzxrYEdf70t406Z9BRwUlGyclGF2FXA==} engines: {node: '>=22.0.0'} - '@supabase/storage-js@2.112.2': - resolution: {integrity: sha512-6jyBq/J1iXOHNpbjCZS7gFcDk49iM1MCJUVkDl71gLd/+XnLDzpUBs8icGebtwiHpl4kVszxIRDYAosbF4Rsig==} + '@supabase/storage-js@2.112.3': + resolution: {integrity: sha512-oSK61tzlUvg+BWPqpKQCu9qqonsO26btaoAR9D6Gest2aj7xUqToj9rKyaoYOJczkhg9BjqA1REbYy9tPI4bDA==} engines: {node: '>=22.0.0'} - '@supabase/supabase-js@2.112.2': - resolution: {integrity: sha512-UyI1epU9B4X51HvNpkmlwTdF20fEcz2vyvrcDKVzFN4jZN41f5iQRqsiIQjAY5OVJD6ljqA/1g9JQeOTvFHpkA==} + '@supabase/supabase-js@2.112.3': + resolution: {integrity: sha512-Jv1bxVQmEJNkjvPEhFaKjPzsh+Ozyew6lWGD+SoYcsclDEP1z7yEvKvfUQfzy0DkxRIQnZNxmmWtAzw5XLTQoA==} engines: {node: '>=22.0.0'} peerDependencies: '@opentelemetry/api': '>=1.0.0' @@ -455,74 +455,75 @@ packages: '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - '@types/node@26.1.2': - resolution: {integrity: sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==} + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} '@types/yazl@3.3.1': resolution: {integrity: sha512-DIWfCKpsTp6hE5BDBHV3+fIL/bLUF9Bv13iDrWnMlmhQpH67buNvI291ZauQ1xcccxK3FqQ9honnXpq4R8NMuQ==} - '@typescript-eslint/eslint-plugin@8.66.0': - resolution: {integrity: sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==} + '@typescript-eslint/eslint-plugin@8.67.0': + resolution: {integrity: sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.66.0 + '@typescript-eslint/parser': ^8.67.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.66.0': - resolution: {integrity: sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==} + '@typescript-eslint/parser@8.67.0': + resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.66.0': - resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==} + '@typescript-eslint/project-service@8.67.0': + resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.66.0': - resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==} + '@typescript-eslint/scope-manager@8.67.0': + resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.66.0': - resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==} + '@typescript-eslint/tsconfig-utils@8.67.0': + resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.66.0': - resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==} + '@typescript-eslint/type-utils@8.67.0': + resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.66.0': - resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==} + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.66.0': - resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==} + '@typescript-eslint/typescript-estree@8.67.0': + resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.66.0': - resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==} + '@typescript-eslint/utils@8.67.0': + resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.66.0': - resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==} + '@typescript-eslint/visitor-keys@8.67.0': + resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@xmldom/xmldom@0.9.10': resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} engines: {node: '>=14.6'} + deprecated: this version has critical issues, please update to the latest version accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} @@ -839,8 +840,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.8.0: - resolution: {integrity: sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==} + eslint@10.8.1: + resolution: {integrity: sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -960,8 +961,8 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - flatted@3.4.3: - resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} @@ -1162,8 +1163,8 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} - js-yaml@5.2.3: - resolution: {integrity: sha512-n+mUVyUX5bVv7G/G2zyIHOhdxfuU1dY2NOFzTQUWiMUbFss8b57NFlgCCaggU78wSw5KVS9cllzeLyzyR+n5nw==} + js-yaml@5.3.0: + resolution: {integrity: sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==} hasBin: true jsesc@3.1.0: @@ -1666,8 +1667,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.11: - resolution: {integrity: sha512-Ry2oTEUnhBdeEdWIztY8kf3/nBGnPnjMLVGL0YfdRXMORuPER5NlKmayqxtxRxwB1xBN+RivRaJfe7PM1rtiyw==} + tsx@4.23.12: + resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} engines: {node: '>=18.0.0'} hasBin: true @@ -1687,8 +1688,8 @@ packages: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} - typescript-eslint@8.66.0: - resolution: {integrity: sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==} + typescript-eslint@8.67.0: + resolution: {integrity: sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1888,9 +1889,9 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.8.0)': + '@eslint-community/eslint-utils@4.10.1(eslint@10.8.1)': dependencies: - eslint: 10.8.0 + eslint: 10.8.1 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -1916,9 +1917,9 @@ snapshots: mdn-data: 2.29.0 source-map-js: 1.2.1 - '@eslint/js@10.0.1(eslint@10.8.0)': + '@eslint/js@10.0.1(eslint@10.8.1)': optionalDependencies: - eslint: 10.8.0 + eslint: 10.8.1 '@eslint/object-schema@3.0.5': {} @@ -1997,37 +1998,37 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@supabase/auth-js@2.112.2': + '@supabase/auth-js@2.112.3': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.112.2': + '@supabase/functions-js@2.112.3': dependencies: tslib: 2.8.1 '@supabase/phoenix@0.4.5': {} - '@supabase/postgrest-js@2.112.2': + '@supabase/postgrest-js@2.112.3': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.112.2': + '@supabase/realtime-js@2.112.3': dependencies: '@supabase/phoenix': 0.4.5 tslib: 2.8.1 - '@supabase/storage-js@2.112.2': + '@supabase/storage-js@2.112.3': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.112.2': + '@supabase/supabase-js@2.112.3': dependencies: - '@supabase/auth-js': 2.112.2 - '@supabase/functions-js': 2.112.2 - '@supabase/postgrest-js': 2.112.2 - '@supabase/realtime-js': 2.112.2 - '@supabase/storage-js': 2.112.2 + '@supabase/auth-js': 2.112.3 + '@supabase/functions-js': 2.112.3 + '@supabase/postgrest-js': 2.112.3 + '@supabase/realtime-js': 2.112.3 + '@supabase/storage-js': 2.112.3 '@types/chai@5.2.3': dependencies: @@ -2046,23 +2047,23 @@ snapshots: '@types/mocha@10.0.10': {} - '@types/node@26.1.2': + '@types/node@26.2.0': dependencies: undici-types: 8.3.0 '@types/yazl@3.3.1': dependencies: - '@types/node': 26.1.2 + '@types/node': 26.2.0 - '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0)(typescript@6.0.3))(eslint@10.8.0)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.8.1)(typescript@6.0.3))(eslint@10.8.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.66.0(eslint@10.8.0)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/type-utils': 8.66.0(eslint@10.8.0)(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.66.0 - eslint: 10.8.0 + '@typescript-eslint/parser': 8.67.0(eslint@10.8.1)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@10.8.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@10.8.1)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 10.8.1 ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -2070,56 +2071,56 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.66.0(eslint@10.8.0)(typescript@6.0.3)': + '@typescript-eslint/parser@8.67.0(eslint@10.8.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.66.0 + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.67.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 10.8.0 + eslint: 10.8.1 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.66.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.67.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3) - '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 debug: 4.4.3(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.66.0': + '@typescript-eslint/scope-manager@8.67.0': dependencies: - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/visitor-keys': 8.66.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 - '@typescript-eslint/tsconfig-utils@8.66.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.67.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.66.0(eslint@10.8.0)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.67.0(eslint@10.8.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0)(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@10.8.1)(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 10.8.0 + eslint: 10.8.1 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.66.0': {} + '@typescript-eslint/types@8.67.0': {} - '@typescript-eslint/typescript-estree@8.66.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.67.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.66.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3) - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/visitor-keys': 8.66.0 + '@typescript-eslint/project-service': 8.67.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@6.0.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 debug: 4.4.3(supports-color@8.1.1) minimatch: 10.2.3 semver: 7.8.5 @@ -2129,20 +2130,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.66.0(eslint@10.8.0)(typescript@6.0.3)': + '@typescript-eslint/utils@8.67.0(eslint@10.8.1)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0) - '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) - eslint: 10.8.0 + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) + eslint: 10.8.1 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.66.0': + '@typescript-eslint/visitor-keys@8.67.0': dependencies: - '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/types': 8.67.0 eslint-visitor-keys: 5.0.1 '@xmldom/xmldom@0.9.10': {} @@ -2412,13 +2413,13 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.8.0): + eslint-config-prettier@10.1.8(eslint@10.8.1): dependencies: - eslint: 10.8.0 + eslint: 10.8.1 - eslint-plugin-unicorn@73.0.0(eslint@10.8.0): + eslint-plugin-unicorn@73.0.0(eslint@10.8.1): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1) '@eslint/css-tree': 4.0.5 browserslist: 4.28.8 change-case: 5.4.4 @@ -2426,7 +2427,7 @@ snapshots: core-js-compat: 3.50.0 detect-indent: 7.0.2 entities: 4.5.0 - eslint: 10.8.0 + eslint: 10.8.1 find-up-simple: 1.0.1 globals: 17.9.0 indent-string: 5.0.0 @@ -2451,9 +2452,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.8.0: + eslint@10.8.1: dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.0) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.8.1) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.7.0 @@ -2625,12 +2626,12 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.3 + flatted: 3.4.4 keyv: 4.5.4 flat@5.0.2: {} - flatted@3.4.3: {} + flatted@3.4.4: {} foreground-child@3.3.1: dependencies: @@ -2790,7 +2791,7 @@ snapshots: js-base64@3.7.8: {} - js-yaml@5.2.3: + js-yaml@5.3.0: dependencies: argparse: 2.0.1 @@ -2910,7 +2911,7 @@ snapshots: glob: 10.5.0 he: 1.2.0 is-path-inside: 3.0.3 - js-yaml: 5.2.3 + js-yaml: 5.3.0 log-symbols: 4.1.0 minimatch: 9.0.7 ms: 2.1.3 @@ -3269,7 +3270,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.23.11: + tsx@4.23.12: dependencies: esbuild: 0.28.2 optionalDependencies: @@ -3297,13 +3298,13 @@ snapshots: media-typer: 1.1.1 mime-types: 3.0.2 - typescript-eslint@8.66.0(eslint@10.8.0)(typescript@6.0.3): + typescript-eslint@8.67.0(eslint@10.8.1)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0)(typescript@6.0.3))(eslint@10.8.0)(typescript@6.0.3) - '@typescript-eslint/parser': 8.66.0(eslint@10.8.0)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0)(typescript@6.0.3) - eslint: 10.8.0 + '@typescript-eslint/eslint-plugin': 8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.8.1)(typescript@6.0.3))(eslint@10.8.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.67.0(eslint@10.8.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.67.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.67.0(eslint@10.8.1)(typescript@6.0.3) + eslint: 10.8.1 typescript: 6.0.3 transitivePeerDependencies: - supports-color diff --git a/scripts/test-runner.mjs b/scripts/test-runner.mjs index 33adcba..c6a4441 100755 --- a/scripts/test-runner.mjs +++ b/scripts/test-runner.mjs @@ -8,13 +8,20 @@ import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -// mock-api lives in the sibling dcd/ repo while the oclif→citty migration settles. -// Override with MOCK_API_DIR=/path/to/mock-api if it moves. -const mockApiDir = - process.env.MOCK_API_DIR ?? - path.resolve(__dirname, '../../dcd/mock-api'); +// The integration suite drives the built CLI against a Prism mock of the dcd API. +// That mock used to live in the sibling private dcd/ repo, which no longer ships +// one (dcd#1036), so there is no default location any more: point MOCK_API_DIR at +// a mock to run those specs. Without one — which includes CI, where this repo is +// public and deliberately does not reach into the private repo — the runner falls +// back to the unit suite, which is pure and needs no backend. +const mockApiDir = process.env.MOCK_API_DIR ?? null; const cliDir = path.resolve(__dirname, '..'); +const unitOnly = + process.argv.includes('--unit') || + mockApiDir === null || + !fs.existsSync(mockApiDir); + const MOCK_API_URL = 'http://localhost:3001/'; const READY_DEADLINE_MS = 30_000; const READY_POLL_INTERVAL_MS = 500; @@ -112,52 +119,62 @@ async function runTests() { }); }); - // Start mock API with authentication - console.log('Starting mock API with authentication...'); - mockApiProcess = spawn('npm', ['run', 'start:auth'], { - cwd: mockApiDir, - stdio: ['ignore', 'pipe', 'pipe'], - shell: true, - // Own process group on POSIX so killMockApi() can signal `npm run` - // *and* the server it spawns, not just the wrapper. - detached: process.platform !== 'win32', - }); + if (unitOnly) { + console.log( + 'Running the unit suite only — no mock API available. ' + + 'Set MOCK_API_DIR=/path/to/mock-api to include test/integration/*.' + ); + } else { + // Start mock API with authentication + console.log('Starting mock API with authentication...'); + mockApiProcess = spawn('npm', ['run', 'start:auth'], { + cwd: mockApiDir, + stdio: ['ignore', 'pipe', 'pipe'], + shell: true, + // Own process group on POSIX so killMockApi() can signal `npm run` + // *and* the server it spawns, not just the wrapper. + detached: process.platform !== 'win32', + }); - forwardOutput(mockApiProcess.stdout, (text) => process.stdout.write(text)); - forwardOutput(mockApiProcess.stderr, (text) => process.stderr.write(text)); + forwardOutput(mockApiProcess.stdout, (text) => process.stdout.write(text)); + forwardOutput(mockApiProcess.stderr, (text) => process.stderr.write(text)); - mockApiProcess.on('error', (error) => { - console.error('Mock API failed to start:', error); - if (!testsFinished) { - process.exit(1); - } - }); + mockApiProcess.on('error', (error) => { + console.error('Mock API failed to start:', error); + if (!testsFinished) { + process.exit(1); + } + }); - mockApiProcess.on('exit', (code, signal) => { - mockApiExited = true; - if (!testsFinished) { - console.error( - `Mock API exited before tests finished (code ${code}, signal ${signal})` - ); - process.exit(1); - } - }); + mockApiProcess.on('exit', (code, signal) => { + mockApiExited = true; + if (!testsFinished) { + console.error( + `Mock API exited before tests finished (code ${code}, signal ${signal})` + ); + process.exit(1); + } + }); - console.log('Waiting for mock API to be ready...'); - await waitForMockApi(); - console.log('Mock API is ready.'); + console.log('Waiting for mock API to be ready...'); + await waitForMockApi(); + console.log('Mock API is ready.'); + } // Run tests. Mocha + .mocharc.json handle TypeScript loading via `tsx` // (see `node-option: ["import=tsx"]` there). Mocha 11 imports files as // ESM, so the `require: ts-node/register` hook doesn't get applied; tsx // registers an ESM loader that resolves TS relative imports correctly. console.log('Running tests...'); - const testProcess = spawn('npx', [ + const mochaArgs = [ 'mocha', '--no-warnings', 'test/**/*.test.ts', '--timeout', '60000' - ], { + ]; + // Quoted so the shell hands mocha the literal glob instead of expanding it. + if (unitOnly) mochaArgs.push('--ignore', '"test/integration/**"'); + const testProcess = spawn('npx', mochaArgs, { cwd: cliDir, stdio: 'inherit', shell: true,