13 KiB
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 Forbidden401 Unauthorized
Most protected routes are correctly blocking users because the tests authenticate users without assigning required roles or permissions.
Example bad pattern:
$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:
$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:
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:
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:
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:
POST /api/v1/school-years/{schoolYear}/archive
But the controller method does not exist.
Required Fix
Add the method to:
app/Http/Controllers/Api/SchoolYears/SchoolYearController.php
Required method:
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:
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:
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:
table email_templates has no column named created_at
Fix:
$table->timestamps();
Or set $timestamps = false on the model if timestamps are not intended.
Preferred fix: add timestamps.
class_prep_adjustments
Error:
table class_prep_adjustments has no column named updated_at
Fix:
$table->timestamps();
late_slip_logs
Error:
NOT NULL constraint failed: late_slip_logs.printed_at
Fix one:
$table->timestamp('printed_at')->nullable();
or ensure every insert provides printed_at.
Preferred fix: make nullable.
additional_charges
Error:
NOT NULL constraint failed: additional_charges.title
Fix one:
- provide
titlein every creation path - or make
titlenullable/defaulted
Preferred fix: update service/test payloads to always provide title unless legacy data requires nullable.
payments
Error:
NOT NULL constraint failed: payments.number_of_installments
Fix:
$table->unsignedInteger('number_of_installments')->default(1);
Also ensure payment creation services pass a value when appropriate.
ip_attempts
Error:
NOT NULL constraint failed: ip_attempts.last_attempt_at
Fix:
$table->timestamp('last_attempt_at')->useCurrent();
or update all insert paths.
student_class
Error:
table student_class has no column named school_id
Fix one:
- add
school_idto the pivot table - or remove references to
school_id
Preferred fix: add school_id if school isolation is required.
semester_scores
Error:
NOT NULL constraint failed: semester_scores.school_id
Fix:
- provide
school_idin 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:
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:
DB_CONNECTION=sqlite
DB_DATABASE=/tmp/alrahma_testing.sqlite
Before tests:
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
RefreshDatabaseis used consistently
Priority
High
7. Update Unit Tests for Service Constructor Changes
Problem
Many unit tests manually instantiate services with outdated constructor arguments.
Examples:
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:
$service = new SomeService($oldArg1, $oldArg2);
with container resolution:
$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:
App\Services\ApplicationUrlService
because it is declared final.
Required Fix
Choose one:
- Remove
final - Extract an interface, for example:
interface ApplicationUrlProvider
- 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:
{
"ok": true
}
{
"status": true,
"message": "...",
"data": {}
}
Raw arrays are also returned in some places.
Required Fix
Standardize API v1 responses.
Recommended shape:
{
"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:
min_score
max_score
template_text
Old callers send:
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:
comments
Old callers send:
comment
Fix:
$comments = $request->input('comments', $request->input('comment'));
Below-60 grading
Current required field:
semester
Some callers only send:
school_year
Fix one:
- make
semesteroptional 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:
processing
Fix one:
- add
processingto 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:
permissions
Old callers send:
permission_ids
Fix:
- accept
permission_idsas 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:
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:
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:
400 Bad Request
when tests expect:
422 Unprocessable Entity
Required Fix
Use Laravel validation responses consistently.
Expected status rules:
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:
207 Multi-Status
where tests expect:
200 OK
Affected endpoint:
/api/v1/administrator/enrollment-withdrawal/update-statuses
Required Fix
Decide the correct contract:
- return
207only for partial success/failure - return
200if 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:
3000 users
but creates:
3001 users
Required Fix
Find the extra user:
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, or404
Then update controller/tests to match the intended behavior.
Priority
Low
16. Remove Insecure TLS Setting from CI
Problem
The log warns:
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:
- Authorization test helpers and permission contracts
- School-year admin route protection
- Missing
SchoolYearController@archive - Route coverage matrix for admin progress routes
- Database schema drift
- SQLite test database locking
- Service constructor test updates
- Final class mocking issue
- API response shape consistency
- Validation payload aliases and status codes
- Missing legacy route aliases
- Remaining workflow-specific failures
Main Root Causes
The test suite is failing because of these root causes:
- Authorization hardening changed route behavior
- Tests still create users without proper roles/permissions
- Backend routes and frontend/test expectations drifted apart
- Migrations and models are no longer aligned
- Service constructors changed but unit tests were not updated
- API response and validation contracts are inconsistent
- SQLite test database setup is unstable
Fixing those root causes should collapse most of the 352 failures without manually touching every failing test.