39228168c8
API CI/CD / Validate (composer + pint) (push) Successful in 3m14s
API CI/CD / Test (PHPUnit) (push) Failing after 3m28s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 56s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
202 lines
7.0 KiB
PHP
202 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Concerns;
|
|
|
|
use App\Models\Configuration;
|
|
use App\Models\Role;
|
|
use App\Models\SchoolYear;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Laravel\Sanctum\Sanctum;
|
|
|
|
trait CreatesApiTestUsers
|
|
{
|
|
protected function seedApiTestConfig(string $schoolYear = '2025-2026', string $semester = 'Fall'): void
|
|
{
|
|
if (class_exists(Configuration::class)) {
|
|
Configuration::query()->updateOrCreate(['config_key' => 'school_year'], ['config_value' => $schoolYear]);
|
|
Configuration::query()->updateOrCreate(['config_key' => 'semester'], ['config_value' => $semester]);
|
|
}
|
|
|
|
if (class_exists(SchoolYear::class)) {
|
|
SchoolYear::query()->updateOrCreate(
|
|
['name' => $schoolYear],
|
|
[
|
|
'start_date' => '2025-09-01',
|
|
'end_date' => '2026-06-30',
|
|
'status' => SchoolYear::STATUS_ACTIVE,
|
|
'is_current' => true,
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<string, Role>
|
|
*/
|
|
protected function seedApiRoles(): array
|
|
{
|
|
$roles = [
|
|
'administrator' => ['priority' => 1, 'dashboard_route' => 'administrator/administratordashboard'],
|
|
'admin' => ['priority' => 2, 'dashboard_route' => 'administrator/administratordashboard'],
|
|
'teacher' => ['priority' => 3, 'dashboard_route' => 'teacher/classes'],
|
|
'parent' => ['priority' => 4, 'dashboard_route' => 'parents/profile'],
|
|
'student' => ['priority' => 5, 'dashboard_route' => 'student/dashboard'],
|
|
];
|
|
|
|
$created = [];
|
|
foreach ($roles as $name => $attributes) {
|
|
$created[$name] = Role::query()->updateOrCreate(
|
|
['name' => $name],
|
|
[
|
|
'slug' => $name,
|
|
'description' => ucfirst($name),
|
|
'priority' => $attributes['priority'],
|
|
'dashboard_route' => $attributes['dashboard_route'],
|
|
'is_active' => 1,
|
|
]
|
|
);
|
|
}
|
|
|
|
return $created;
|
|
}
|
|
|
|
protected function createApiUserWithRole(string $roleName = 'administrator', array $overrides = []): User
|
|
{
|
|
$roles = $this->seedApiRoles();
|
|
$this->seedApiTestConfig();
|
|
$this->seedSchoolYearClosurePermissionsForTests($roles);
|
|
|
|
$user = User::factory()->create(array_merge([
|
|
'status' => 'Active',
|
|
'is_verified' => 1,
|
|
'is_suspended' => 0,
|
|
'address_street' => '1 Test Street',
|
|
'city' => 'Brooklyn',
|
|
'state' => 'NY',
|
|
'zip' => '11201',
|
|
'email' => $roleName.'-api-test-'.str_replace('.', '', uniqid('', true)).'@example.test',
|
|
], $overrides));
|
|
|
|
$role = $roles[$roleName] ?? Role::query()->where('name', $roleName)->firstOrFail();
|
|
$user->roles()->syncWithoutDetaching([$role->id]);
|
|
|
|
return $user->fresh() ?? $user;
|
|
}
|
|
|
|
protected function actingAsApiAdministrator(): User
|
|
{
|
|
$user = $this->createApiUserWithRole('administrator');
|
|
$this->actingAs($user, 'api');
|
|
Sanctum::actingAs($user);
|
|
|
|
return $user;
|
|
}
|
|
|
|
|
|
/**
|
|
* Keep test fixtures aligned with explicit permission middleware.
|
|
* Production receives these through the closure permission migration.
|
|
*
|
|
* @param array<string, Role> $roles
|
|
*/
|
|
protected function seedSchoolYearClosurePermissionsForTests(array $roles): void
|
|
{
|
|
if (! Schema::hasTable('permissions') || ! Schema::hasTable('role_permissions')) {
|
|
return;
|
|
}
|
|
|
|
$permissions = [
|
|
'school_year.view',
|
|
'school_year.view_closed',
|
|
'school_year.create',
|
|
'school_year.close',
|
|
'school_year.reopen',
|
|
'school_year.archive',
|
|
'school_year.manage',
|
|
'school_year.select',
|
|
'student_enrollment.view',
|
|
'student_enrollment.promote',
|
|
'finance.view',
|
|
'finance.balance_transfer.view',
|
|
'reports.view',
|
|
];
|
|
|
|
foreach ($permissions as $permission) {
|
|
DB::table('permissions')->updateOrInsert(
|
|
['name' => $permission],
|
|
['description' => 'Test permission '.$permission, 'created_at' => now(), 'updated_at' => now()]
|
|
);
|
|
}
|
|
|
|
$adminRoleIds = collect([$roles['administrator'] ?? null, $roles['admin'] ?? null])->filter()->map(fn (Role $role) => (int) $role->id)->all();
|
|
$teacherRoleId = isset($roles['teacher']) ? (int) $roles['teacher']->id : null;
|
|
$parentRoleId = isset($roles['parent']) ? (int) $roles['parent']->id : null;
|
|
|
|
$this->grantTestPermissions($adminRoleIds, $permissions);
|
|
if ($teacherRoleId) {
|
|
$this->grantTestPermissions([$teacherRoleId], ['school_year.view', 'school_year.view_closed', 'school_year.select', 'student_enrollment.view']);
|
|
}
|
|
if ($parentRoleId) {
|
|
$this->grantTestPermissions([$parentRoleId], ['school_year.view', 'school_year.select', 'finance.view']);
|
|
}
|
|
}
|
|
|
|
/** @param list<int> $roleIds */
|
|
private function grantTestPermissions(array $roleIds, array $permissions): void
|
|
{
|
|
foreach ($roleIds as $roleId) {
|
|
foreach ($permissions as $permission) {
|
|
$permissionId = (int) DB::table('permissions')->where('name', $permission)->value('id');
|
|
if ($permissionId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
DB::table('role_permissions')->updateOrInsert(
|
|
['role_id' => $roleId, 'permission_id' => $permissionId],
|
|
[
|
|
'can_create' => true,
|
|
'can_read' => true,
|
|
'can_update' => true,
|
|
'can_delete' => true,
|
|
'can_manage' => true,
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function validUserPayload(int $roleId, array $overrides = []): array
|
|
{
|
|
$unique = str_replace('.', '', uniqid('', true));
|
|
|
|
return array_merge([
|
|
'firstname' => 'Api',
|
|
'lastname' => 'Coverage',
|
|
'gender' => 'Male',
|
|
'cellphone' => '5551234567',
|
|
'email' => "api.coverage.$unique@example.test",
|
|
'address_street' => '1 Test Street',
|
|
'city' => 'Brooklyn',
|
|
'state' => 'NY',
|
|
'zip' => '11201',
|
|
'accept_school_policy' => true,
|
|
'role_id' => $roleId,
|
|
'password' => 'password',
|
|
'semester' => 'Fall',
|
|
'school_year' => '2025-2026',
|
|
'school_id' => 1,
|
|
'user_type' => 'primary',
|
|
'status' => 'Active',
|
|
'is_verified' => true,
|
|
'is_suspended' => false,
|
|
], $overrides);
|
|
}
|
|
}
|