Skip to content

ci: shard core test suite across parallel runner jobs - #325

Merged
khaira777 merged 3 commits into
mainfrom
perf/324-shard-core-test-suite
Aug 16, 2026
Merged

ci: shard core test suite across parallel runner jobs#325
khaira777 merged 3 commits into
mainfrom
perf/324-shard-core-test-suite

Conversation

@khaira777

Copy link
Copy Markdown
Contributor

Intent

Shard the CI core test suite (~90 serial suites) into two parallel runner jobs to cut PR merge-ready time, without removing any test coverage. Update branch protection to require the new shard checks.

What Changed

  • Added scripts/ci/run-test-shard.cjs to dynamically parse test suites from package.json and execute round-robin partitions based on SHARD_TOTAL and SHARD_INDEX.
  • Updated .github/workflows/ci.yml to run the core test suite across a two-runner matrix job (linux-tests) with payment method split prechecks on shard 0.
  • Added tests in tests/dev-tooling-scripts.test.ts covering shard script parameter validation, round-robin partitioning, fail-fast behavior, and CI workflow configuration.

Risk Assessment

✅ Low: The change cleanly shards the CI core test suites across two matrix runners using a dynamic runner script without modifying local test scripts or reducing test coverage.

Testing

Exercised scripts/ci/run-test-shard.cjs CLI error validation, round-robin distribution, fail-fast error handling on failing suites, and suite coverage invariance on the repository package.json (all 90 suites partitioned into 45 suites per shard with 100% coverage and zero overlaps). Validated .github/workflows/ci.yml linux-tests matrix shard setup and pretest execution. All automated checks passed cleanly.

Evidence: CI Core Test Suite Sharding Validation Evidence
# CI Core Test Suite Sharding Validation Evidence

## Summary
- **Total Test Suites in `npm test`**: 90 suites
- **Shard 0 Suite Count**: 45 suites
- **Shard 1 Suite Count**: 45 suites
- **Overlap**: 0 suites (completely disjoint)
- **Coverage Preserved**: 100% (45 + 45 = 90 suites, exact original order preserved)

## GitHub Actions CI Workflow Configuration (`.github/workflows/ci.yml`)
- **Job Name**: `linux-tests`
- **Runner OS**: `ubuntu-latest`
- **Matrix Strategy**: `shard: [0,1]`
- **Fail Fast**: `false`
- **Execution Command**: `SHARD_TOTAL=2 SHARD_INDEX=${{ matrix.shard }} node scripts/ci/run-test-shard.cjs`
- **Pretest Hook**: `Payment method split checks` executed conditionally on `matrix.shard == 0`
- **Required Status Checks for Branch Protection**: `linux-tests (0)` and `linux-tests (1)`

## Shard 0 Test Suites (45 Suites - Index 0, 2, 4, ...)

