fix test issues
API CI/CD / Validate (composer + pint) (push) Successful in 3m5s
API CI/CD / Test (PHPUnit) (push) Failing after 7m12s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 49s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
API CI/CD / Validate (composer + pint) (push) Successful in 3m5s
API CI/CD / Test (PHPUnit) (push) Failing after 7m12s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 49s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,836 @@
|
||||
# PHPUnit Failure Fix Summary
|
||||
|
||||
## Overview
|
||||
|
||||
The PHPUnit run failed with a large number of cascading failures, but the issues are concentrated in a few root causes.
|
||||
|
||||
Result summary:
|
||||
|
||||
- **679 tests passed**
|
||||
- **352 tests failed**
|
||||
- **2 tests risky**
|
||||
- Most failures are caused by authorization changes, missing routes, schema drift, stale test setup, and inconsistent API contracts.
|
||||
|
||||
Do **not** fix the 352 failures one by one. Fix the root causes below in order.
|
||||
|
||||
---
|
||||
|
||||
## 1. Fix Authorization and Permission Test Setup
|
||||
|
||||
### Problem
|
||||
|
||||
Many tests now fail with:
|
||||
|
||||
- `403 Forbidden`
|
||||
- `401 Unauthorized`
|
||||
|
||||
Most protected routes are correctly blocking users because the tests authenticate users without assigning required roles or permissions.
|
||||
|
||||
Example bad pattern:
|
||||
|
||||
```php
|
||||
$user = User::factory()->create();
|
||||
Sanctum::actingAs($user);
|
||||
```
|
||||
|
||||
This creates an authenticated user, but not an authorized admin.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Update test helpers to create proper users with roles and permissions.
|
||||
|
||||
Recommended helpers:
|
||||
|
||||
```php
|
||||
$this->actingAsApiAdministrator();
|
||||
$this->actingAsApiUserWithPermissions(['permission.name']);
|
||||
$this->actingAsParent();
|
||||
$this->actingAsTeacher();
|
||||
```
|
||||
|
||||
Then update affected feature tests to use the correct helper.
|
||||
|
||||
### Priority
|
||||
|
||||
**Critical**
|
||||
|
||||
This is the biggest failure cluster and must be fixed before chasing controller-level test failures.
|
||||
|
||||
---
|
||||
|
||||
## 2. Protect School Year Admin Routes Correctly
|
||||
|
||||
### Problem
|
||||
|
||||
The test expects non-admin users to be rejected from:
|
||||
|
||||
```http
|
||||
GET /api/v1/school-years
|
||||
```
|
||||
|
||||
But the route currently returns `200` for a non-admin user.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Separate public/current school-year routes from admin-only routes.
|
||||
|
||||
Recommended access model:
|
||||
|
||||
```text
|
||||
GET /api/v1/school-years -> admin only
|
||||
GET /api/v1/school-years/current -> authenticated users
|
||||
GET /api/v1/school-years/options -> authenticated users if frontend needs it
|
||||
```
|
||||
|
||||
Use a stricter permission for the index route, such as:
|
||||
|
||||
```text
|
||||
school_year.manage
|
||||
school_year.admin.view
|
||||
```
|
||||
|
||||
Do not let parent users list all school years unless that is intentional.
|
||||
|
||||
### Priority
|
||||
|
||||
**Critical**
|
||||
|
||||
---
|
||||
|
||||
## 3. Add Missing `SchoolYearController@archive`
|
||||
|
||||
### Problem
|
||||
|
||||
The route exists:
|
||||
|
||||
```http
|
||||
POST /api/v1/school-years/{schoolYear}/archive
|
||||
```
|
||||
|
||||
But the controller method does not exist.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Add the method to:
|
||||
|
||||
```text
|
||||
app/Http/Controllers/Api/SchoolYears/SchoolYearController.php
|
||||
```
|
||||
|
||||
Required method:
|
||||
|
||||
```php
|
||||
public function archive(int $schoolYear): JsonResponse
|
||||
```
|
||||
|
||||
Then either:
|
||||
|
||||
- implement real archive behavior, or
|
||||
- remove the route if archiving is no longer supported.
|
||||
|
||||
### Priority
|
||||
|
||||
**Critical**
|
||||
|
||||
---
|
||||
|
||||
## 4. Update API Route Coverage Matrix
|
||||
|
||||
### Problem
|
||||
|
||||
The route coverage tests report missing mapping for:
|
||||
|
||||
```http
|
||||
GET /api/v1/admin/progress
|
||||
GET /api/v1/admin/progress/meta
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Add both routes to the API use-case coverage matrix.
|
||||
|
||||
Assign them to the correct domain, likely:
|
||||
|
||||
```text
|
||||
Academic Progress
|
||||
Administrator Reporting
|
||||
```
|
||||
|
||||
Also update E2E workflow coverage if required by the test suite.
|
||||
|
||||
### Priority
|
||||
|
||||
**High**
|
||||
|
||||
---
|
||||
|
||||
## 5. Fix Database Schema Drift
|
||||
|
||||
### Problem
|
||||
|
||||
Several tests fail because models/services expect columns that migrations do not provide, or migrations require fields that services/tests do not send.
|
||||
|
||||
### Required Fixes
|
||||
|
||||
#### `email_templates`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
table email_templates has no column named created_at
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
```php
|
||||
$table->timestamps();
|
||||
```
|
||||
|
||||
Or set `$timestamps = false` on the model if timestamps are not intended.
|
||||
|
||||
Preferred fix: **add timestamps**.
|
||||
|
||||
---
|
||||
|
||||
#### `class_prep_adjustments`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
table class_prep_adjustments has no column named updated_at
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
```php
|
||||
$table->timestamps();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `late_slip_logs`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
NOT NULL constraint failed: late_slip_logs.printed_at
|
||||
```
|
||||
|
||||
Fix one:
|
||||
|
||||
```php
|
||||
$table->timestamp('printed_at')->nullable();
|
||||
```
|
||||
|
||||
or ensure every insert provides `printed_at`.
|
||||
|
||||
Preferred fix: **make nullable**.
|
||||
|
||||
---
|
||||
|
||||
#### `additional_charges`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
NOT NULL constraint failed: additional_charges.title
|
||||
```
|
||||
|
||||
Fix one:
|
||||
|
||||
- provide `title` in every creation path
|
||||
- or make `title` nullable/defaulted
|
||||
|
||||
Preferred fix: **update service/test payloads to always provide title** unless legacy data requires nullable.
|
||||
|
||||
---
|
||||
|
||||
#### `payments`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
NOT NULL constraint failed: payments.number_of_installments
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
```php
|
||||
$table->unsignedInteger('number_of_installments')->default(1);
|
||||
```
|
||||
|
||||
Also ensure payment creation services pass a value when appropriate.
|
||||
|
||||
---
|
||||
|
||||
#### `ip_attempts`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
NOT NULL constraint failed: ip_attempts.last_attempt_at
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
```php
|
||||
$table->timestamp('last_attempt_at')->useCurrent();
|
||||
```
|
||||
|
||||
or update all insert paths.
|
||||
|
||||
---
|
||||
|
||||
#### `student_class`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
table student_class has no column named school_id
|
||||
```
|
||||
|
||||
Fix one:
|
||||
|
||||
- add `school_id` to the pivot table
|
||||
- or remove references to `school_id`
|
||||
|
||||
Preferred fix: **add `school_id`** if school isolation is required.
|
||||
|
||||
---
|
||||
|
||||
#### `semester_scores`
|
||||
|
||||
Error:
|
||||
|
||||
```text
|
||||
NOT NULL constraint failed: semester_scores.school_id
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
- provide `school_id` in all score creation/update paths
|
||||
- or allow nullable for legacy records
|
||||
|
||||
Preferred fix: **provide `school_id` consistently**.
|
||||
|
||||
### Priority
|
||||
|
||||
**High**
|
||||
|
||||
---
|
||||
|
||||
## 6. Stabilize SQLite Test Database
|
||||
|
||||
### Problem
|
||||
|
||||
The log contains repeated:
|
||||
|
||||
```text
|
||||
SQLSTATE[HY000]: General error: 5 database is locked
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Do not share one SQLite file across many concurrent or transaction-heavy tests.
|
||||
|
||||
Recommended CI setup:
|
||||
|
||||
```env
|
||||
DB_CONNECTION=sqlite
|
||||
DB_DATABASE=/tmp/alrahma_testing.sqlite
|
||||
```
|
||||
|
||||
Before tests:
|
||||
|
||||
```bash
|
||||
rm -f /tmp/alrahma_testing.sqlite
|
||||
touch /tmp/alrahma_testing.sqlite
|
||||
php artisan migrate:fresh --env=testing
|
||||
```
|
||||
|
||||
Also check:
|
||||
|
||||
- no parallel tests share the same SQLite file
|
||||
- long transactions are not left open
|
||||
- `RefreshDatabase` is used consistently
|
||||
|
||||
### Priority
|
||||
|
||||
**High**
|
||||
|
||||
---
|
||||
|
||||
## 7. Update Unit Tests for Service Constructor Changes
|
||||
|
||||
### Problem
|
||||
|
||||
Many unit tests manually instantiate services with outdated constructor arguments.
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
AdministratorAbsenceService expects 6 arguments, test passes 5
|
||||
TeacherSubmissionNotificationService expects 3 arguments, test passes 2
|
||||
ClassProgressMutationService expects 4 arguments, test passes 2
|
||||
DiscountInvoiceService expects 1 argument, test passes 0
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Replace manual construction:
|
||||
|
||||
```php
|
||||
$service = new SomeService($oldArg1, $oldArg2);
|
||||
```
|
||||
|
||||
with container resolution:
|
||||
|
||||
```php
|
||||
$service = app(SomeService::class);
|
||||
```
|
||||
|
||||
If mocks are needed, bind them into the container before resolving the service.
|
||||
|
||||
### Priority
|
||||
|
||||
**High**
|
||||
|
||||
---
|
||||
|
||||
## 8. Fix Final Class Mocking
|
||||
|
||||
### Problem
|
||||
|
||||
This class cannot be mocked:
|
||||
|
||||
```text
|
||||
App\Services\ApplicationUrlService
|
||||
```
|
||||
|
||||
because it is declared `final`.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Choose one:
|
||||
|
||||
1. Remove `final`
|
||||
2. Extract an interface, for example:
|
||||
|
||||
```php
|
||||
interface ApplicationUrlProvider
|
||||
```
|
||||
|
||||
3. Use a real instance in tests instead of mocking it
|
||||
|
||||
Preferred fix: **extract an interface** if the service should remain final.
|
||||
|
||||
### Priority
|
||||
|
||||
**Medium**
|
||||
|
||||
---
|
||||
|
||||
## 9. Standardize API Response Shape
|
||||
|
||||
### Problem
|
||||
|
||||
Some tests expect one response shape while controllers return another.
|
||||
|
||||
Examples of inconsistent shapes:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": true,
|
||||
"message": "...",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Raw arrays are also returned in some places.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Standardize API v1 responses.
|
||||
|
||||
Recommended shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": true,
|
||||
"message": "Success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Then update:
|
||||
|
||||
- controllers
|
||||
- API resources
|
||||
- frontend API clients
|
||||
- tests
|
||||
|
||||
### Priority
|
||||
|
||||
**Medium**
|
||||
|
||||
---
|
||||
|
||||
## 10. Fix Validation Contract Drift
|
||||
|
||||
### Problem
|
||||
|
||||
Several controllers now require fields that old tests/frontend callers do not send.
|
||||
|
||||
### Required Fixes
|
||||
|
||||
#### Attendance comment templates
|
||||
|
||||
Current required fields:
|
||||
|
||||
```text
|
||||
min_score
|
||||
max_score
|
||||
template_text
|
||||
```
|
||||
|
||||
Old callers send:
|
||||
|
||||
```text
|
||||
comment
|
||||
type
|
||||
is_active
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
- support legacy aliases, or
|
||||
- update callers/tests to the new payload
|
||||
|
||||
Preferred fix: **support aliases temporarily and migrate frontend/tests**.
|
||||
|
||||
---
|
||||
|
||||
#### Score comments
|
||||
|
||||
Current required field:
|
||||
|
||||
```text
|
||||
comments
|
||||
```
|
||||
|
||||
Old callers send:
|
||||
|
||||
```text
|
||||
comment
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
```php
|
||||
$comments = $request->input('comments', $request->input('comment'));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### Below-60 grading
|
||||
|
||||
Current required field:
|
||||
|
||||
```text
|
||||
semester
|
||||
```
|
||||
|
||||
Some callers only send:
|
||||
|
||||
```text
|
||||
school_year
|
||||
```
|
||||
|
||||
Fix one:
|
||||
|
||||
- make `semester` optional and infer current semester
|
||||
- or update callers/tests to always send `semester`
|
||||
|
||||
Preferred fix: **frontend/tests should send semester explicitly**.
|
||||
|
||||
---
|
||||
|
||||
#### Print request status
|
||||
|
||||
Current validation rejects:
|
||||
|
||||
```text
|
||||
processing
|
||||
```
|
||||
|
||||
Fix one:
|
||||
|
||||
- add `processing` to allowed statuses
|
||||
- or update callers/tests to use the valid status value
|
||||
|
||||
Preferred fix: **allow `processing`** if it exists in the real workflow.
|
||||
|
||||
---
|
||||
|
||||
#### Role permissions
|
||||
|
||||
Current required field:
|
||||
|
||||
```text
|
||||
permissions
|
||||
```
|
||||
|
||||
Old callers send:
|
||||
|
||||
```text
|
||||
permission_ids
|
||||
```
|
||||
|
||||
Fix:
|
||||
|
||||
- accept `permission_ids` as alias
|
||||
- or update frontend/tests
|
||||
|
||||
Preferred fix: **accept both**.
|
||||
|
||||
### Priority
|
||||
|
||||
**Medium**
|
||||
|
||||
---
|
||||
|
||||
## 11. Add Missing Legacy Route Aliases
|
||||
|
||||
### Problem
|
||||
|
||||
Some tests/frontend paths call legacy endpoints that now return `404`.
|
||||
|
||||
Examples:
|
||||
|
||||
```http
|
||||
GET /api/v1/configuration-admin
|
||||
POST /api/v1/configuration-admin
|
||||
GET /api/v1/attendance-tracking/compose
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Either:
|
||||
|
||||
- add compatibility aliases to the current controllers, or
|
||||
- update frontend/tests to the new endpoints
|
||||
|
||||
Recommended short-term fix:
|
||||
|
||||
```php
|
||||
Route::get('/configuration-admin', ...);
|
||||
Route::post('/configuration-admin', ...);
|
||||
Route::get('/attendance-tracking/compose', ...);
|
||||
```
|
||||
|
||||
Long-term fix:
|
||||
|
||||
- remove legacy aliases only after frontend and tests fully migrate.
|
||||
|
||||
### Priority
|
||||
|
||||
**Medium**
|
||||
|
||||
---
|
||||
|
||||
## 12. Normalize Validation Status Codes
|
||||
|
||||
### Problem
|
||||
|
||||
Some validation failures return:
|
||||
|
||||
```http
|
||||
400 Bad Request
|
||||
```
|
||||
|
||||
when tests expect:
|
||||
|
||||
```http
|
||||
422 Unprocessable Entity
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Use Laravel validation responses consistently.
|
||||
|
||||
Expected status rules:
|
||||
|
||||
```text
|
||||
400 -> malformed request
|
||||
401 -> unauthenticated
|
||||
403 -> authenticated but forbidden
|
||||
404 -> missing resource
|
||||
422 -> validation error
|
||||
```
|
||||
|
||||
### Priority
|
||||
|
||||
**Medium**
|
||||
|
||||
---
|
||||
|
||||
## 13. Review Enrollment Bulk Status Endpoint
|
||||
|
||||
### Problem
|
||||
|
||||
Endpoint returns:
|
||||
|
||||
```http
|
||||
207 Multi-Status
|
||||
```
|
||||
|
||||
where tests expect:
|
||||
|
||||
```http
|
||||
200 OK
|
||||
```
|
||||
|
||||
Affected endpoint:
|
||||
|
||||
```http
|
||||
/api/v1/administrator/enrollment-withdrawal/update-statuses
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Decide the correct contract:
|
||||
|
||||
- return `207` only for partial success/failure
|
||||
- return `200` if the operation is all-or-nothing
|
||||
|
||||
Then update either the controller or the tests.
|
||||
|
||||
### Priority
|
||||
|
||||
**Low to Medium**
|
||||
|
||||
---
|
||||
|
||||
## 14. Fix Bulk User Creation Count
|
||||
|
||||
### Problem
|
||||
|
||||
Bulk user creation expects:
|
||||
|
||||
```text
|
||||
3000 users
|
||||
```
|
||||
|
||||
but creates:
|
||||
|
||||
```text
|
||||
3001 users
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Find the extra user:
|
||||
|
||||
```php
|
||||
User::where('email', 'like', 'bulk_api_%')
|
||||
->pluck('email')
|
||||
->countBy();
|
||||
```
|
||||
|
||||
Check whether the extra user comes from:
|
||||
|
||||
- test setup
|
||||
- authenticated admin fixture
|
||||
- duplicate creation retry
|
||||
- linked/secondary user creation
|
||||
- email prefix overlap
|
||||
|
||||
### Priority
|
||||
|
||||
**Low**
|
||||
|
||||
---
|
||||
|
||||
## 15. Fix Public Token / Time-Off Route Tests
|
||||
|
||||
### Problem
|
||||
|
||||
Invalid token route returns a `400` HTML response.
|
||||
|
||||
### Required Fix
|
||||
|
||||
Review the public time-off token contract:
|
||||
|
||||
- expected token format
|
||||
- expiry behavior
|
||||
- invalid token response
|
||||
- whether response should be HTML, JSON, redirect, `400`, or `404`
|
||||
|
||||
Then update controller/tests to match the intended behavior.
|
||||
|
||||
### Priority
|
||||
|
||||
**Low**
|
||||
|
||||
---
|
||||
|
||||
## 16. Remove Insecure TLS Setting from CI
|
||||
|
||||
### Problem
|
||||
|
||||
The log warns:
|
||||
|
||||
```text
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0 makes TLS connections insecure
|
||||
```
|
||||
|
||||
### Required Fix
|
||||
|
||||
Remove this setting from CI.
|
||||
|
||||
If the runner needs to trust an internal/self-signed certificate, install the internal CA certificate properly instead.
|
||||
|
||||
### Priority
|
||||
|
||||
**Low but important for security**
|
||||
|
||||
---
|
||||
|
||||
# Recommended Fix Order
|
||||
|
||||
Fix in this order:
|
||||
|
||||
1. Authorization test helpers and permission contracts
|
||||
2. School-year admin route protection
|
||||
3. Missing `SchoolYearController@archive`
|
||||
4. Route coverage matrix for admin progress routes
|
||||
5. Database schema drift
|
||||
6. SQLite test database locking
|
||||
7. Service constructor test updates
|
||||
8. Final class mocking issue
|
||||
9. API response shape consistency
|
||||
10. Validation payload aliases and status codes
|
||||
11. Missing legacy route aliases
|
||||
12. Remaining workflow-specific failures
|
||||
|
||||
---
|
||||
|
||||
# Main Root Causes
|
||||
|
||||
The test suite is failing because of these root causes:
|
||||
|
||||
1. **Authorization hardening changed route behavior**
|
||||
2. **Tests still create users without proper roles/permissions**
|
||||
3. **Backend routes and frontend/test expectations drifted apart**
|
||||
4. **Migrations and models are no longer aligned**
|
||||
5. **Service constructors changed but unit tests were not updated**
|
||||
6. **API response and validation contracts are inconsistent**
|
||||
7. **SQLite test database setup is unstable**
|
||||
|
||||
Fixing those root causes should collapse most of the 352 failures without manually touching every failing test.
|
||||
Reference in New Issue
Block a user