1+ import { describe , it , expect , jest , beforeEach } from "@jest/globals" ;
2+ import { createHelpers } from "../../helper.js" ;
3+ import { createMockDeps , createMockOctokit } from "../fixtures/mock-deps.js" ;
4+ import { Dependencies } from "../../types/Dependencies.js" ;
5+
6+ describe ( "createHelpers - calculateMetaData" , ( ) => {
7+ let deps : Dependencies ;
8+
9+ beforeEach ( ( ) => {
10+ deps = createMockDeps ( ) ;
11+ } ) ;
12+
13+ it ( "returns metadata from GitHub API and SCC" , async ( ) => {
14+ const helpers = createHelpers ( deps ) ;
15+ const result = await helpers . calculateMetaData ( ) ;
16+
17+ expect ( result . name ) . toBe ( "test-repo" ) ;
18+ expect ( result . description ) . toBe ( "A test repository" ) ;
19+ expect ( result . repositoryURL ) . toBe ( "https://github.com/test-owner/test-repo" ) ;
20+ expect ( result . repositoryVisibility ) . toBe ( "public" ) ;
21+ expect ( result . languages ) . toEqual ( [ "TypeScript" , "JavaScript" ] ) ;
22+ expect ( result . laborHours ) . toBeGreaterThan ( 0 ) ;
23+ expect ( result . reuseFrequency ?. forks ) . toBe ( 5 ) ;
24+ expect ( result . tags ) . toEqual ( [ "test" , "automation" ] ) ;
25+ } ) ;
26+
27+ it ( "reports private visibility for private repos" , async ( ) => {
28+ const mockOctokit = createMockOctokit ( {
29+ rest : {
30+ repos : {
31+ get : jest . fn < any > ( ) . mockResolvedValue ( {
32+ data : {
33+ name : "private-repo" ,
34+ description : "Secret stuff" ,
35+ html_url : "https://github.com/test-owner/private-repo" ,
36+ private : true ,
37+ forks_count : 0 ,
38+ topics : [ ] ,
39+ created_at : "2024-01-01T00:00:00Z" ,
40+ updated_at : "2024-06-01T00:00:00Z" ,
41+ default_branch : "main" ,
42+ } ,
43+ } ) ,
44+ } ,
45+ } ,
46+ } ) ;
47+
48+ deps = createMockDeps ( { octokit : mockOctokit } ) ;
49+ const helpers = createHelpers ( deps ) ;
50+ const result = await helpers . calculateMetaData ( ) ;
51+
52+ expect ( result . repositoryVisibility ) . toBe ( "private" ) ;
53+ } ) ;
54+
55+ it ( "handles SCC failure gracefully" , async ( ) => {
56+ deps = createMockDeps ( {
57+ exec : jest . fn < any > ( ) . mockRejectedValue ( new Error ( "scc not found" ) ) ,
58+ } ) ;
59+
60+ const helpers = createHelpers ( deps ) ;
61+ await expect ( helpers . calculateMetaData ( ) ) . rejects . toThrow ( "scc not found" ) ;
62+ expect ( deps . log . error ) . toHaveBeenCalled ( ) ;
63+ } ) ;
64+ } ) ;
65+
66+ describe ( "createHelpers - getBaseBranch" , ( ) => {
67+ it ( "returns configured branch when provided" , async ( ) => {
68+ const deps = createMockDeps ( { branch : "develop" } ) ;
69+ const helpers = createHelpers ( deps ) ;
70+
71+ expect ( await helpers . getBaseBranch ( ) ) . toBe ( "develop" ) ;
72+ } ) ;
73+
74+ it ( "fetches default branch from API when not configured" , async ( ) => {
75+ const deps = createMockDeps ( { branch : "" } ) ;
76+ const helpers = createHelpers ( deps ) ;
77+
78+ expect ( await helpers . getBaseBranch ( ) ) . toBe ( "main" ) ;
79+ expect ( deps . octokit . rest . repos . get ) . toHaveBeenCalled ( ) ;
80+ } ) ;
81+ } ) ;
82+
83+ describe ( "createHelpers - readJSON" , ( ) => {
84+ it ( "parses valid JSON from file" , async ( ) => {
85+ const mockData = { name : "test" , version : "1.0" } ;
86+ const deps = createMockDeps ( {
87+ readFile : jest . fn < any > ( ) . mockResolvedValue ( JSON . stringify ( mockData ) ) ,
88+ } ) ;
89+ const helpers = createHelpers ( deps ) ;
90+
91+ const result = await helpers . readJSON ( "/some/path/code.json" ) ;
92+ expect ( result ) . toEqual ( mockData ) ;
93+ } ) ;
94+
95+ it ( "returns null for missing files" , async ( ) => {
96+ const deps = createMockDeps ( {
97+ readFile : jest . fn < any > ( ) . mockRejectedValue ( new Error ( "ENOENT" ) ) ,
98+ } ) ;
99+ const helpers = createHelpers ( deps ) ;
100+
101+ const result = await helpers . readJSON ( "/missing/code.json" ) ;
102+ expect ( result ) . toBeNull ( ) ;
103+ } ) ;
104+
105+ it ( "returns null for invalid JSON" , async ( ) => {
106+ const deps = createMockDeps ( {
107+ readFile : jest . fn < any > ( ) . mockResolvedValue ( "not json {{{" ) ,
108+ } ) ;
109+ const helpers = createHelpers ( deps ) ;
110+
111+ const result = await helpers . readJSON ( "/bad/code.json" ) ;
112+ expect ( result ) . toBeNull ( ) ;
113+ } ) ;
114+ } ) ;
115+
116+ describe ( "createHelpers - sendPR" , ( ) => {
117+ it ( "creates a PR and sets outputs" , async ( ) => {
118+ const deps = createMockDeps ( ) ;
119+ const helpers = createHelpers ( deps ) ;
120+
121+ const fakeCodeJSON = { name : "test" } as any ;
122+ await helpers . sendPR ( fakeCodeJSON , "main" ) ;
123+
124+ expect ( deps . octokit . createPullRequest ) . toHaveBeenCalledWith (
125+ expect . objectContaining ( {
126+ owner : "test-owner" ,
127+ repo : "test-repo" ,
128+ base : "main" ,
129+ title : "Update code.json" ,
130+ } ) ,
131+ ) ;
132+ expect ( deps . setOutput ) . toHaveBeenCalledWith ( "updated" , true ) ;
133+ expect ( deps . setOutput ) . toHaveBeenCalledWith ( "method_used" , "pull_request" ) ;
134+ } ) ;
135+
136+ it ( "uses archival title and labels when archived" , async ( ) => {
137+ const deps = createMockDeps ( { isArchived : true } ) ;
138+ const helpers = createHelpers ( deps ) ;
139+
140+ await helpers . sendPR ( { name : "test" } as any , "main" ) ;
141+
142+ expect ( deps . octokit . createPullRequest ) . toHaveBeenCalledWith (
143+ expect . objectContaining ( {
144+ title : "Update code.json for archival" ,
145+ labels : [ "archived" ] ,
146+ } ) ,
147+ ) ;
148+ } ) ;
149+ } ) ;
150+
151+ describe ( "createHelpers - pushDirectlyWithFallback" , ( ) => {
152+ it ( "falls back to PR when no admin token" , async ( ) => {
153+ const deps = createMockDeps ( { adminToken : "" } ) ;
154+ const helpers = createHelpers ( deps ) ;
155+
156+ await helpers . pushDirectlyWithFallback ( { name : "test" } as any , "main" ) ;
157+
158+ // Should have fallen back to PR
159+ expect ( deps . octokit . createPullRequest ) . toHaveBeenCalled ( ) ;
160+ expect ( deps . log . error ) . toHaveBeenCalledWith (
161+ expect . stringContaining ( "ADMIN_TOKEN is not provided" ) ,
162+ ) ;
163+ } ) ;
164+
165+ it ( "pushes directly when admin token is available" , async ( ) => {
166+ const adminOctokit = createMockOctokit ( ) ;
167+ const deps = createMockDeps ( {
168+ adminToken : "admin-pat-token" ,
169+ adminOctokit,
170+ } ) ;
171+ const helpers = createHelpers ( deps ) ;
172+
173+ await helpers . pushDirectlyWithFallback ( { name : "test" } as any , "main" ) ;
174+
175+ expect ( adminOctokit . rest . repos . createOrUpdateFileContents ) . toHaveBeenCalled ( ) ;
176+ expect ( deps . setOutput ) . toHaveBeenCalledWith ( "method_used" , "direct_push" ) ;
177+ } ) ;
178+ } ) ;
179+
180+ describe ( "createHelpers - validateOnly" , ( ) => {
181+ it ( "fails when code.json is missing" , async ( ) => {
182+ const deps = createMockDeps ( {
183+ readFile : jest . fn < any > ( ) . mockRejectedValue ( new Error ( "ENOENT" ) ) ,
184+ } ) ;
185+ const helpers = createHelpers ( deps ) ;
186+
187+ await helpers . validateOnly ( ) ;
188+
189+ expect ( deps . setFailed ) . toHaveBeenCalledWith (
190+ expect . stringContaining ( "code.json file not found" ) ,
191+ ) ;
192+ } ) ;
193+
194+ it ( "succeeds for valid code.json" , async ( ) => {
195+ const validCodeJSON = await import ( "../fixtures/test-code.json" ) ;
196+ const deps = createMockDeps ( {
197+ readFile : jest . fn < any > ( ) . mockResolvedValue ( JSON . stringify ( validCodeJSON . default ?? validCodeJSON ) ) ,
198+ } ) ;
199+ const helpers = createHelpers ( deps ) ;
200+
201+ await helpers . validateOnly ( ) ;
202+
203+ expect ( deps . setFailed ) . not . toHaveBeenCalled ( ) ;
204+ expect ( deps . log . info ) . toHaveBeenCalledWith ( "code.json is valid!" ) ;
205+ } ) ;
206+ } ) ;
0 commit comments