test(api-docs): align post-process outputs (#6942)

- 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>
clean-squash
Jason Stirnaman 2026-03-13 17:30:39 -05:00 committed by GitHub
parent 1d200bd719
commit 90260d5d24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 23 deletions

View File

@ -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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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<OpenApiSpec>(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<OpenApiSpec>(specPath);
const spec = readYaml<OpenApiSpec>(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],
];

View File

@ -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 PRs 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 dont 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.