Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ jobs:
- name: Build packages
run: pnpm build

- name: Migrate test database
run: pnpm db:migrate
env:
DATABASE_URL: postgresql://haip:haip@localhost:5432/haip_test

- name: Run tests and verify README test counts
run: node scripts/sync-test-count.mjs --check
env:
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<img src="https://img.shields.io/badge/NestJS-framework-E0234E?logo=nestjs&logoColor=white" alt="NestJS" />
<img src="https://img.shields.io/badge/PostgreSQL-database-4169E1?logo=postgresql&logoColor=white" alt="PostgreSQL" />
<img src="https://img.shields.io/badge/License-Apache%202.0-blue" alt="Apache 2.0 License" />
<img src="https://img.shields.io/badge/Tests-1602%20passing-brightgreen" alt="1602 Tests Passing" /> <img src="https://img.shields.io/badge/AI%20Agents-12%20built--in-blueviolet" alt="12 AI Agents" />
<img src="https://img.shields.io/badge/Tests-1630%20passing-brightgreen" alt="1630 Tests Passing" /> <img src="https://img.shields.io/badge/AI%20Agents-12%20built--in-blueviolet" alt="12 AI Agents" />
</p>

<p align="center">
Expand Down Expand Up @@ -510,7 +510,8 @@ Operator notes for activating existing adapters, metasearch landings on the dire
| OTA Channels | Booking.com + Expedia (EQC) + SiteMinder + DerbySoft | Direct + aggregated OTA connectivity (ARI + content) |
| XML Processing | fast-xml-parser | Booking.com OTA XML protocol |
| Package Manager | pnpm workspaces | Monorepo management |
| Testing | Vitest (1602 tests across 223 test files) | Unit and integration tests || Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |
| Testing | Vitest (1630 passing tests across 228 files with passing tests) | Unit and integration tests |
| Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |
| Containers | Docker + docker-compose | Local dev and production deployment |
| CI/CD | GitHub Actions | Automated testing, builds, and releases |

Expand Down Expand Up @@ -642,7 +643,7 @@ Before going live, verify the items in [`docs/deployment.md`](./docs/deployment.
### Run tests

```bash
# All tests (1602 tests across 223 test files)
# Passing-test count: 1630 test cases across 228 files (skipped excluded)

# API tests only
pnpm --filter @telivityhaip/api test
Expand Down Expand Up @@ -1190,7 +1191,7 @@ HAIP is built in public and contributions are welcome.
pnpm install # Install dependencies
pnpm build # Build all workspace packages
pnpm dev # Start API in dev mode (hot reload)
pnpm test # Run all tests (1602 tests, 223 files)
pnpm test # Run all tests (1630 passing, 228 files with passes; skipped excluded)
pnpm lint # ESLint
```

Expand Down
8 changes: 5 additions & 3 deletions docs/test-stats.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"tests": 1602,
"files": 223,
"updatedAt": "2026-08-27T10:58:08.378Z"
"tests": 1630,
"files": 228,
"scope": "all workspace packages with a test script",
"semantics": "passed test cases and files containing at least one passed test; skipped test cases and skipped-only files are excluded",
"updatedAt": "2026-08-27T17:54:52.422Z"
}
142 changes: 142 additions & 0 deletions packages/shared/src/sync-test-count-script.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { describe, expect, it } from 'vitest';
import {
applyCounts,
buildStatsDocument,
countPassedReport,
isReadmeOutOfDate,
selectTestPackagePaths,
} from '../../../scripts/sync-test-count.mjs';

const COUNTS = { tests: 10, files: 2 };

function syncedReadmeFixture() {
return [
'<img src="https://img.shields.io/badge/License-Apache%202.0-blue" alt="Apache 2.0 License" />',
' <img src="https://img.shields.io/badge/Tests-10%20passing-brightgreen" alt="10 Tests Passing" />',
'',
'| Tool | Notes | Purpose |',
'| --- | --- | --- |',
'| Testing | Vitest (10 passing tests across 2 files with passing tests) | Unit and integration tests |',
'| Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |',
'',
'# Passing-test count: 10 test cases across 2 files (skipped excluded)',
'',
'pnpm test # Run all tests (10 passing, 2 files with passes; skipped excluded)',
'',
].join('\n');
}

