Skip to content

Commit 6cf9075

Browse files
author
Hermes
committed
test(marketplace): add test coverage for fetch-source guard block
- Add marketplace-fetch-source.test.ts covering all guard paths: - missing sourceUrl only (stderr mentions sourceUrl, exit 0) - missing sha only (stderr mentions sha, exit 0) - missing both (stderr mentions both, exit 0) - both present (guard does not trigger, gh api errors expected) - missing entry.json (exit 1) - Trim inline comment to 2 lines per review feedback
1 parent a9a6b97 commit 6cf9075

2 files changed

Lines changed: 105 additions & 3 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { describe, it, expect } from 'bun:test';
2+
import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3+
import { resolve, join } from 'node:path';
4+
import { tmpdir } from 'node:os';
5+
import { spawnSync } from 'node:child_process';
6+
7+
const SCRIPT = resolve(import.meta.dir, '../marketplace-fetch-source.ts');
8+
9+
interface FetchOutput {
10+
files: string[];
11+
errors: string[];
12+
}
13+
14+
function runFetch(entryJson: Record<string, unknown>): { output: FetchOutput; stderr: string; exitCode: number } {
15+
const artifactsDir = mkdtempSync(join(tmpdir(), 'fetch-test-'));
16+
try {
17+
mkdirSync(join(artifactsDir, 'source'), { recursive: true });
18+
writeFileSync(join(artifactsDir, 'entry.json'), JSON.stringify(entryJson));
19+
20+
const result = spawnSync('bun', [SCRIPT], {
21+
env: { ...process.env, ARTIFACTS_DIR: artifactsDir },
22+
stdio: ['ignore', 'pipe', 'pipe'],
23+
});
24+
25+
const exitCode = result.status ?? 1;
26+
const stdout = result.stdout?.toString() ?? '';
27+
const stderr = result.stderr?.toString() ?? '';
28+
29+
let output: FetchOutput = { files: [], errors: [] };
30+
if (stdout) {
31+
try {
32+
output = JSON.parse(stdout);
33+
} catch {
34+
// stdout wasn't JSON — return empty default
35+
}
36+
}
37+
38+
return { output, stderr, exitCode };
39+
} finally {
40+
rmSync(artifactsDir, { recursive: true, force: true });
41+
}
42+
}
43+
44+
describe('marketplace-fetch-source: guard for missing sourceUrl/sha', () => {
45+
it('exits 0 with empty files when sourceUrl is missing', () => {
46+
const { output, stderr, exitCode } = runFetch({ sha: 'abc123' });
47+
expect(exitCode).toBe(0);
48+
expect(output.files).toHaveLength(0);
49+
expect(stderr).toContain('sourceUrl');
50+
expect(output.errors.length).toBeGreaterThan(0);
51+
expect(output.errors[0]).toContain('sourceUrl');
52+
});
53+
54+
it('exits 0 with empty files when sha is missing', () => {
55+
const { output, stderr, exitCode } = runFetch({ sourceUrl: 'https://github.com/owner/repo/blob/main/path' });
56+
expect(exitCode).toBe(0);
57+
expect(output.files).toHaveLength(0);
58+
expect(stderr).toContain('sha');
59+
expect(output.errors.length).toBeGreaterThan(0);
60+
expect(output.errors[0]).toContain('sha');
61+
});
62+
63+
it('exits 0 with empty files when both sourceUrl and sha are missing', () => {
64+
const { output, stderr, exitCode } = runFetch({});
65+
expect(exitCode).toBe(0);
66+
expect(output.files).toHaveLength(0);
67+
expect(stderr).toContain('sourceUrl');
68+
expect(stderr).toContain('sha');
69+
expect(output.errors.length).toBeGreaterThan(0);
70+
});
71+
72+
it('does not trigger guard when entry.json has both sourceUrl and sha', () => {
73+
// When both fields are present, the guard should NOT fire.
74+
// The script proceeds past the guard to URL parsing and gh api calls.
75+
// Since gh api will fail in the test env, we verify the guard didn't
76+
// trigger by checking stderr does NOT contain the guard's signature message.
77+
const { stderr, output } = runFetch({
78+
sourceUrl: 'https://github.com/coleam00/Archon/blob/main/README.md',
79+
sha: 'abc123def456',
80+
});
81+
expect(stderr).not.toContain('missing required field');
82+
expect(output.files).toHaveLength(0); // no fetch possible in test env
83+
// The script proceeds past the guard; gh api errors are expected.
84+
expect(output.errors.length).toBeGreaterThan(0);
85+
expect(output.errors.some((e) => e.includes('gh api'))).toBe(true);
86+
});
87+
});
88+
89+
describe('marketplace-fetch-source: missing entry.json', () => {
90+
it('exits 1 when entry.json is absent', () => {
91+
const artifactsDir = mkdtempSync(join(tmpdir(), 'fetch-test-'));
92+
try {
93+
// No entry.json created
94+
const result = spawnSync('bun', [SCRIPT], {
95+
env: { ...process.env, ARTIFACTS_DIR: artifactsDir },
96+
stdio: ['ignore', 'pipe', 'pipe'],
97+
});
98+
expect(result.status ?? 1).toBe(1);
99+
} finally {
100+
rmSync(artifactsDir, { recursive: true, force: true });
101+
}
102+
});
103+
});

.archon/scripts/marketplace-fetch-source.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ mkdirSync(sourceDir, { recursive: true });
3434
const errors: string[] = [];
3535
const files: string[] = [];
3636

37-
// Guard: sourceUrl and sha are required for fetching source files.
38-
// If either is missing (e.g., PR doesn't add a marketplace entry),
39-
// output an empty result so downstream nodes can proceed gracefully.
37+
// Guard: sourceUrl/sha are required. If missing, output empty result so
38+
// downstream nodes proceed gracefully (e.g., PR without a marketplace entry).
4039
if (!sourceUrl || !sha) {
4140
const missing: string[] = [];
4241
if (!sourceUrl) missing.push('sourceUrl');

0 commit comments

Comments
 (0)