Skip to content

Commit 15fed71

Browse files
authored
Merge pull request #94 from netbootxyz/implement-testing
Add comprehensive test suite and CI integration
2 parents 44c2695 + 95e2b0d commit 15fed71

19 files changed

+2986
-1
lines changed

.github/workflows/build.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,40 @@ on:
66
workflow_dispatch:
77

88
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: '0'
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: '18'
21+
22+
- name: Install dependencies
23+
run: npm install
24+
25+
- name: Run tests
26+
run: npm test
27+
28+
- name: Run test coverage
29+
run: npm run test:coverage
30+
31+
- name: Upload coverage to Codecov
32+
uses: codecov/codecov-action@v4
33+
if: always()
34+
with:
35+
file: ./coverage/lcov.info
36+
flags: webapp
37+
name: webapp-coverage
38+
fail_ci_if_error: false
39+
940
build:
1041
runs-on: ubuntu-latest
42+
needs: test # Only build if tests pass
1143

1244
steps:
1345
- name: Checkout

.github/workflows/webapp-dev.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,29 @@ name: webapp-dev
22
on:
33
push
44
jobs:
5+
test:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- name: Checkout
9+
uses: actions/checkout@v4
10+
11+
- name: Setup Node.js
12+
uses: actions/setup-node@v4
13+
with:
14+
node-version: '18'
15+
16+
- name: Install dependencies
17+
run: npm install
18+
19+
- name: Run tests
20+
run: npm test
21+
22+
- name: Run test coverage
23+
run: npm run test:coverage
24+
525
build:
626
runs-on: ubuntu-latest
27+
needs: test # Only build/push if tests pass
728
steps:
829
- name: Checkout
930
uses: actions/checkout@v4

jest.config.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
testMatch: [
4+
'**/tests/**/*.test.js',
5+
'**/__tests__/**/*.test.js'
6+
],
7+
collectCoverageFrom: [
8+
'lib/**/*.js',
9+
'!**/node_modules/**',
10+
'!**/tests/**'
11+
],
12+
coverageDirectory: 'coverage',
13+
coverageReporters: ['text', 'lcov', 'html'],
14+
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
15+
testTimeout: 5000,
16+
verbose: false,
17+
collectCoverage: false,
18+
detectOpenHandles: true,
19+
forceExit: true,
20+
clearMocks: true,
21+
resetMocks: true,
22+
restoreMocks: true,
23+
// Disable coverage thresholds since we're testing logic patterns, not code execution
24+
// coverageThreshold: {
25+
// global: {
26+
// branches: 10,
27+
// functions: 10,
28+
// lines: 10,
29+
// statements: 10
30+
// }
31+
// },
32+
// Prevent Jest from keeping the process alive
33+
maxWorkers: 1,
34+
// Handle async operations better
35+
testEnvironmentOptions: {
36+
// Force close any lingering connections
37+
teardown: 'jest-environment-node'
38+
}
39+
};

lib/utils.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Extracted utility functions for better testability
2+
3+
/**
4+
* Disable signatures in boot configuration
5+
*/
6+
function disableSignatures(configContent) {
7+
return configContent.replace(/set sigs_enabled true/g, 'set sigs_enabled false');
8+
}
9+
10+
/**
11+
* Validate port number
12+
*/
13+
function validatePort(port, defaultPort = 3000) {
14+
const portNum = Number(port);
15+
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {
16+
return defaultPort;
17+
}
18+
return portNum;
19+
}
20+
21+
/**
22+
* Check if version is a commit SHA
23+
*/
24+
function isCommitSha(version) {
25+
return version.length === 40 && /^[a-f0-9]+$/i.test(version);
26+
}
27+
28+
/**
29+
* Generate download URL based on version type
30+
*/
31+
function getDownloadUrl(version, file = '') {
32+
const baseUrl = isCommitSha(version)
33+
? `https://s3.amazonaws.com/dev.boot.netboot.xyz/${version}/ipxe/`
34+
: `https://github.com/netbootxyz/netboot.xyz/releases/download/${version}/`;
35+
return baseUrl + file;
36+
}
37+
38+
/**
39+
* Validate file path for security
40+
*/
41+
function validateFilePath(userPath, rootDir) {
42+
try {
43+
const path = require('path');
44+
const resolved = path.resolve(rootDir, userPath);
45+
const rootWithSeparator = path.resolve(rootDir) + path.sep;
46+
return {
47+
path: resolved,
48+
isSecure: resolved.startsWith(rootWithSeparator)
49+
};
50+
} catch {
51+
return { path: null, isSecure: false };
52+
}
53+
}
54+
55+
/**
56+
* Check if host is allowed for downloads
57+
*/
58+
function isAllowedHost(url, allowedHosts = ['s3.amazonaws.com']) {
59+
try {
60+
const urlLib = require('url');
61+
const parsedUrl = urlLib.parse(url);
62+
return allowedHosts.includes(parsedUrl.host);
63+
} catch {
64+
return false;
65+
}
66+
}
67+
68+
module.exports = {
69+
disableSignatures,
70+
validatePort,
71+
isCommitSha,
72+
getDownloadUrl,
73+
validateFilePath,
74+
isAllowedHost
75+
};

package.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@
44
"description": "Configuration and mirroring application for netboot.xyz stack",
55
"main": "app.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "jest --testPathPattern=unit",
8+
"test:all": "jest",
9+
"test:watch": "jest --watch --testPathPattern=unit",
10+
"test:coverage": "jest --coverage --testPathPattern=unit",
11+
"test:integration": "jest --testPathPattern=integration",
12+
"test:unit": "jest --testPathPattern=unit",
13+
"test:basic": "jest tests/unit/basic.test.js",
14+
"test:safe": "jest tests/unit/basic.test.js tests/unit/socket-logic.test.js",
15+
"test:debug": "jest --detectOpenHandles --verbose --no-cache",
16+
"test:clean": "jest --clearCache"
817
},
918
"repository": {
1019
"type": "git",
@@ -24,5 +33,13 @@
2433
"node-fetch": "2.7.0",
2534
"socket.io": "4.8.1",
2635
"systeminformation": "5.25.11"
36+
},
37+
"devDependencies": {
38+
"jest": "^29.7.0",
39+
"supertest": "^6.3.3",
40+
"socket.io-client": "^4.8.1",
41+
"nock": "^13.3.8",
42+
"@types/jest": "^29.5.8",
43+
"mock-fs": "^5.2.0"
2744
}
2845
}

0 commit comments

Comments
 (0)