| # | Global Index | Suite Target | Command Definition |
|---|---|---|---|
| 1 | 0 | `test:smoke` | `node tests/run-electron-node-test.cjs tests/smoke-test.test.ts` |
| 2 | 2 | `test:kds-integration` | `node tests/run-electron-node-test.cjs tests/kds-integration.test.ts` |
| 3 | 4 | `test:kds-frontend-conflict` | `ts-node --transpile-only -P tests/tsconfig.json tests/kds-frontend-conflict.test.ts` |
| 4 | 6 | `test:cors` | `ts-node --transpile-only -P tests/tsconfig.json tests/cors-security.test.ts` |
| 5 | 8 | `test:release-config` | `ts-node --transpile-only -P tests/tsconfig.json tests/release-config.test.ts && ts-node --transpile-only -P tests/tsconfig.json tests/verify-electron-runtime-xattr.test.ts` |
| 6 | 10 | `test:first-run` | `node tests/run-electron-node-test.cjs tests/first-run-setup.test.ts && node tests/run-electron-node-test.cjs tests/setup-failure-recovery.test.ts` |
| 7 | 12 | `test:staff-authz` | `node tests/run-electron-node-test.cjs tests/staff-authz.test.ts` |
| 8 | 14 | `test:authz-phase3` | `node tests/run-electron-node-test.cjs tests/authz-matrix-phase3.test.ts` |
| 9 | 16 | `test:customer-auth` | `node tests/run-electron-node-test.cjs tests/customer-auth.test.ts` |
| 10 | 18 | `test:backup` | `ts-node --transpile-only -P tests/tsconfig.json tests/backup-restore.test.ts && node tests/run-electron-node-test.cjs tests/backup-restore-production.test.ts && node tests/run-electron-node-test.cjs tests/restore-managed-path.test.ts` |
| 11 | 20 | `test:cloud-account-status` | `node tests/run-electron-node-test.cjs tests/cloud-account-status.test.ts` |
| 12 | 22 | `test:printer-width-refresh` | `node tests/run-electron-node-test.cjs tests/printer-width-refresh.test.ts` |
| 13 | 24 | `test:translations` | `ts-node --transpile-only -P tests/tsconfig.json tests/translations.test.ts` |
| 14 | 26 | `test:currency` | `ts-node --transpile-only -P tests/tsconfig.json tests/currency.test.ts && ts-node --transpile-only -P tests/tsconfig.json tests/country-profile.test.ts` |
| 15 | 28 | `test:tax-components` | `ts-node --transpile-only -P tests/tsconfig.json tests/tax-components.test.ts` |
| 16 | 30 | `test:tax-pack-management` | `node tests/run-electron-node-test.cjs tests/tax-pack-management.test.ts` |
| 17 | 32 | `test:legacy-tax-pack-digest` | `node tests/run-electron-node-test.cjs tests/legacy-tax-pack-digest.test.ts` |
| 18 | 34 | `test:customer-phone-search` | `node tests/run-electron-node-test.cjs tests/customer-phone-search.test.ts` |
| 19 | 36 | `test:receipt-column-width` | `ts-node --transpile-only -P tests/tsconfig.json tests/receipt-column-width.test.ts` |
| 20 | 38 | `test:receipt-printing` | `ts-node --transpile-only -P tests/tsconfig.json tests/receipt-printing.test.ts` |
| 21 | 40 | `test:kitchen-addons` | `node tests/run-electron-node-test.cjs tests/kitchen-addons-parsing.test.ts` |
| 22 | 42 | `test:issue-125-addon-reads` | `node tests/run-electron-node-test.cjs tests/issue-125-addon-read-paths.test.ts` |
| 23 | 44 | `test:reports-insights` | `node tests/run-electron-node-test.cjs tests/reports-insights.test.ts` |
| 24 | 46 | `test:integration-happy` | `node tests/run-electron-node-test.cjs tests/integration-happy-path.test.ts` |
| 25 | 48 | `test:integration-payments` | `node tests/run-electron-node-test.cjs tests/integration-payments.test.ts` |
| 26 | 50 | `test:issue-214-auth` | `node tests/run-electron-node-test.cjs tests/issue-214-auth-migration.test.ts` |
| 27 | 52 | `test:integration-lifecycle` | `node tests/run-electron-node-test.cjs tests/integration-order-lifecycle.test.ts` |
| 28 | 54 | `test:integration-loyalty` | `node tests/run-electron-node-test.cjs tests/integration-loyalty.test.ts` |
| 29 | 56 | `test:loyalty-toggle` | `node tests/run-electron-node-test.cjs tests/loyalty-toggle.test.ts` |
| 30 | 58 | `test:integration-discount-settings` | `node tests/run-electron-node-test.cjs tests/integration-discount-settings.test.ts` |
| 31 | 60 | `test:issue-248-csv` | `node tests/run-electron-node-test.cjs tests/issue-248-menu-csv.test.ts && node tests/run-electron-node-test.cjs tests/menu-csv-formula-export.test.ts` |
| 32 | 62 | `test:bills-print-api` | `ts-node --transpile-only -P tests/tsconfig.json tests/bills-print-api.test.ts` |
| 33 | 64 | `test:issue-134-routing` | `node tests/run-electron-node-test.cjs tests/issue-134-station-routing.test.ts` |
| 34 | 66 | `test:issue-137-barcode` | `node tests/run-electron-node-test.cjs tests/issue-137-barcode.test.ts` |
| 35 | 68 | `test:issue-250-catalog-perf` | `node tests/run-electron-node-test.cjs tests/issue-250-catalog-perf.test.ts` |
| 36 | 70 | `test:issue-265-morocco-profile` | `node tests/run-electron-node-test.cjs tests/issue-265-morocco-profile.test.ts` |
| 37 | 72 | `test:tables-string-ids` | `node tests/run-electron-node-test.cjs tests/tables-string-ids.test.ts` |
| 38 | 74 | `test:schema-health` | `node tests/run-electron-node-test.cjs tests/schema-health.test.ts` |
| 39 | 76 | `test:migration-v56-v57` | `node tests/run-electron-node-test.cjs tests/migration-v56-to-v57.test.ts` |
| 40 | 78 | `test:google-drive` | `node tests/run-electron-node-test.cjs tests/google-drive.test.ts` |
| 41 | 80 | `test:phone-validation` | `node tests/run-electron-node-test.cjs tests/phone-validation.test.ts` |
| 42 | 82 | `test:issue-133-kds-kot-toggles` | `node tests/run-electron-node-test.cjs tests/issue-133-kds-kot-toggles.test.ts` |
| 43 | 84 | `test:whatsapp-service` | `ts-node --transpile-only -P tests/tsconfig.json tests/whatsapp-service.test.ts` |
| 44 | 86 | `test:issue-127-password-recovery` | `node tests/run-electron-node-test.cjs tests/issue-127-password-recovery.test.ts` |
| 45 | 88 | `test:windows-uninstaller` | `node tests/run-windows-uninstaller-tests.cjs` |

