11name : Comprehensive Test Suite
22
3+ # Complements ci.yml (Node 20 + full-stack smoke gate) by proving the real
4+ # unit suites also pass on the minimum supported Node version, and by
5+ # validating the installer + deployment config. Runs daily so flakes and
6+ # dependency drift surface even without a push.
7+
38on :
49 push :
5- branches : [ main, develop, feature/* ]
10+ branches : [main, develop]
611 pull_request :
7- branches : [ main, develop ]
12+ branches : [main, develop]
813 schedule :
9- # Run daily at 2 AM UTC
10- - cron : ' 0 2 * * *'
14+ - cron : ' 0 2 * * *' # daily 02:00 UTC
1115 workflow_dispatch :
12- inputs :
13- environment :
14- description : ' Test environment'
15- required : true
16- default : ' staging'
17- type : choice
18- options :
19- - development
20- - staging
21- - production
2216
2317permissions :
2418 contents : read
25- issues : write
2619 pull-requests : write
27- pages : write
28- id-token : write
2920
3021jobs :
31- comprehensive-tests :
32- name : Run Comprehensive Tests
22+ # Real unit suites across the supported Node range (engines: >=18)
23+ unit-matrix :
24+ name : Unit Tests (Node ${{ matrix.node-version }})
3325 runs-on : ubuntu-latest
34- timeout-minutes : 30
35-
26+ timeout-minutes : 25
3627 strategy :
28+ fail-fast : false
3729 matrix :
38- node-version : [18.x, 20.x]
39-
30+ node-version : ['18.x', '20.x']
4031 steps :
41- - name : Checkout code
42- uses : actions/checkout@v4
43-
44- - name : Setup Node.js
45- uses : actions/setup-node@v4
32+ - uses : actions/checkout@v4
33+ - uses : actions/setup-node@v4
4634 with :
4735 node-version : ${{ matrix.node-version }}
4836 cache : ' npm'
49-
37+ # npm install (not ci) avoids the rollup optional-deps bug (npm/cli#4828)
5038 - name : Install dependencies
39+ run : npm install --legacy-peer-deps
40+ - name : Run all unit suites (core, web, server, mcp-server)
41+ run : npm run test
42+
43+ # Genuinely-real checks: the installer exists, deployment compose parses
44+ installer-and-deploy-config :
45+ name : Installer & Deploy Config
46+ runs-on : ubuntu-latest
47+ steps :
48+ - uses : actions/checkout@v4
49+ - name : Installation script present and executable
5150 run : |
52- echo "Installing dependencies..."
53- npm ci
54- echo "Dependencies installed successfully"
55-
56- - name : Run installation script tests
57- id : tests
51+ test -f public/install.sh || { echo "::error::public/install.sh missing"; exit 1; }
52+ test -x public/install.sh || echo "::warning::public/install.sh is not marked executable"
53+ echo "✅ installer present"
54+ - name : Production docker compose config is valid
5855 run : |
59- echo "🧪 Testing GraphDone installation script (PR #24)"
60-
61- # Create test results directory
62- mkdir -p test-results/reports
63-
64- # Create a simple test results file
65- cat > test-results/reports/results.json << 'EOF'
66- {
67- "totalTests": 5,
68- "passed": 5,
69- "failed": 0,
70- "duration": 1234,
71- "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
72- "suites": [
73- {
74- "name": "Installation Script Validation",
75- "status": "passed",
76- "passed": 1,
77- "failed": 0,
78- "duration": 100
79- },
80- {
81- "name": "Docker Compose Configuration",
82- "status": "passed",
83- "passed": 1,
84- "failed": 0,
85- "duration": 50
86- },
87- {
88- "name": "Node.js Dependencies",
89- "status": "passed",
90- "passed": 1,
91- "failed": 0,
92- "duration": 200
93- },
94- {
95- "name": "Certificate Generation Script",
96- "status": "passed",
97- "passed": 1,
98- "failed": 0,
99- "duration": 150
100- },
101- {
102- "name": "Environment Setup",
103- "status": "passed",
104- "passed": 1,
105- "failed": 0,
106- "duration": 100
107- }
108- ]
109- }
110- EOF
111-
112- # Validate the installation script exists and is executable
113- echo "✅ Checking installation script..."
114- if [ -f "public/install.sh" ]; then
115- echo " Installation script found at public/install.sh"
116- ls -la public/install.sh
117- else
118- echo " ❌ Installation script not found!"
119- exit 1
120- fi
121-
122- # Validate Docker Compose configuration
123- echo "✅ Validating Docker Compose configuration..."
124- if docker compose -f deployment/docker-compose.yml config > /dev/null 2>&1; then
125- echo " Docker Compose configuration is valid"
126- else
127- echo " ❌ Docker Compose configuration is invalid!"
128- exit 1
129- fi
130-
131- # Check package.json scripts
132- echo "✅ Checking package.json scripts..."
133- if npm run --silent | grep -q "test:installation"; then
134- echo " test:installation script found"
135- fi
136-
137- # Generate HTML report
138- cat > test-results/reports/index.html << 'EOF'
139- <!DOCTYPE html>
140- <html>
141- <head>
142- <title>GraphDone CI Test Results</title>
143- <style>
144- body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
145- .header { background: linear-gradient(135deg, #40e0d0 0%, #48d1cc 100%); color: white; padding: 20px; border-radius: 8px; }
146- h1 { margin: 0; }
147- .summary { background: white; padding: 20px; margin: 20px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
148- .passed { color: #28a745; font-weight: bold; }
149- .failed { color: #dc3545; font-weight: bold; }
150- table { width: 100%; border-collapse: collapse; background: white; }
151- th { background: #40e0d0; color: white; padding: 12px; text-align: left; }
152- td { padding: 12px; border-bottom: 1px solid #eee; }
153- tr:hover { background: #f9f9f9; }
154- </style>
155- </head>
156- <body>
157- <div class="header">
158- <h1>🧪 GraphDone Installation Script Test Results</h1>
159- <p>PR #24: One-line installation script validation</p>
160- </div>
161-
162- <div class="summary">
163- <h2>Summary</h2>
164- <p>Total Tests: <span class="passed">5</span></p>
165- <p>Passed: <span class="passed">5 ✅</span></p>
166- <p>Failed: <span class="failed">0</span></p>
167- <p>Duration: 0.6s</p>
168- </div>
169-
170- <table>
171- <tr><th>Test Suite</th><th>Status</th><th>Duration</th></tr>
172- <tr><td>Installation Script Validation</td><td class="passed">✅ Passed</td><td>100ms</td></tr>
173- <tr><td>Docker Compose Configuration</td><td class="passed">✅ Passed</td><td>50ms</td></tr>
174- <tr><td>Node.js Dependencies</td><td class="passed">✅ Passed</td><td>200ms</td></tr>
175- <tr><td>Certificate Generation Script</td><td class="passed">✅ Passed</td><td>150ms</td></tr>
176- <tr><td>Environment Setup</td><td class="passed">✅ Passed</td><td>100ms</td></tr>
177- </table>
178- </body>
179- </html>
180- EOF
181-
182- echo ""
183- echo "✅ All installation script tests passed!"
184- echo " Test results saved to test-results/reports/"
185-
186- - name : Upload test results
187- if : always()
188- uses : actions/upload-artifact@v4
189- with :
190- name : test-results-${{ matrix.node-version }}
191- path : test-results/
192-
193- - name : Upload HTML report
194- if : always()
195- uses : actions/upload-artifact@v4
196- with :
197- name : html-report-${{ matrix.node-version }}
198- path : test-results/reports/index.html
199-
200- - name : Comment PR with results
201- if : github.event_name == 'pull_request' && success()
56+ docker compose -f deployment/docker-compose.yml config > /dev/null \
57+ && echo "✅ deployment/docker-compose.yml is valid" \
58+ || { echo "::error::deployment/docker-compose.yml failed to parse"; exit 1; }
59+
60+ # Honest PR comment derived from real job outcomes (no fabricated numbers)
61+ report :
62+ name : Report
63+ runs-on : ubuntu-latest
64+ needs : [unit-matrix, installer-and-deploy-config]
65+ if : always() && github.event_name == 'pull_request'
66+ steps :
67+ - name : Comment PR with real results
20268 uses : actions/github-script@v7
20369 with :
20470 github-token : ${{ secrets.GITHUB_TOKEN }}
20571 script : |
206- const fs = require('fs');
207- const path = require('path');
208-
209- let resultsSummary = '## 🧪 Test Results\n\n';
210-
211- try {
212- const resultsPath = path.join(process.env.GITHUB_WORKSPACE, 'test-results/reports/results.json');
213- if (fs.existsSync(resultsPath)) {
214- const results = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
215-
216- resultsSummary += `### Summary\n`;
217- resultsSummary += `- **Total Tests**: ${results.totalTests}\n`;
218- resultsSummary += `- **Passed**: ${results.passed} ✅\n`;
219- resultsSummary += `- **Failed**: ${results.failed} ❌\n`;
220- resultsSummary += `- **Duration**: ${Math.round(results.duration / 1000)}s\n\n`;
221-
222- if (results.suites && results.suites.length > 0) {
223- resultsSummary += `### Test Suites\n`;
224- resultsSummary += `| Suite | Status | Passed | Failed | Duration |\n`;
225- resultsSummary += `|-------|--------|--------|--------|----------|\n`;
226-
227- results.suites.forEach(suite => {
228- const status = suite.status === 'passed' ? '✅' : '❌';
229- resultsSummary += `| ${suite.name} | ${status} | ${suite.passed} | ${suite.failed} | ${(suite.duration / 1000).toFixed(2)}s |\n`;
230- });
231- }
232-
233- resultsSummary += `\n### Installation Script Validation\n`;
234- resultsSummary += `- Script Location: ✅ public/install.sh\n`;
235- resultsSummary += `- Docker Config: ✅ Valid\n`;
236- resultsSummary += `- Dependencies: ✅ Installed\n`;
237- resultsSummary += `- Environment: ✅ Configured\n`;
238- } else {
239- resultsSummary += '⚠️ Test results file not found. This may indicate the tests did not complete.\n';
240- resultsSummary += 'Check the workflow logs for details.\n';
241- }
242- } catch (error) {
243- resultsSummary += `⚠️ Error reading test results: ${error.message}\n`;
244- resultsSummary += 'The tests may have encountered an issue. Check the workflow logs.\n';
245- }
246-
247- github.rest.issues.createComment({
72+ const unit = '${{ needs.unit-matrix.result }}';
73+ const cfg = '${{ needs.installer-and-deploy-config.result }}';
74+ const mark = (r) => r === 'success' ? '✅ passed' : (r === 'skipped' ? '⏭️ skipped' : '❌ ' + r);
75+ const body = [
76+ '## 🧪 Comprehensive Test Suite',
77+ '',
78+ `- **Unit suites (Node 18.x & 20.x)** — core, web, server, mcp-server: ${mark(unit)}`,
79+ `- **Installer & deploy config**: ${mark(cfg)}`,
80+ '',
81+ '_Full-stack smoke gate runs in the **CI** workflow._',
82+ ].join('\n');
83+ await github.rest.issues.createComment({
24884 issue_number: context.issue.number,
24985 owner: context.repo.owner,
25086 repo: context.repo.repo,
251- body: resultsSummary
252- });
87+ body,
88+ });
0 commit comments