From cf38b05f53600d23173909ee131a13f8558d1aae Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Fri, 13 Mar 2026 15:58:24 -0500 Subject: [PATCH] test(api-docs): align post-process outputs - read assertions from _build artifacts instead of source specs\n- capture issue 6939 plan for follow-up steps\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- api-docs/scripts/test-post-process-specs.ts | 62 +++++++++++++-------- docs/plans/plan-issue-6939.md | 28 ++++++++++ 2 files changed, 67 insertions(+), 23 deletions(-) create mode 100644 docs/plans/plan-issue-6939.md diff --git a/api-docs/scripts/test-post-process-specs.ts b/api-docs/scripts/test-post-process-specs.ts index 48566ae48..073ca11b6 100644 --- a/api-docs/scripts/test-post-process-specs.ts +++ b/api-docs/scripts/test-post-process-specs.ts @@ -73,11 +73,20 @@ function createTmpRoot(): { productDir: string; specDir: string; specPath: string; + buildSpecPath: string; } { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'post-process-test-')); const productDir = path.join(root, 'influxdb3', 'core'); const specDir = path.join(productDir, 'v3'); const specPath = path.join(specDir, 'openapi.yaml'); + const buildSpecPath = path.join( + root, + '_build', + 'influxdb3', + 'core', + 'v3', + 'openapi.yaml' + ); fs.mkdirSync(specDir, { recursive: true }); @@ -90,7 +99,7 @@ function createTmpRoot(): { }; fs.writeFileSync(path.join(productDir, '.config.yml'), yaml.dump(config)); - return { root, productDir, specDir, specPath }; + return { root, productDir, specDir, specPath, buildSpecPath }; } function writeYaml(filePath: string, data: unknown): void { @@ -157,7 +166,7 @@ function assert(name: string, condition: boolean, reason: string): void { // --------------------------------------------------------------------------- function testDescriptionSetting(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml(specPath, makeSpec([{ name: 'Write data' }], ['Write data'])); writeYaml(path.join(specDir, 'tags.yml'), { @@ -169,7 +178,7 @@ function testDescriptionSetting(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('1a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); const tag = spec.tags?.find((t) => t.name === 'Write data'); assert( '1b. description applied to tag', @@ -182,7 +191,7 @@ function testDescriptionSetting(): void { } function testTagRename(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml(specPath, makeSpec([{ name: 'Cache data' }], ['Cache data'])); writeYaml(path.join(specDir, 'tags.yml'), { @@ -194,7 +203,7 @@ function testTagRename(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('2a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); const oldTag = spec.tags?.find((t) => t.name === 'Cache data'); assert( '2b. old tag name gone from tags[]', @@ -223,7 +232,7 @@ function testTagRename(): void { } function testXRelated(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml(specPath, makeSpec([{ name: 'Write data' }], ['Write data'])); writeYaml(path.join(specDir, 'tags.yml'), { @@ -240,7 +249,7 @@ function testXRelated(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('3a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); const tag = spec.tags?.find((t) => t.name === 'Write data'); const related = tag?.['x-related'] as | Array<{ title: string; href: string }> @@ -364,7 +373,7 @@ function testMalformedYamlFails(): void { // 8. Info overlay — API-specific content/info.yml function testInfoOverlay(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml( specPath, @@ -385,7 +394,7 @@ function testInfoOverlay(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('8a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); assert( '8b. title overridden', spec.info.title === 'Overridden Title', @@ -409,7 +418,7 @@ function testInfoOverlay(): void { // 9. Info overlay — product-level fallback function testInfoOverlayProductFallback(): void { - const { root, productDir, specPath } = createTmpRoot(); + const { root, productDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml( specPath, @@ -428,7 +437,7 @@ function testInfoOverlayProductFallback(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('9a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); assert( '9b. title from product-level', spec.info.title === 'Product-Level Title', @@ -446,7 +455,7 @@ function testInfoOverlayProductFallback(): void { // 10. Servers overlay function testServersOverlay(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml( specPath, @@ -474,7 +483,7 @@ function testServersOverlay(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('10a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); assert( '10b. servers replaced', spec.servers?.length === 1, @@ -497,7 +506,7 @@ function testServersOverlay(): void { // 11. Info overlay preserves fields not in overlay function testInfoOverlayPreservesFields(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml( specPath, @@ -521,7 +530,7 @@ function testInfoOverlayPreservesFields(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('11a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); assert( '11b. title preserved', spec.info.title === 'Original Title', @@ -550,7 +559,7 @@ function testInfoOverlayPreservesFields(): void { // 12. No content overlays — spec unchanged function testNoOverlaysNoWrite(): void { - const { root, specPath } = createTmpRoot(); + const { root, specPath, buildSpecPath } = createTmpRoot(); try { const original = makeSpec([{ name: 'Write data' }], ['Write data']); writeYaml(specPath, original); @@ -562,12 +571,19 @@ function testNoOverlaysNoWrite(): void { /* busy wait */ } - const { exitCode, stderr } = runScript(root, 'influxdb3/core'); + const { exitCode } = runScript(root, 'influxdb3/core'); assert('12a. exits 0', exitCode === 0, `exit code was ${exitCode}`); + + const built = readYaml(buildSpecPath); assert( - '12b. no write message', - !stderr.includes('wrote'), - `unexpected write: ${stderr}` + '12b. build output matches input when no overlays/tags', + JSON.stringify(built) === JSON.stringify(original), + 'build output differed from source' + ); + assert( + '12c. source file untouched', + fs.statSync(specPath).mtimeMs === mtime, + 'source spec modified' ); } finally { cleanup(root); @@ -576,7 +592,7 @@ function testNoOverlaysNoWrite(): void { // 13. Combined: info + servers + tags applied together function testCombinedOverlaysAndTags(): void { - const { root, specDir, specPath } = createTmpRoot(); + const { root, specDir, specPath, buildSpecPath } = createTmpRoot(); try { writeYaml( specPath, @@ -607,7 +623,7 @@ function testCombinedOverlaysAndTags(): void { const { exitCode } = runScript(root, 'influxdb3/core'); assert('13a. exits 0', exitCode === 0, `exit code was ${exitCode}`); - const spec = readYaml(specPath); + const spec = readYaml(buildSpecPath); assert( '13b. info title updated', spec.info.title === 'New Title', @@ -667,7 +683,7 @@ const tests: Array<[string, () => void]> = [ '11. Info overlay preserves fields not in overlay', testInfoOverlayPreservesFields, ], - ['12. No overlays or tags — no write', testNoOverlaysNoWrite], + ['12. No overlays or tags — build mirrors source', testNoOverlaysNoWrite], ['13. Combined: info + servers + tags', testCombinedOverlaysAndTags], ]; diff --git a/docs/plans/plan-issue-6939.md b/docs/plans/plan-issue-6939.md new file mode 100644 index 000000000..5f9fbd905 --- /dev/null +++ b/docs/plans/plan-issue-6939.md @@ -0,0 +1,28 @@ +## Issue #6939 plan + +### Summary + +- PR #6939 overhauls the API docs pipeline: a unified TypeScript post-processor (`api-docs/scripts/post-process-specs.ts`) writes resolved specs to `_build/`, Redoc and static copies read from `_build/`, and workflows add a TypeScript compile step. +- We need to review and harden the new script and workflows, focusing on correctness when overlays/tags are absent and ensuring CI/previews run the build steps in the right order. + +### Files to touch + +- `api-docs/scripts/post-process-specs.ts` — adjust write/log behavior when no overlays/tags are applied and ensure output handling is correct (lines \~350-395). +- `api-docs/scripts/test-post-process-specs.ts` — align tests with expected write/log behavior and add coverage if needed (lines \~552-575). +- `.circleci/config.yml` — confirm/adjust API docs build step ordering and TypeScript compile inclusion (lines \~24-35). +- `.github/workflows/pr-preview.yml` — ensure PR preview runs TypeScript compile + API docs generation when API docs change (lines \~118-150). +- (If needed) `api-docs/generate-api-docs.sh` — keep pipeline steps in sync (lines \~52-68). + +### Approach + +1. Run the PR’s test plan locally to surface current failures: `npx tsc --project api-docs/scripts/tsconfig.json`, `node api-docs/scripts/dist/post-process-specs.js`, `node api-docs/scripts/dist/generate-openapi-articles.js --static-only`, and `npx hugo --quiet` (accepting the runtime). Note existing untracked artifacts (`content/enterprise_influxdb/v1/api/v1/`, `static/openapi/`) to avoid clobbering. +2. Review `post-process-specs.ts` logic around overlays/tags and output writes; ensure it skips unnecessary writes/logs while still producing required `_build` outputs for downstream steps. +3. Update tests in `test-post-process-specs.ts` to reflect the intended behavior (particularly the “no overlays” case) and add assertions for `_build` outputs if needed. +4. Validate CI wiring: confirm `.circleci/config.yml` and `pr-preview.yml` run TypeScript compile before API docs generation and don’t skip API steps incorrectly. +5. Re-run the pipeline commands after changes; ensure Hugo/build/test outputs are clean, then prepare for branch/commit per guidelines. + +### Risks / Open questions + +- Hugo build is long (\~75s) and must not be canceled. +- If any product lacks overlays/tags, skipping writes could leave `_build` empty; need to balance no-op behavior with required outputs. +- Existing untracked API outputs may obscure diffs; keep them untouched.