1
+ name : Tests
2
+
3
+ on :
4
+ push :
5
+ branches : [ "main", "develop" ]
6
+ pull_request :
7
+ branches : [ "main", "develop" ]
8
+
9
+ permissions :
10
+ contents : read
11
+ pull-requests : write
12
+ issues : write
13
+
14
+ jobs :
15
+ tests :
16
+ name : Tests (PHP ${{ matrix.php }})
17
+ runs-on : ubuntu-latest
18
+
19
+ strategy :
20
+ fail-fast : false
21
+ matrix :
22
+ php : ['8.4']
23
+
24
+ steps :
25
+ - name : Checkout code
26
+ uses : actions/checkout@v4
27
+
28
+ - name : Setup PHP
29
+ uses : shivammathur/setup-php@v2
30
+ with :
31
+ php-version : ${{ matrix.php }}
32
+ extensions : mbstring, hash, sodium
33
+ coverage : xdebug
34
+
35
+ - name : Install dependencies
36
+ uses : ramsey/composer-install@v3
37
+
38
+ - name : Check code style
39
+ run : composer lint
40
+ continue-on-error : true
41
+ id : lint
42
+
43
+ - name : Run tests with coverage
44
+ run : ./vendor/bin/phpunit --coverage-clover=coverage/clover.xml --coverage-html=coverage/html --coverage-text
45
+ continue-on-error : true
46
+ id : tests
47
+
48
+ - name : Check coverage percentage
49
+ id : coverage
50
+ run : |
51
+ if [ -f coverage/clover.xml ]; then
52
+ COVERAGE=$(php -r "
53
+ \$xml = simplexml_load_file('coverage/clover.xml');
54
+ \$metrics = \$xml->project->metrics;
55
+ \$statements = (float)\$metrics['statements'];
56
+ \$coveredstatements = (float)\$metrics['coveredstatements'];
57
+ if (\$statements > 0) {
58
+ \$percentage = (\$coveredstatements / \$statements) * 100;
59
+ echo round(\$percentage, 2);
60
+ } else {
61
+ echo '0';
62
+ }
63
+ ")
64
+ echo "percentage=$COVERAGE" >> $GITHUB_OUTPUT
65
+ echo "Coverage: $COVERAGE%"
66
+
67
+ if (( $(echo "$COVERAGE < 100" | bc -l) )); then
68
+ echo "coverage_failed=true" >> $GITHUB_OUTPUT
69
+ echo "❌ Coverage requirement not met: $COVERAGE% (required: 100%)"
70
+ exit 1
71
+ else
72
+ echo "coverage_failed=false" >> $GITHUB_OUTPUT
73
+ echo "✅ Coverage requirement met: $COVERAGE%"
74
+ fi
75
+ else
76
+ echo "percentage=0" >> $GITHUB_OUTPUT
77
+ echo "coverage_failed=true" >> $GITHUB_OUTPUT
78
+ echo "❌ Coverage report not found"
79
+ exit 1
80
+ fi
81
+
82
+ - name : Comment on PR (Failure)
83
+ if : failure() && github.event_name == 'pull_request'
84
+ uses : actions/github-script@v7
85
+ with :
86
+ script : |
87
+ const fs = require('fs');
88
+
89
+ let lintStatus = '${{ steps.lint.outcome }}';
90
+ let testStatus = '${{ steps.tests.outcome }}';
91
+ let coveragePercentage = '${{ steps.coverage.outputs.percentage }}';
92
+ let coverageFailed = '${{ steps.coverage.outputs.coverage_failed }}';
93
+
94
+ let message = `## 🔍 Test Results - PHP ${{ matrix.php }}\n\n`;
95
+
96
+ if (lintStatus === 'failure') {
97
+ message += `❌ **Code Style Check Failed**\n\n`;
98
+ message += `Code style violations found. Please run \`composer fix\` to automatically fix them.\n\n`;
99
+ } else {
100
+ message += `✅ **Code Style Check Passed**\n\n`;
101
+ }
102
+
103
+ if (testStatus === 'failure') {
104
+ message += `❌ **Tests Failed**\n\n`;
105
+ message += `Some tests are failing. Please check the [workflow run](${context.payload.pull_request.html_url}/checks) for details.\n\n`;
106
+ } else {
107
+ message += `✅ **Tests Passed**\n\n`;
108
+ }
109
+
110
+ if (coverageFailed === 'true') {
111
+ message += `❌ **Code Coverage: ${coveragePercentage}%** (Required: 100%)\n\n`;
112
+ message += `Code coverage requirement not met. Please add tests to achieve 100% coverage.\n\n`;
113
+ } else {
114
+ message += `✅ **Code Coverage: ${coveragePercentage}%**\n\n`;
115
+ }
116
+
117
+ message += `---\n*This comment was automatically generated by the test workflow.*`;
118
+
119
+ // Check if we already commented on this PR
120
+ const comments = await github.rest.issues.listComments({
121
+ owner: context.repo.owner,
122
+ repo: context.repo.repo,
123
+ issue_number: context.issue.number,
124
+ });
125
+
126
+ const botComment = comments.data.find(comment =>
127
+ comment.user.login === 'github-actions[bot]' &&
128
+ comment.body.includes('Test Results - PHP ${{ matrix.php }}')
129
+ );
130
+
131
+ if (botComment) {
132
+ // Update existing comment
133
+ await github.rest.issues.updateComment({
134
+ owner: context.repo.owner,
135
+ repo: context.repo.repo,
136
+ comment_id: botComment.id,
137
+ body: message
138
+ });
139
+ } else {
140
+ // Create new comment
141
+ await github.rest.issues.createComment({
142
+ owner: context.repo.owner,
143
+ repo: context.repo.repo,
144
+ issue_number: context.issue.number,
145
+ body: message
146
+ });
147
+ }
148
+
149
+ - name : Upload coverage reports
150
+ if : always()
151
+ uses : actions/upload-artifact@v4
152
+ with :
153
+ name : coverage-reports-php${{ matrix.php }}
154
+ path : coverage/
155
+ retention-days : 30
156
+
157
+ - name : Fail on lint errors
158
+ if : steps.lint.outcome == 'failure'
159
+ run : |
160
+ echo "❌ Build failed due to code style violations"
161
+ exit 1
162
+
163
+ - name : Fail on coverage < 100%
164
+ if : steps.coverage.outputs.coverage_failed == 'true'
165
+ run : |
166
+ echo "❌ Build failed due to insufficient code coverage: ${{ steps.coverage.outputs.percentage }}%"
167
+ exit 1
0 commit comments