fix: report line-only coverage honestly and tighten Bun test helpers
CI / policy (push) Successful in 7s
CI / validate (push) Successful in 41s
CI / build (push) Successful in 36s
CI / policy (pull_request) Successful in 10s
CI / validate (pull_request) Successful in 32s
CI / security (push) Successful in 1m3s
CI / test (push) Successful in 3m3s
CI / build (pull_request) Successful in 23s
CI / security (pull_request) Successful in 57s
CI / test (pull_request) Successful in 3m25s
CI / benchmark (push) Successful in 5m19s
CI / benchmark (pull_request) Successful in 4m48s
CI / images (push) Successful in 1m20s
CI / required (push) Successful in 3s
CI / images (pull_request) Successful in 54s
CI / required (pull_request) Successful in 1s
CI / policy (push) Successful in 7s
CI / validate (push) Successful in 41s
CI / build (push) Successful in 36s
CI / policy (pull_request) Successful in 10s
CI / validate (pull_request) Successful in 32s
CI / security (push) Successful in 1m3s
CI / test (push) Successful in 3m3s
CI / build (pull_request) Successful in 23s
CI / security (pull_request) Successful in 57s
CI / test (pull_request) Successful in 3m25s
CI / benchmark (push) Successful in 5m19s
CI / benchmark (pull_request) Successful in 4m48s
CI / images (push) Successful in 1m20s
CI / required (push) Successful in 3s
CI / images (pull_request) Successful in 54s
CI / required (pull_request) Successful in 1s
The generated HTML and Cobertura reports no longer show unmeasured branch and function coverage as 100%. Poll and waitFor deadlines use performance.now so frozen dates cannot make them loop forever, the malformed-configuration test covers undefined again next to the empty array, compose health checks honor PORT, and the migration spec lists only what is still open. Refs #291, #292, #293 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dfe6d28d2b
commit
282d9c6787
6 files changed
+46
-30
No files matched your search
+1
-1
@@ -17,7 +17,7 @@ services:
|
|||||||
'CMD',
|
'CMD',
|
||||||
'bun',
|
'bun',
|
||||||
'-e',
|
'-e',
|
||||||
"fetch('http://localhost:4000/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))",
|
"fetch('http://localhost:' + (process.env.PORT || 4000) + '/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))",
|
||||||
]
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ services:
|
|||||||
'CMD',
|
'CMD',
|
||||||
'bun',
|
'bun',
|
||||||
'-e',
|
'-e',
|
||||||
"fetch('http://localhost:4000/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))",
|
"fetch('http://localhost:' + (process.env.PORT || 4000) + '/').then(r => process.exit(r.ok ? 0 : 1), () => process.exit(1))",
|
||||||
]
|
]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
|
|||||||
+13
-22
@@ -41,10 +41,6 @@ start); it is not a result from the live Gitea workflow.
|
|||||||
the live runner. The `images` job still has Alpine's `nodejs` on `PATH` for the
|
the live runner. The `images` job still has Alpine's `nodejs` on `PATH` for the
|
||||||
checkout action, which the project command does not use. These and the Gitea
|
checkout action, which the project command does not use. These and the Gitea
|
||||||
activation steps remain unverified until a live pull-request run.
|
activation steps remain unverified until a live pull-request run.
|
||||||
- **Intermittent spec.** `real planning worker cancellation > preserves balances
|
|
||||||
and provenance after a valid checkpoint` failed in one of three full coverage
|
|
||||||
runs on the development laptop (it cancels from a stage's progress callback and
|
|
||||||
asserts that the stage did not complete); the cause is not yet investigated.
|
|
||||||
- **ADR 0009** stays `proposed` until the final gate.
|
- **ADR 0009** stays `proposed` until the final gate.
|
||||||
|
|
||||||
## Problem Statement
|
## Problem Statement
|
||||||
@@ -276,23 +272,18 @@ remains proposed; the Status section lists what is implemented.
|
|||||||
The glossary receives no new terms because the choices concern general tooling,
|
The glossary receives no new terms because the choices concern general tooling,
|
||||||
rather than new leave-planning concepts.
|
rather than new leave-planning concepts.
|
||||||
|
|
||||||
Remaining verification and unresolved requirements:
|
Remaining verification (everything else is recorded as verified under Status):
|
||||||
|
|
||||||
- Angular compilation, test-resource loading, serializers, mocking, isolation,
|
- Native lint and format wait for a stable Bun release that ships `bun lint` and
|
||||||
and the existing worker harnesses must work under the native Bun runner.
|
`bun fmt` (#290).
|
||||||
- Standalone TypeScript 7 diagnostics must be checked alongside Angular's
|
- Live Gitea validation (#293): the custom job `shell` template, the Bun download
|
||||||
TypeScript 6 diagnostics; their coexistence is agreed, but project-specific
|
in each job, the `images` job with publisher credentials, and the benchmark's
|
||||||
differences have not been measured.
|
timing margin on the runners.
|
||||||
- Current Istanbul reports include text, HTML, and Cobertura. Native coverage
|
- The `images` job keeps Alpine's `nodejs` on `PATH` because the checkout action
|
||||||
equivalence and report-format support have not been established; accepting a
|
needs it (a runner requirement); project commands do not use it and no Node
|
||||||
reduced capability or different downstream format has not been agreed.
|
fallback for them is agreed.
|
||||||
- Third-party Gitea checkout and artifact actions may impose their own Node
|
- `src/test-setup.ts` imports Analog's internal `jitTransform` by path; replace it
|
||||||
requirement. Identify the actual platform constraint; no exception allowing
|
when Angular or Analog exports a JIT transform.
|
||||||
project commands to fall back to Node has been agreed, and CI infrastructure
|
|
||||||
changes beyond the migration require a separate decision.
|
|
||||||
- The exact Bun release and container compatibility remain to be verified.
|
|
||||||
The local installation currently lacks the documented native lint, format,
|
|
||||||
and standalone type-check commands.
|
|
||||||
|
|
||||||
Research checked on 2026-10-17:
|
Research checked on 2026-10-17:
|
||||||
|
|
||||||
@@ -305,8 +296,8 @@ Research checked on 2026-10-17:
|
|||||||
- [Angular build guidance](https://angular.dev/ecosystem/custom-build-pipeline)
|
- [Angular build guidance](https://angular.dev/ecosystem/custom-build-pipeline)
|
||||||
recommends retaining its standard CLI pipeline.
|
recommends retaining its standard CLI pipeline.
|
||||||
- [Angular's web request engine](https://angular.dev/api/ssr/AngularAppEngine) and
|
- [Angular's web request engine](https://angular.dev/api/ssr/AngularAppEngine) and
|
||||||
[Bun's HTTP server](https://bun.sh/docs/runtime/http/server) provide the intended
|
[Bun's HTTP server](https://bun.sh/docs/runtime/http/server) describe the original
|
||||||
server integration contracts; their compatibility here remains untested.
|
server integration plan, replaced by the `Bun.serve` static server above.
|
||||||
- [Angular version compatibility](https://angular.dev/reference/versions) and
|
- [Angular version compatibility](https://angular.dev/reference/versions) and
|
||||||
[compiler roadmap](https://angular.dev/roadmap) establish the current Angular
|
[compiler roadmap](https://angular.dev/roadmap) establish the current Angular
|
||||||
compiler boundary. [Microsoft's coexistence guidance](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#typescript-and-embedded-languages)
|
compiler boundary. [Microsoft's coexistence guidance](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/#typescript-and-embedded-languages)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
// Converts coverage/lcov.info (Bun) to HTML and Cobertura. Run: bun scripts/coverage-reports.mjs
|
// Converts coverage/lcov.info (Bun) to HTML and Cobertura. Run: bun scripts/coverage-reports.mjs
|
||||||
// shortcut: line coverage only, Bun's lcov has no usable branch/function data; revisit if Bun adds it.
|
// shortcut: line coverage only, Bun's lcov has no usable branch/function data; revisit if Bun adds it.
|
||||||
import { readFileSync } from 'node:fs';
|
// Totals here sum lines (8351/8578); Bun's text table averages per-file percentages, so it reads lower.
|
||||||
import { resolve } from 'node:path';
|
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||||
|
import { join, resolve } from 'node:path';
|
||||||
import libCoverage from 'istanbul-lib-coverage';
|
import libCoverage from 'istanbul-lib-coverage';
|
||||||
import libReport from 'istanbul-lib-report';
|
import libReport from 'istanbul-lib-report';
|
||||||
import reports from 'istanbul-reports';
|
import reports from 'istanbul-reports';
|
||||||
@@ -29,3 +30,26 @@ for (const record of readFileSync('coverage/lcov.info', 'utf8').split('end_of_re
|
|||||||
}
|
}
|
||||||
const context = libReport.createContext({ dir: 'coverage', coverageMap: map });
|
const context = libReport.createContext({ dir: 'coverage', coverageMap: map });
|
||||||
for (const name of ['html', 'cobertura']) reports.create(name).execute(context);
|
for (const name of ['html', 'cobertura']) reports.create(name).execute(context);
|
||||||
|
|
||||||
|
// Istanbul reports 0 branches/functions as 100%; relabel them instead of claiming coverage.
|
||||||
|
const rewrite = (file, edit) => writeFileSync(file, edit(readFileSync(file, 'utf8')));
|
||||||
|
for (const page of readdirSync('coverage', { recursive: true }).filter((f) => f.endsWith('.html')))
|
||||||
|
rewrite(join('coverage', page), (html) =>
|
||||||
|
html
|
||||||
|
.replace(
|
||||||
|
/<span class="strong">100% <\/span>(\s*<span class="quiet">(?:Branches|Functions)<\/span>\s*<span class='fraction'>)0\/0</g,
|
||||||
|
'<span class="strong">n/a </span>$1not collected<',
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/<td data-value="100" class="pct \w+">100%<\/td>(\s*)<td data-value="0" class="abs \w+">0\/0<\/td>/g,
|
||||||
|
'<td data-value="0" class="pct">n/a</td>$1<td data-value="0" class="abs">not collected</td>',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
rewrite('coverage/cobertura-coverage.xml', (xml) =>
|
||||||
|
xml
|
||||||
|
.replaceAll('branch-rate="1"', 'branch-rate="0"')
|
||||||
|
.replace(
|
||||||
|
'<coverage ',
|
||||||
|
'<!-- Line coverage only: bun test does not collect branch or function coverage. -->\n<coverage ',
|
||||||
|
),
|
||||||
|
);
|
||||||
@@ -174,6 +174,7 @@ describe('composable planning objectives', () => {
|
|||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
null,
|
null,
|
||||||
|
[undefined],
|
||||||
[[]],
|
[[]],
|
||||||
{ ...OPTIMIZATION_PRESETS.shortSequenceDeficit, conditions: ['internal', 'internal'] },
|
{ ...OPTIMIZATION_PRESETS.shortSequenceDeficit, conditions: ['internal', 'internal'] },
|
||||||
{ ...OPTIMIZATION_PRESETS.longestWorkSequence, direction: 'up' },
|
{ ...OPTIMIZATION_PRESETS.longestWorkSequence, direction: 'up' },
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ export const vi = Object.assign(bt.vi, {
|
|||||||
},
|
},
|
||||||
runAllTicks() {},
|
runAllTicks() {},
|
||||||
async waitFor<T>(fn: () => T | Promise<T>, { timeout = 1000, interval = 50 } = {}): Promise<T> {
|
async waitFor<T>(fn: () => T | Promise<T>, { timeout = 1000, interval = 50 } = {}): Promise<T> {
|
||||||
const end = Date.now() + timeout;
|
const end = performance.now() + timeout;
|
||||||
let last: unknown;
|
let last: unknown;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
try {
|
try {
|
||||||
return await fn();
|
return await fn();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
last = e;
|
last = e;
|
||||||
if (Date.now() > end) throw last;
|
if (performance.now() > end) throw last;
|
||||||
await new Promise((r) => setTimeout(r, interval));
|
await new Promise((r) => setTimeout(r, interval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,13 +78,13 @@ export type MockInstance<T extends AnyFn = AnyFn> = bt.Mock<T>;
|
|||||||
m === 'not'
|
m === 'not'
|
||||||
? chain(!negate)
|
? chain(!negate)
|
||||||
: async (...args: unknown[]) => {
|
: async (...args: unknown[]) => {
|
||||||
const end = Date.now() + timeout;
|
const end = performance.now() + timeout;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
try {
|
try {
|
||||||
const e: any = bt.expect(await fn());
|
const e: any = bt.expect(await fn());
|
||||||
return await (negate ? e.not : e)[m](...args);
|
return await (negate ? e.not : e)[m](...args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (Date.now() > end) throw err;
|
if (performance.now() > end) throw err;
|
||||||
await new Promise((r) => setTimeout(r, interval));
|
await new Promise((r) => setTimeout(r, interval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user