Skip to content

Commit fa20ba5

Browse files
committed
ci: track the dist built-output tests
The bare 'dist/' .gitignore pattern matched any directory named 'dist', silently excluding tests/dist/built-output.test.ts from commits and failing 'just validate' (test-dist) in CI. Anchor the pattern to the repository root so the build-output directory stays ignored while the test file is tracked.
1 parent 18022b0 commit fa20ba5

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

‎.gitignore‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
node_modules/
33

44
# Build output
5-
dist/
5+
/dist/
66
.astro/
77
artifacts/
88

‎tests/dist/built-output.test.ts‎

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { describe, expect, test } from 'bun:test';
2+
import { existsSync, readFileSync } from 'node:fs';
3+
import { resolve } from 'node:path';
4+
5+
const DIST = resolve('dist');
6+
7+
function requireBuilt(file: string): string {
8+
const path = resolve(DIST, file);
9+
if (!existsSync(path)) {
10+
throw new Error(`Required build output missing: ${file}. Run \`just build\` first.`);
11+
}
12+
return readFileSync(path, 'utf8');
13+
}
14+
15+
describe('built llms outputs', () => {
16+
test('all three llms files exist', () => {
17+
expect(existsSync(resolve(DIST, 'llms.txt'))).toBe(true);
18+
expect(existsSync(resolve(DIST, 'llms-small.txt'))).toBe(true);
19+
expect(existsSync(resolve(DIST, 'llms-full.txt'))).toBe(true);
20+
});
21+
22+
test('llms.txt uses canonical production URLs', () => {
23+
const content = requireBuilt('llms.txt');
24+
expect(content).toContain('https://purview.dev/llms-small.txt');
25+
expect(content).toContain('https://purview.dev/llms-full.txt');
26+
expect(content).toContain('https://github.com/purview-dev');
27+
});
28+
29+
test('llms-full.txt includes aggregated documentation content', () => {
30+
const content = requireBuilt('llms-full.txt');
31+
expect(content.trim().length).toBeGreaterThan(10_000);
32+
expect(content).toContain('Purview');
33+
});
34+
35+
test('llms outputs exclude secrets and local build paths', () => {
36+
const content = [
37+
requireBuilt('llms.txt'),
38+
requireBuilt('llms-small.txt'),
39+
requireBuilt('llms-full.txt'),
40+
].join('\n');
41+
expect(content).not.toMatch(/\bghp_[A-Za-z0-9]{36,}\b/);
42+
expect(content).not.toMatch(/\.cache[/\\]/);
43+
expect(content).not.toMatch(/[A-Za-z]:\\/);
44+
expect(content).not.toMatch(/node_modules/);
45+
});
46+
});
47+
48+
describe('built SEO outputs', () => {
49+
test('sitemap index exists and references the canonical site', () => {
50+
const content = requireBuilt('sitemap-index.xml');
51+
expect(content).toContain('https://purview.dev/sitemap-0.xml');
52+
});
53+
54+
test('robots.txt references the sitemap', () => {
55+
const content = requireBuilt('robots.txt');
56+
expect(content).toContain('https://purview.dev/sitemap-index.xml');
57+
});
58+
59+
test('key brand assets are present in the build', () => {
60+
for (const file of [
61+
'favicon.svg',
62+
'favicon-32.png',
63+
'apple-touch-icon.png',
64+
'og/default.png',
65+
'site.webmanifest',
66+
'CNAME',
67+
]) {
68+
expect(existsSync(resolve(DIST, file)), `missing ${file}`).toBe(true);
69+
}
70+
});
71+
});
72+
73+
describe('built HTML safety', () => {
74+
test('no local paths or secrets leak into generated pages', () => {
75+
const { glob } = require('fast-glob');
76+
const files = glob.sync('**/*.html', { cwd: DIST });
77+
for (const file of files) {
78+
const content = readFileSync(resolve(DIST, file), 'utf8');
79+
expect(content).not.toMatch(/[A-Za-z]:\\/);
80+
expect(content).not.toMatch(/\bghp_[A-Za-z0-9]{36,}\b/);
81+
}
82+
});
83+
});

0 commit comments

Comments
 (0)