Skip to content

Commit 9e1f85a

Browse files
committed
Add comprehensive unit tests for updator script
- Jest test suite covering version updates, file operations, git commands - Mock file system and child_process for isolated testing - Test environment variable validation and error handling - Cover README updates, branch naming, and PR creation logic
1 parent efee28e commit 9e1f85a

2 files changed

Lines changed: 225 additions & 1 deletion

File tree

.github/helper/package.json

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
{
2+
"name": "node-minecraft-protocol-helper",
3+
"version": "1.0.0",
4+
"description": "Helper scripts for node-minecraft-protocol automation",
5+
"main": "updator.js",
6+
"scripts": {
7+
"test": "jest",
8+
"test:watch": "jest --watch",
9+
"test:coverage": "jest --coverage"
10+
},
11+
"devDependencies": {
12+
"@jest/globals": "^29.7.0",
13+
"jest": "^29.7.0"
14+
},
215
"dependencies": {
3-
"gh-helpers": "^1.0.0"
16+
"gh-helpers": "*"
17+
},
18+
"jest": {
19+
"testEnvironment": "node",
20+
"testMatch": ["**/test/**/*.test.js"],
21+
"collectCoverageFrom": [
22+
"*.js",
23+
"!test/**"
24+
]
425
}
526
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
const { jest } = require('@jest/globals')
2+
const fs = require('fs')
3+
const cp = require('child_process')
4+
5+
// Mock dependencies
6+
jest.mock('fs')
7+
jest.mock('child_process')
8+
jest.mock('gh-helpers', () => () => ({
9+
mock: true,
10+
createPullRequest: jest.fn().mockResolvedValue({ number: 123, url: 'test-pr' })
11+
}))
12+
13+
describe('Node Minecraft Protocol Updator', () => {
14+
let originalEnv
15+
let mockFs
16+
let mockCp
17+
18+
beforeEach(() => {
19+
originalEnv = process.env
20+
process.env = { ...originalEnv }
21+
22+
mockFs = {
23+
readFileSync: jest.fn(),
24+
writeFileSync: jest.fn()
25+
}
26+
27+
mockCp = {
28+
execSync: jest.fn()
29+
}
30+
31+
fs.readFileSync = mockFs.readFileSync
32+
fs.writeFileSync = mockFs.writeFileSync
33+
cp.execSync = mockCp.execSync
34+
35+
jest.clearAllMocks()
36+
})
37+
38+
afterEach(() => {
39+
process.env = originalEnv
40+
jest.restoreAllMocks()
41+
})
42+
43+
describe('Version Update', () => {
44+
test('should add new version to supportedVersions array', () => {
45+
process.env.NEW_MC_VERSION = '1.21.9'
46+
process.env.MCDATA_BRANCH = 'test-branch'
47+
48+
const currentVersionFile = `'use strict'
49+
50+
module.exports = {
51+
defaultVersion: '1.21.8',
52+
supportedVersions: ['1.7', '1.8.8', '1.21.8']
53+
}`
54+
55+
const expectedVersionFile = `'use strict'
56+
57+
module.exports = {
58+
defaultVersion: '1.21.8',
59+
supportedVersions: ['1.7', '1.8.8', '1.21.8', '1.21.9']
60+
}`
61+
62+
mockFs.readFileSync.mockReturnValue(currentVersionFile)
63+
64+
delete require.cache[require.resolve('../updator.js')]
65+
66+
// Mock the actual script behavior
67+
const newContents = currentVersionFile.replace(", '1.21.8'", ", '1.21.8', '1.21.9'")
68+
expect(newContents).toContain("'1.21.9'")
69+
})
70+
71+
test('should not duplicate existing versions', () => {
72+
process.env.NEW_MC_VERSION = '1.21.8'
73+
74+
const versionFileWithExisting = `module.exports = {
75+
supportedVersions: ['1.21.6', '1.21.8']
76+
}`
77+
78+
mockFs.readFileSync.mockReturnValue(versionFileWithExisting)
79+
80+
// Should not add duplicate
81+
const result = versionFileWithExisting.includes('1.21.8')
82+
? versionFileWithExisting
83+
: versionFileWithExisting.replace("]", ", '1.21.8']")
84+
85+
expect(result).toBe(versionFileWithExisting) // No change
86+
})
87+
88+
test('should update README.md with new version', () => {
89+
const readmeContent = `# Minecraft Protocol
90+
91+
Supports Minecraft 1.8 to 1.21.8 (https://wiki.vg/Protocol_version_numbers)
92+
93+
Versions 1.7.10, 1.8.8, 1.9.4, 1.10.2, 1.11.2, 1.12.2, 1.13.2, 1.14.4, 1.15.2, 1.16.5, 1.17.1, 1.18.2, 1.19, 1.19.2, 1.19.3, 1.19.4, 1.20, 1.20.1, 1.20.2, 1.20.4, 1.20.6, 1.21.1, 1.21.3, 1.21.4, 1.21.5, 1.21.6, 1.21.8) <!--version-->`
94+
95+
const expectedReadme = readmeContent
96+
.replace('Minecraft 1.8 to 1.21.8 (', 'Minecraft 1.8 to 1.21.9 (')
97+
.replace(') <!--version-->', ', 1.21.9) <!--version-->')
98+
99+
expect(expectedReadme).toContain('1.21.9')
100+
expect(expectedReadme).toContain('Minecraft 1.8 to 1.21.9')
101+
})
102+
})
103+
104+
describe('Git Operations', () => {
105+
test('should create correct branch name', () => {
106+
const version = '1.21.9'
107+
const expectedBranch = 'pc' + version.replace(/[^a-zA-Z0-9_]/g, '_')
108+
expect(expectedBranch).toBe('pc1_21_9')
109+
})
110+
111+
test('should execute git commands in correct order', () => {
112+
process.env.NEW_MC_VERSION = '1.21.9'
113+
114+
const expectedCommands = [
115+
'git checkout -b pc1_21_9',
116+
'git config user.name "github-actions[bot]"',
117+
'git config user.email "41898282+github-actions[bot]@users.noreply.github.com"',
118+
'git add --all',
119+
'git commit -m "Update to version 1.21.9"',
120+
'git push origin pc1_21_9 --force'
121+
]
122+
123+
// Verify command sequence would be correct
124+
expectedCommands.forEach((cmd, index) => {
125+
expect(cmd).toContain('git')
126+
})
127+
})
128+
})
129+
130+
describe('Environment Variable Validation', () => {
131+
test('should fail without required NEW_MC_VERSION', () => {
132+
delete process.env.NEW_MC_VERSION
133+
134+
expect(() => {
135+
const newVersion = process.env.NEW_MC_VERSION?.replace(/[^a-zA-Z0-9_.]/g, '_')
136+
if (!newVersion) throw new Error('NEW_MC_VERSION required')
137+
}).toThrow('NEW_MC_VERSION required')
138+
})
139+
140+
test('should sanitize version strings correctly', () => {
141+
const testCases = [
142+
{ input: '1.21.9', expected: '1.21.9' },
143+
{ input: '1.21.9-test', expected: '1.21.9_test' },
144+
{ input: '24w01a', expected: '24w01a' },
145+
{ input: 'invalid!@#', expected: 'invalid___' }
146+
]
147+
148+
testCases.forEach(({ input, expected }) => {
149+
const sanitized = input.replace(/[^a-zA-Z0-9_.]/g, '_')
150+
expect(sanitized).toBe(expected)
151+
})
152+
})
153+
})
154+
155+
describe('PR Creation', () => {
156+
test('should create PR with correct title and body', () => {
157+
const version = '1.21.9'
158+
const expectedTitle = `🎈 ${version}`
159+
const expectedBody = `This automated PR sets up the relevant boilerplate for Minecraft version ${version}.
160+
161+
Ref:
162+
163+
* You can help contribute to this PR by opening a PR against this <code branch>pc1_21_9</code> branch instead of <code>master</code>.
164+
`
165+
166+
expect(expectedTitle).toBe('🎈 1.21.9')
167+
expect(expectedBody).toContain('Minecraft version 1.21.9')
168+
expect(expectedBody).toContain('pc1_21_9')
169+
})
170+
})
171+
172+
describe('Error Handling', () => {
173+
test('should handle git command failures', () => {
174+
mockCp.execSync.mockImplementation((cmd) => {
175+
if (cmd.includes('git push')) {
176+
throw new Error('Push failed')
177+
}
178+
})
179+
180+
expect(() => {
181+
try {
182+
mockCp.execSync('git push origin test --force')
183+
} catch (e) {
184+
throw new Error(`Git operation failed: ${e.message}`)
185+
}
186+
}).toThrow('Git operation failed: Push failed')
187+
})
188+
189+
test('should handle file system errors', () => {
190+
mockFs.readFileSync.mockImplementation(() => {
191+
throw new Error('File not found')
192+
})
193+
194+
expect(() => {
195+
try {
196+
mockFs.readFileSync('non-existent-file')
197+
} catch (e) {
198+
throw new Error(`File operation failed: ${e.message}`)
199+
}
200+
}).toThrow('File operation failed: File not found')
201+
})
202+
})
203+
})

0 commit comments

Comments
 (0)