## Shard 1 Test Suites (45 Suites - Index 1, 3, 5, ...)

| # | Global Index | Suite Target | Command Definition |
|---|---|---|---|
| 1 | 1 | `test:server-port-collision` | `node tests/run-electron-node-test.cjs tests/server-port-collision.test.ts` |
| 2 | 3 | `test:kds-contract` | `node tests/run-electron-node-test.cjs tests/kds-contract.test.ts` |
| 3 | 5 | `test:kds-window-hardening` | `ts-node --transpile-only -P tests/tsconfig.json tests/kds-window-hardening.test.ts` |
| 4 | 7 | `test:csp-lan` | `ts-node --transpile-only -P tests/tsconfig.json tests/csp-lan-header.test.ts` |
| 5 | 9 | `test:telemetry` | `node tests/run-electron-node-test.cjs tests/telemetry-delivery.test.ts` |
| 6 | 11 | `test:security` | `node tests/run-electron-node-test.cjs tests/security-hardening.test.ts` |
| 7 | 13 | `test:orders-authz` | `node tests/run-electron-node-test.cjs tests/orders-authz.test.ts` |
| 8 | 15 | `test:auth-ui-deterministic` | `ts-node --transpile-only -P tests/tsconfig.json tests/auth-ui-deterministic.test.ts` |
| 9 | 17 | `test:customer-pagination` | `node tests/run-electron-node-test.cjs tests/customer-pagination.test.ts` |
| 10 | 19 | `test:recovery-cloud` | `node tests/run-electron-node-test.cjs tests/recovery-legacy-fk.test.ts && node tests/run-electron-node-test.cjs tests/cloud-deletion-recovery.test.ts` |
| 11 | 21 | `test:printer` | `ts-node --transpile-only -P tests/tsconfig.json tests/printer.test.ts && npm run test:printer-api` |
| 12 | 23 | `test:printer-migrations` | `node tests/run-electron-node-test.cjs tests/printer-usb-device-path-migration.test.ts` |
| 13 | 25 | `test:phone` | `ts-node --transpile-only -P tests/tsconfig.json tests/phone.test.ts` |
| 14 | 27 | `test:tax-engine` | `ts-node --transpile-only -P tests/tsconfig.json tests/tax-engine.test.ts` |
| 15 | 29 | `test:tax-pack-catalog` | `ts-node --transpile-only -P tests/tsconfig.json tests/tax-pack-catalog.test.ts` |
| 16 | 31 | `test:manual-tax-config` | `node tests/run-electron-node-test.cjs tests/manual-tax-config.test.ts` |
| 17 | 33 | `test:support-ticket` | `node tests/run-electron-node-test.cjs tests/support-ticket.test.ts` |
| 18 | 35 | `test:phone-search-integration` | `node tests/run-electron-node-test.cjs tests/phone-search-integration.test.ts` |
| 19 | 37 | `test:notes-validation` | `ts-node --transpile-only -P tests/tsconfig.json tests/order-notes-validation.test.ts` |
| 20 | 39 | `test:cancel-override` | `node tests/run-electron-node-test.cjs tests/cancel-override.test.ts && node tests/run-electron-node-test.cjs tests/manager-pin-rate-limit-bypass.test.ts` |
| 21 | 41 | `test:order-item-addons` | `node tests/run-electron-node-test.cjs tests/order-item-addons.test.ts && node tests/run-electron-node-test.cjs tests/addon-price-integrity.test.ts` |
| 22 | 43 | `test:windows-country-code-crash` | `node tests/run-electron-node-test.cjs tests/issue-windows-country-code-crash.test.ts` |
| 23 | 45 | `test:sequence` | `node tests/run-electron-node-test.cjs tests/sequence-generation.test.ts` |
| 24 | 47 | `test:integration-tax` | `node tests/run-electron-node-test.cjs tests/integration-tax.test.ts` |
| 25 | 49 | `test:issue-214` | `node tests/run-electron-node-test.cjs tests/issue-214-payment-integrity.test.ts` |
| 26 | 51 | `test:issue-214-migration` | `node tests/run-electron-node-test.cjs tests/issue-214-migration-integrity.test.ts` |
| 27 | 53 | `test:integration-reconciliation` | `node tests/run-electron-node-test.cjs tests/integration-bill-reconciliation.test.ts` |
| 28 | 55 | `test:integration-discount` | `node tests/run-electron-node-test.cjs tests/integration-discount-edge.test.ts` |
| 29 | 57 | `test:discount-system` | `node tests/run-electron-node-test.cjs tests/discount-system.test.ts` |
| 30 | 59 | `test:integration-loyalty-global` | `node tests/run-electron-node-test.cjs tests/integration-loyalty-global-rate.test.ts` |
| 31 | 61 | `test:integration-loyalty-redemption` | `node tests/run-electron-node-test.cjs tests/integration-loyalty-redemption.test.ts` |
| 32 | 63 | `test:issue-24` | `node tests/run-electron-node-test.cjs tests/issue-24-cancel-item-checkout.test.ts` |
| 33 | 65 | `test:issue-134-mgmt` | `node tests/run-electron-node-test.cjs tests/issue-134-station-management.test.ts` |
| 34 | 67 | `test:issue-244-product-addon-links` | `node tests/run-electron-node-test.cjs tests/issue-244-product-addon-links.test.ts` |
| 35 | 69 | `test:issue-258-bill-pagination` | `node tests/run-electron-node-test.cjs tests/issue-258-bill-pagination.test.ts` |
| 36 | 71 | `test:issue-266-currency-symbol-print` | `node tests/run-electron-node-test.cjs tests/issue-266-currency-symbol-print.test.ts` |
| 37 | 73 | `test:held-orders` | `node tests/run-electron-node-test.cjs tests/held-orders.test.ts && ts-node --transpile-only -P tests/tsconfig.json tests/held-orders-store.test.ts` |
| 38 | 75 | `test:upgrade-path` | `node tests/run-electron-node-test.cjs tests/upgrade-path.test.ts` |
| 39 | 77 | `test:master-pin` | `node tests/run-electron-node-test.cjs tests/master-pin.test.ts` |
| 40 | 79 | `test:database-tools-api` | `node tests/run-electron-node-test.cjs tests/database-tools-api.test.ts && node tests/run-electron-node-test.cjs tests/database-maintenance-lock.test.ts` |
| 41 | 81 | `test:phone-migration` | `node tests/run-electron-node-test.cjs tests/phone-migration.test.ts` |
| 42 | 83 | `test:whatsapp-schema` | `node tests/run-electron-node-test.cjs tests/whatsapp-schema.test.ts` |
| 43 | 85 | `test:whatsapp-middleware` | `node tests/run-electron-node-test.cjs tests/whatsapp-middleware.test.ts` |
| 44 | 87 | `test:dev-tooling` | `ts-node --transpile-only -P tests/tsconfig.json tests/dev-tooling-scripts.test.ts && npm run test:phase2 && npm run test:url-allowlist && npm run test:static-routes` |
| 45 | 89 | `test:shutdown-lifecycle` | `npm run build && node tests/run-electron-node-test.cjs tests/shutdown-lifecycle.test.ts` |

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • npx ts-node --transpile-only -P tests/tsconfig.json tests/dev-tooling-scripts.test.ts
  • npm run test:dev-tooling
  • npm run test:release-config
  • node scripts/ci/run-test-shard.cjs parameter validation and fail-fast checks
  • CI workflow .github/workflows/ci.yml matrix and runner isolation schema verification
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

`npm test` runs ~90 suites serially (~3.5 min) and dominated the PR
critical path, so merge-ready time was effectively the linux-baseline
wall time. Split the same suite list (derived at run time from
package.json's "test" script, so coverage cannot drift) into two
round-robin shards and run them on separate runners in parallel with
the build/lint job.

Shards are intentionally separate runners rather than two background
processes on one machine: several auth suites interfere (rate-limit /
JWT state) when the suite runs concurrently on a single host.

No test coverage is removed; the `npm test` script and its pretest are
unchanged for local/release use, and CI runs the identical suite set.
@khaira777
khaira777 requested a review from itsbkm as a code owner August 16, 2026 06:38
@khaira777
khaira777 merged commit 14a9c74 into main Aug 16, 2026
11 checks passed
@khaira777
khaira777 deleted the perf/324-shard-core-test-suite branch August 16, 2026 13:53
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.

1 participant