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+ } ) ;
0 commit comments