Skip to content

Commit 40e6020

Browse files
authored
🐛 Fix regression in YAML parsing (#126)
1 parent eb016d1 commit 40e6020

4 files changed

Lines changed: 162 additions & 19 deletions

File tree

package-lock.json

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/github/workflow.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ export default class Workflow extends Base {
289289
path: this.#path,
290290
language: this.#language,
291291
text: this.#text,
292-
yaml: this.getYaml(),
292+
// Ensure yaml is the parsed object, not a pending Promise (regression fix)
293+
yaml: await this.getYaml(),
293294
isTruncated: this.#isTruncated,
294295
state: this.#state,
295296
created_at: this.#created_at,

test/github/workflow.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ describe('workflow', () => {
194194
expect(result.created_at).toBe('2023-01-01T00:00:00.000Z')
195195
expect(result.updated_at).toBe('2023-01-02T00:00:00.000Z')
196196
expect(result.last_run_at).toBe('2023-01-03T00:00:00.000Z')
197+
expect(result.yaml).not.toBeInstanceOf(Promise)
198+
expect(typeof result.yaml).toBe('object')
197199
})
198200

199201
test('should handle API errors gracefully', async () => {
@@ -225,4 +227,28 @@ describe('workflow', () => {
225227
expect(result.last_run_at).toBeNull()
226228
})
227229
})
230+
231+
/**
232+
* Regression test: ensure getWorkflow returns a parsed YAML object (not a Promise)
233+
*/
234+
test('should resolve yaml object directly (regression)', async () => {
235+
workflow.octokit.request = jest
236+
.fn()
237+
// workflow metadata
238+
.mockResolvedValueOnce({
239+
data: {
240+
id: 42,
241+
node_id: 'W_42',
242+
state: 'active',
243+
created_at: '2023-01-01T00:00:00Z',
244+
updated_at: '2023-01-01T00:00:00Z',
245+
},
246+
})
247+
// runs
248+
.mockResolvedValueOnce({data: {workflow_runs: []}})
249+
250+
const result = await workflow.getWorkflow('o', 'r', workflow.path)
251+
expect(result.yaml).not.toBeInstanceOf(Promise)
252+
expect(result.yaml && typeof result.yaml).toBe('object')
253+
})
228254
})
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* Integration-style test for Report extraction to ensure workflow YAML is parsed
3+
* and data sets (listeners, permissions, runsOn, secrets, vars, uses) are populated.
4+
* This is written BEFORE the underlying bug fix (yaml Promise) so it will fail initially (TDD).
5+
*/
6+
import {jest} from '@jest/globals'
7+
import Report from '../../src/report/report.js'
8+
9+
// Minimal mock logger with required interface (spinner-compatible methods)
10+
const mockLogger = () => ({
11+
debug: jest.fn(),
12+
warn: jest.fn(),
13+
error: jest.fn(),
14+
start: jest.fn(),
15+
stopAndPersist: jest.fn(),
16+
fail: jest.fn(),
17+
isDebug: true,
18+
text: '',
19+
set text(_) {},
20+
})
21+
22+
// Minimal mock cache (disabled path interactions for this test)
23+
const mockCache = () => ({
24+
path: '',
25+
exists: jest.fn().mockResolvedValue(false),
26+
load: jest.fn(),
27+
save: jest.fn(),
28+
})
29+
30+
// Sample workflow YAML content containing all extractable elements
31+
const WORKFLOW_YAML = `name: CI Full
32+
on:
33+
push:
34+
branches: [ main ]
35+
permissions:
36+
contents: read
37+
jobs:
38+
build:
39+
runs-on: ubuntu-latest
40+
steps:
41+
- name: Checkout
42+
uses: actions/checkout@v4
43+
- name: Setup
44+
uses: actions/setup-node@v4
45+
with:
46+
node-version: 20
47+
- name: Env usage
48+
run: echo "+\${{ secrets.MY_SECRET }}+\${{ vars.MY_VAR }}+"
49+
`
50+
51+
// Fake workflow object as produced by Repository.getWorkflows / Workflow.getWorkflow (after fix)
52+
function createWorkflow() {
53+
return {
54+
node_id: 'WF_node',
55+
path: '.github/workflows/ci.yml',
56+
language: 'YAML',
57+
text: WORKFLOW_YAML,
58+
yaml: {
59+
name: 'CI Full',
60+
on: {push: {branches: ['main']}},
61+
permissions: {contents: 'read'},
62+
jobs: {
63+
build: {
64+
'runs-on': 'ubuntu-latest',
65+
steps: [
66+
{name: 'Checkout', uses: 'actions/checkout@v4'},
67+
{name: 'Setup', uses: 'actions/setup-node@v4', with: {'node-version': 20}},
68+
{name: 'Env usage', run: 'echo "+${{ secrets.MY_SECRET }}+${{ vars.MY_VAR }}+"'},
69+
],
70+
},
71+
},
72+
},
73+
state: 'active',
74+
created_at: new Date().toISOString(),
75+
updated_at: new Date().toISOString(),
76+
last_run_at: new Date().toISOString(),
77+
}
78+
}
79+
80+
describe('Report extraction end-to-end (regression)', () => {
81+
test('should populate extraction sets from workflow YAML', async () => {
82+
const flags = {
83+
token: 'TEST',
84+
repository: 'o/r',
85+
csv: null,
86+
json: null,
87+
md: null,
88+
listeners: true,
89+
permissions: true,
90+
runsOn: true,
91+
secrets: true,
92+
vars: true,
93+
uses: true,
94+
unique: 'false',
95+
}
96+
97+
const report = new Report(flags, mockLogger(), mockCache())
98+
99+
const repoData = {
100+
workflows: [createWorkflow()],
101+
}
102+
103+
const data = await report.createReport(repoData)
104+
expect(data).toHaveLength(1)
105+
const wf = data[0]
106+
107+
// These expectations will fail until yaml Promise bug is fixed
108+
expect(wf.name).toBe('CI Full')
109+
expect(wf.listeners instanceof Set && wf.listeners.size).toBeGreaterThan(0)
110+
expect(wf.permissions instanceof Set && wf.permissions.size).toBeGreaterThan(0)
111+
expect(wf.runsOn instanceof Set && wf.runsOn.size).toBeGreaterThan(0)
112+
expect(wf.secrets instanceof Set && wf.secrets.size).toBeGreaterThan(0)
113+
expect(wf.vars instanceof Set && wf.vars.size).toBeGreaterThan(0)
114+
expect(wf.uses instanceof Set && wf.uses.size).toBeGreaterThan(0)
115+
})
116+
})

0 commit comments

Comments
 (0)