describe('sync-test-count workspace coverage', () => {
it('selects every non-root workspace whose manifest defines the pnpm test surface', () => {
const manifests = new Map([
['/repo/apps/api', { scripts: { test: 'vitest run' } }],
['/repo/apps/dashboard', { scripts: { test: 'vitest run' } }],
['/repo/packages/database', { scripts: { test: 'vitest run' } }],
['/repo/packages/shared', { scripts: { test: 'vitest run' } }],
['/repo/packages/no-tests', { scripts: { build: 'tsup' } }],
]);
const workspaces = [
{ path: '/repo' },
...Array.from(manifests.keys(), (path) => ({ path })),
];

expect(selectTestPackagePaths(
workspaces,
'/repo',
(path) => manifests.get(path),
)).toEqual([
'/repo/apps/api',
'/repo/apps/dashboard',
'/repo/packages/database',
'/repo/packages/shared',
]);
});

it('counts passed test cases and only files with at least one passed test', () => {
expect(countPassedReport({
numPassedTests: 2,
testResults: [
{ assertionResults: [{ status: 'passed' }, { status: 'skipped' }] },
{ assertionResults: [{ status: 'skipped' }] },
{ assertionResults: [] },
{ assertionResults: [{ status: 'passed' }] },
],
})).toEqual({ tests: 2, files: 2 });
});

it('publishes the test-case count scope and skipped-test semantics explicitly', () => {
expect(buildStatsDocument(
{ tests: 12, files: 3 },
'2026-08-25T12:00:00.000Z',
)).toEqual({
tests: 12,
files: 3,
scope: 'all workspace packages with a test script',
semantics: 'passed test cases and files containing at least one passed test; skipped test cases and skipped-only files are excluded',
updatedAt: '2026-08-25T12:00:00.000Z',
});
});
});

describe('sync-test-count README applyCounts / --check coverage', () => {
it('is idempotent on a fully synced README (check would pass)', () => {
const readme = syncedReadmeFixture();
expect(applyCounts(readme, COUNTS)).toBe(readme);
expect(isReadmeOutOfDate(readme, COUNTS)).toBe(false);
});

it('fails check when the Tests badge is stale', () => {
const stale = syncedReadmeFixture().replace(
/Tests-10%20passing-brightgreen" alt="10 Tests Passing"/,
'Tests-9%20passing-brightgreen" alt="9 Tests Passing"',
);
expect(isReadmeOutOfDate(stale, COUNTS)).toBe(true);
expect(applyCounts(stale, COUNTS)).toContain('Tests-10%20passing');
});

it('fails check when the Passing-test heading is stale', () => {
const stale = syncedReadmeFixture().replace(
'# Passing-test count: 10 test cases across 2 files (skipped excluded)',
'# Passing-test count: 9 test cases across 2 files (skipped excluded)',
);
expect(isReadmeOutOfDate(stale, COUNTS)).toBe(true);
expect(applyCounts(stale, COUNTS)).toContain(
'# Passing-test count: 10 test cases across 2 files (skipped excluded)',
);
});

it('fails check when the pnpm test command count is stale', () => {
const stale = syncedReadmeFixture().replace(
'pnpm test # Run all tests (10 passing, 2 files with passes; skipped excluded)',
'pnpm test # Run all tests (9 passing, 2 files with passes; skipped excluded)',
);
expect(isReadmeOutOfDate(stale, COUNTS)).toBe(true);
expect(applyCounts(stale, COUNTS)).toContain(
'pnpm test # Run all tests (10 passing, 2 files with passes; skipped excluded)',
);
});

it('fails check when Testing and Build remain concatenated on one row', () => {
const malformed = syncedReadmeFixture().replace(
'| Testing | Vitest (10 passing tests across 2 files with passing tests) | Unit and integration tests |\n| Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |\n',
'| Testing | Vitest (10 passing tests across 2 files with passing tests) | Unit and integration tests || Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |\n',
);
expect(isReadmeOutOfDate(malformed, COUNTS)).toBe(true);
const fixed = applyCounts(malformed, COUNTS);
expect(fixed).toContain(
'| Testing | Vitest (10 passing tests across 2 files with passing tests) | Unit and integration tests |',
);
expect(fixed).toContain(
'| Build | tsup (packages) + Vite (dashboard) + nest build (API) | Fast builds |',
);
expect(fixed).not.toContain('tests || Build |');
});

it('fails check when only the Testing table row counts are stale', () => {
const stale = syncedReadmeFixture().replace(
'Vitest (10 passing tests across 2 files with passing tests)',
'Vitest (9 passing tests across 2 files with passing tests)',
);
expect(isReadmeOutOfDate(stale, COUNTS)).toBe(true);
});
});
Loading
Loading