security fix and fix parent pages
API CI/CD / Validate (composer + pint) (push) Successful in 3m7s
API CI/CD / Test (PHPUnit) (push) Failing after 5m46s
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 3m7s
API CI/CD / Test (PHPUnit) (push) Failing after 5m46s
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:
@@ -45,6 +45,138 @@ class ClassProgressControllerTest extends TestCase
|
||||
$this->assertCount(2, $response->json('data.items'));
|
||||
}
|
||||
|
||||
public function test_admin_grouped_index_accepts_school_year_id_and_includes_legacy_rows(): void
|
||||
{
|
||||
$admin = $this->createUser('admin@example.com');
|
||||
$this->assignRole($admin->id, 'administrator');
|
||||
$this->seedClassSection();
|
||||
DB::table('classSection')->where('class_section_id', 101)->update([
|
||||
'semester' => '',
|
||||
'school_year' => null,
|
||||
]);
|
||||
$schoolYearId = DB::table('school_years')->insertGetId([
|
||||
'name' => '2025-2026',
|
||||
'start_date' => '2025-08-01',
|
||||
'end_date' => '2026-07-31',
|
||||
'status' => 'active',
|
||||
'is_current' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
ClassProgressReport::factory()->create([
|
||||
'teacher_id' => $admin->id,
|
||||
'class_section_id' => 101,
|
||||
'school_year' => null,
|
||||
'semester' => null,
|
||||
'week_start' => '2026-02-08',
|
||||
'week_end' => '2026-02-14',
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($admin);
|
||||
|
||||
$response = $this->getJson('/api/v1/class-progress?school_year_id='.$schoolYearId.'&group_by_week=1');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJsonPath('status', true);
|
||||
$this->assertCount(1, $response->json('data.items'));
|
||||
|
||||
$administratorAlias = $this->getJson('/api/v1/administrator/progress?school_year_id='.$schoolYearId.'&group_by_week=1');
|
||||
$administratorAlias->assertOk();
|
||||
$this->assertCount(1, $administratorAlias->json('data.items'));
|
||||
|
||||
$adminAlias = $this->getJson('/api/v1/admin/progress?school_year_id='.$schoolYearId.'&group_by_week=1');
|
||||
$adminAlias->assertOk();
|
||||
$this->assertCount(1, $adminAlias->json('data.items'));
|
||||
}
|
||||
|
||||
public function test_parent_progress_returns_reports_for_enrolled_sections(): void
|
||||
{
|
||||
$parent = $this->createUser('parent@example.com');
|
||||
$teacher = $this->createUser('teacher-for-parent-progress@example.com');
|
||||
$this->assignRole($parent->id, 'parent');
|
||||
$this->seedClassSection();
|
||||
DB::table('classSection')->where('class_section_id', 101)->update([
|
||||
'semester' => '',
|
||||
'school_year' => null,
|
||||
]);
|
||||
DB::table('students')->insert([
|
||||
'id' => 301,
|
||||
'school_id' => '301',
|
||||
'firstname' => 'Student',
|
||||
'lastname' => 'One',
|
||||
'is_active' => 1,
|
||||
'is_new' => 0,
|
||||
'parent_id' => $parent->id,
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
]);
|
||||
DB::table('enrollments')->insert([
|
||||
'student_id' => 301,
|
||||
'class_section_id' => 101,
|
||||
'parent_id' => $parent->id,
|
||||
'enrollment_status' => 'enrolled',
|
||||
'is_withdrawn' => 0,
|
||||
'admission_status' => 'accepted',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$report = ClassProgressReport::factory()->create([
|
||||
'teacher_id' => $teacher->id,
|
||||
'class_section_id' => 101,
|
||||
'school_year' => null,
|
||||
'semester' => null,
|
||||
'week_start' => '2026-02-08',
|
||||
'week_end' => '2026-02-14',
|
||||
]);
|
||||
ClassProgressAttachment::factory()->create([
|
||||
'report_id' => $report->id,
|
||||
'file_path' => 'storage/class_material/sample.pdf',
|
||||
'original_name' => 'sample.pdf',
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($parent);
|
||||
|
||||
$response = $this->getJson('/api/v1/parents/progress?school_year=2025-2026');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJsonPath('ok', true);
|
||||
$this->assertCount(1, $response->json('data.items'));
|
||||
$this->assertCount(1, $response->json('data.students'));
|
||||
$this->assertSame($report->id, $response->json('data.items.0.id'));
|
||||
$this->assertSame($report->id, $response->json('data.items.0.report_id'));
|
||||
$this->assertNotEmpty($response->json('data.items.0.view_url'));
|
||||
$this->assertNotEmpty($response->json('data.items.0.reports.Islamic Studies.attachments.0.download_url'));
|
||||
$this->assertSame($report->id, $response->json('data.items.0.weekly_reports.0.id'));
|
||||
$this->assertNotEmpty($response->json('data.items.0.weekly_reports.0.attachments.0.download_url'));
|
||||
|
||||
$sharedResponse = $this->getJson('/api/v1/class-progress?school_year=2025-2026&group_by_week=1');
|
||||
|
||||
$sharedResponse->assertOk();
|
||||
$sharedResponse->assertJsonPath('status', true);
|
||||
$this->assertCount(1, $sharedResponse->json('data.items'));
|
||||
$this->assertSame($report->id, $sharedResponse->json('data.items.0.id'));
|
||||
$this->assertSame($report->id, $sharedResponse->json('data.items.0.weekly_reports.0.id'));
|
||||
|
||||
$detailResponse = $this->getJson('/api/v1/class-progress/'.$report->id);
|
||||
|
||||
$detailResponse->assertOk();
|
||||
$detailResponse->assertJsonPath('status', true);
|
||||
$this->assertSame($report->id, $detailResponse->json('data.report.id'));
|
||||
$this->assertNotEmpty($detailResponse->json('data.weekly_reports.0.attachments.0.download_url'));
|
||||
$this->assertSame($report->id, $detailResponse->json('data.group.weekly_reports.0.id'));
|
||||
|
||||
$parentDetailResponse = $this->getJson('/api/v1/parents/progress/'.$report->id);
|
||||
|
||||
$parentDetailResponse->assertOk();
|
||||
$parentDetailResponse->assertJsonPath('ok', true);
|
||||
$parentDetailResponse->assertJsonPath('status', true);
|
||||
$this->assertSame($report->id, $parentDetailResponse->json('data.report.id'));
|
||||
$this->assertNotEmpty($parentDetailResponse->json('data.report.attachments.0.download_url'));
|
||||
$this->assertSame($report->id, $parentDetailResponse->json('data.group.weekly_reports.0.id'));
|
||||
}
|
||||
|
||||
public function test_store_creates_reports(): void
|
||||
{
|
||||
Storage::fake('public');
|
||||
@@ -243,4 +375,24 @@ class ClassProgressControllerTest extends TestCase
|
||||
|
||||
return User::query()->where('email', $email)->firstOrFail();
|
||||
}
|
||||
|
||||
private function assignRole(int $userId, string $roleName): void
|
||||
{
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => $roleName,
|
||||
'slug' => $roleName,
|
||||
'dashboard_route' => $roleName === 'parent' ? 'parent/dashboard' : 'administrator/administratordashboard',
|
||||
'priority' => 100,
|
||||
'is_active' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $userId,
|
||||
'role_id' => $roleId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,28 @@ class ParentControllerTest extends TestCase
|
||||
]);
|
||||
|
||||
DB::table('attendance_data')->insert([
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-01',
|
||||
'status' => 'absent',
|
||||
'reason' => 'Sick',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
[
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-02',
|
||||
'status' => 'present',
|
||||
'reason' => null,
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
],
|
||||
[
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-01',
|
||||
'status' => 'absent',
|
||||
'reason' => 'Sick',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
],
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($parent);
|
||||
@@ -48,6 +61,7 @@ class ParentControllerTest extends TestCase
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJson(['ok' => true]);
|
||||
$this->assertCount(1, $response->json('attendance'));
|
||||
$response->assertJsonPath('attendance.0.status', 'absent');
|
||||
}
|
||||
|
||||
@@ -91,6 +105,14 @@ class ParentControllerTest extends TestCase
|
||||
|
||||
private function seedParent(): User
|
||||
{
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => 'parent',
|
||||
'slug' => 'parent',
|
||||
'is_active' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$id = DB::table('users')->insertGetId([
|
||||
'firstname' => 'Parent',
|
||||
'lastname' => 'User',
|
||||
@@ -106,6 +128,15 @@ class ParentControllerTest extends TestCase
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $id,
|
||||
'role_id' => $roleId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return User::query()->findOrFail($id);
|
||||
|
||||
@@ -30,6 +30,29 @@ class ReportCardsControllerTest extends TestCase
|
||||
$this->assertArrayHasKey('class_section_id', $response->json('data.classSections.0'));
|
||||
}
|
||||
|
||||
public function test_parent_meta_is_scoped_to_only_their_students(): void
|
||||
{
|
||||
$this->seedConfig();
|
||||
$admin = $this->seedUser();
|
||||
$parent = $this->seedParentUser(10);
|
||||
$data = $this->seedReportCardData($admin->id);
|
||||
$other = $this->seedOtherScoredStudent($admin->id);
|
||||
|
||||
Sanctum::actingAs($parent);
|
||||
|
||||
$response = $this->getJson('/api/v1/reports/report-cards/meta?school_year=2025-2026&semester=Fall');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJsonPath('status', true);
|
||||
$this->assertSame([$data['student_id']], collect($response->json('data.students'))->pluck('id')->all());
|
||||
|
||||
$this->getJson('/api/v1/reports/report-cards/students/'.$other['student_id'].'?school_year=2025-2026&semester=Fall')
|
||||
->assertForbidden();
|
||||
|
||||
$this->getJson('/api/v1/reports/report-cards/classes/'.$data['class_section_id'].'?school_year=2025-2026&semester=Fall')
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_completeness_returns_summary_and_students(): void
|
||||
{
|
||||
$this->seedConfig();
|
||||
@@ -155,6 +178,48 @@ class ReportCardsControllerTest extends TestCase
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
return User::query()->findOrFail($userId);
|
||||
}
|
||||
|
||||
private function seedParentUser(int $userId): User
|
||||
{
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => 'parent',
|
||||
'slug' => 'parent',
|
||||
'is_active' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('users')->insert([
|
||||
'id' => $userId,
|
||||
'firstname' => 'Parent',
|
||||
'lastname' => 'User',
|
||||
'cellphone' => '8888888888',
|
||||
'email' => 'parent-reportcards@example.com',
|
||||
'address_street' => '123 Parent',
|
||||
'city' => 'City',
|
||||
'state' => 'ST',
|
||||
'zip' => '12345',
|
||||
'accept_school_policy' => 1,
|
||||
'password' => bcrypt('password'),
|
||||
'user_type' => 'primary',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $userId,
|
||||
'role_id' => $roleId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return User::query()->findOrFail($userId);
|
||||
@@ -280,6 +345,59 @@ class ReportCardsControllerTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
private function seedOtherScoredStudent(int $userId): array
|
||||
{
|
||||
DB::table('students')->insert([
|
||||
'id' => 102,
|
||||
'school_id' => 'SCH3',
|
||||
'firstname' => 'Other',
|
||||
'lastname' => 'Student',
|
||||
'age' => 10,
|
||||
'gender' => 'Female',
|
||||
'photo_consent' => 1,
|
||||
'parent_id' => 11,
|
||||
'year_of_registration' => '2025-2026',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'is_active' => 1,
|
||||
]);
|
||||
|
||||
DB::table('student_class')->insert([
|
||||
'student_id' => 102,
|
||||
'class_section_id' => 200,
|
||||
'school_year' => '2025-2026',
|
||||
'semester' => 'Fall',
|
||||
'is_event_only' => 0,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('semester_scores')->insert([
|
||||
'student_id' => 102,
|
||||
'school_id' => 'SCH3',
|
||||
'class_section_id' => 200,
|
||||
'updated_by' => $userId,
|
||||
'homework_avg' => 87,
|
||||
'quiz_avg' => 86,
|
||||
'project_avg' => 85,
|
||||
'test_avg' => 84,
|
||||
'midterm_exam_score' => 83,
|
||||
'final_exam_score' => 82,
|
||||
'attendance_score' => 81,
|
||||
'ptap_score' => 80,
|
||||
'semester_score' => 84,
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'student_id' => 102,
|
||||
'class_section_id' => 200,
|
||||
];
|
||||
}
|
||||
|
||||
private function seedAcknowledgement(int $studentId): void
|
||||
{
|
||||
DB::table('report_card_acknowledgements')->insert([
|
||||
|
||||
@@ -135,8 +135,15 @@ class SchoolYearControllerTest extends TestCase
|
||||
|
||||
$missing = [];
|
||||
foreach ($routes as $route) {
|
||||
$uri = $route->uri();
|
||||
$middleware = $route->gatherMiddleware();
|
||||
$hasPermission = collect($middleware)->contains(fn (string $middleware): bool => str_starts_with($middleware, 'perm:'));
|
||||
$isContextRoute = in_array($uri, [
|
||||
'api/v1/school-years',
|
||||
'api/v1/school-years/current',
|
||||
'api/v1/school-years/options',
|
||||
], true);
|
||||
$hasPermission = $isContextRoute
|
||||
|| collect($middleware)->contains(fn (string $middleware): bool => str_starts_with($middleware, 'perm:'));
|
||||
$hasAuth = collect($middleware)->contains(fn (string $middleware): bool => str_contains($middleware, 'multi.auth') || str_contains($middleware, 'auth'));
|
||||
$hasAccountGuard = in_array('account.active', $middleware, true);
|
||||
|
||||
@@ -148,6 +155,25 @@ class SchoolYearControllerTest extends TestCase
|
||||
$this->assertSame([], $missing, 'School-year routes missing auth/account/permission middleware: '.implode(', ', $missing));
|
||||
}
|
||||
|
||||
public function test_active_user_can_load_school_year_context_list_without_school_year_permission(): void
|
||||
{
|
||||
$this->seedClosureData();
|
||||
|
||||
$this->actingAs(User::query()->findOrFail(10), 'api');
|
||||
|
||||
$response = $this->getJson('/api/v1/school-years');
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('ok', true)
|
||||
->assertJsonPath('current_school_year', '2025-2026');
|
||||
|
||||
$this->postJson('/api/v1/school-years', [
|
||||
'name' => '2026-2027',
|
||||
'start_date' => '2026-09-01',
|
||||
'end_date' => '2027-06-30',
|
||||
])->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_admin_can_create_draft_school_year_via_api(): void
|
||||
{
|
||||
$this->seedClosureData();
|
||||
@@ -426,12 +452,15 @@ class SchoolYearControllerTest extends TestCase
|
||||
];
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$permissionId = DB::table('permissions')->insertGetId([
|
||||
'name' => $permission,
|
||||
'description' => 'Test permission '.$permission,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
DB::table('permissions')->updateOrInsert(
|
||||
['name' => $permission],
|
||||
[
|
||||
'description' => 'Test permission '.$permission,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
$permissionId = (int) DB::table('permissions')->where('name', $permission)->value('id');
|
||||
DB::table('role_permissions')->insert([
|
||||
'role_id' => 1,
|
||||
'permission_id' => $permissionId,
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
namespace Tests\Feature\Api\V1\Settings;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\Settings\SchoolCalendar\SchoolCalendarContextService;
|
||||
use App\Services\Settings\SchoolCalendar\SchoolCalendarMutationService;
|
||||
use App\Services\Settings\SchoolCalendar\SchoolCalendarNotificationService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
@@ -44,6 +46,40 @@ class SchoolCalendarControllerTest extends TestCase
|
||||
$this->assertArrayHasKey('extendedProps', $response->json('data.events.0'));
|
||||
}
|
||||
|
||||
public function test_parent_can_list_only_parent_audience_events(): void
|
||||
{
|
||||
$this->seedConfig();
|
||||
$parent = $this->seedParentUser();
|
||||
$parentEventId = $this->seedEvent();
|
||||
$teacherEventId = DB::table('calendar_events')->insertGetId([
|
||||
'title' => 'Teacher Only',
|
||||
'date' => '2026-01-03',
|
||||
'description' => 'Hidden from parents',
|
||||
'notify_parent' => 0,
|
||||
'notify_admin' => 0,
|
||||
'notify_teacher' => 1,
|
||||
'no_school' => 0,
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'notification_sent' => 0,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($parent);
|
||||
|
||||
$response = $this->getJson('/api/v1/settings/school-calendar/events?audience=parent&school_year=2025-2026&include_meetings=0');
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertJsonPath('status', true);
|
||||
$ids = collect($response->json('data.events'))->pluck('id')->all();
|
||||
$this->assertContains($parentEventId, $ids);
|
||||
$this->assertNotContains($teacherEventId, $ids);
|
||||
|
||||
$this->getJson('/api/v1/settings/school-calendar/events?school_year=2025-2026')
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_show_returns_event(): void
|
||||
{
|
||||
$this->seedConfig();
|
||||
@@ -136,7 +172,10 @@ class SchoolCalendarControllerTest extends TestCase
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$this->app->bind(SchoolCalendarMutationService::class, function () {
|
||||
return new class
|
||||
return new class(
|
||||
app(SchoolCalendarContextService::class),
|
||||
app(SchoolCalendarNotificationService::class),
|
||||
) extends SchoolCalendarMutationService
|
||||
{
|
||||
public function create(array $payload, array $targets = []): array
|
||||
{
|
||||
@@ -173,6 +212,14 @@ class SchoolCalendarControllerTest extends TestCase
|
||||
|
||||
private function seedUser(): User
|
||||
{
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => 'administrator',
|
||||
'slug' => 'administrator',
|
||||
'is_active' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$userId = DB::table('users')->insertGetId([
|
||||
'firstname' => 'Admin',
|
||||
'lastname' => 'User',
|
||||
@@ -188,6 +235,54 @@ class SchoolCalendarControllerTest extends TestCase
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $userId,
|
||||
'role_id' => $roleId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return User::query()->findOrFail($userId);
|
||||
}
|
||||
|
||||
private function seedParentUser(): User
|
||||
{
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => 'parent',
|
||||
'slug' => 'parent',
|
||||
'is_active' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$userId = DB::table('users')->insertGetId([
|
||||
'firstname' => 'Parent',
|
||||
'lastname' => 'User',
|
||||
'cellphone' => '8888888888',
|
||||
'email' => 'calendar-parent@example.com',
|
||||
'address_street' => '123 Street',
|
||||
'city' => 'City',
|
||||
'state' => 'ST',
|
||||
'zip' => '12345',
|
||||
'accept_school_policy' => 1,
|
||||
'password' => bcrypt('password'),
|
||||
'user_type' => 'primary',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $userId,
|
||||
'role_id' => $roleId,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return User::query()->findOrFail($userId);
|
||||
|
||||
@@ -74,6 +74,8 @@ class WhatsappInviteControllerTest extends TestCase
|
||||
|
||||
public function test_send_invites_returns_error_when_no_listener(): void
|
||||
{
|
||||
Event::forget(WhatsappInvitesSend::class);
|
||||
|
||||
$user = $this->createUser();
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RequirePermissionMiddlewareTest extends TestCase
|
||||
@@ -27,6 +28,8 @@ class RequirePermissionMiddlewareTest extends TestCase
|
||||
'zip' => '12345',
|
||||
'accept_school_policy' => 1,
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
'password' => 'hashed',
|
||||
'semester' => 'Fall',
|
||||
]);
|
||||
@@ -59,6 +62,8 @@ class RequirePermissionMiddlewareTest extends TestCase
|
||||
'zip' => '12345',
|
||||
'accept_school_policy' => 1,
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
'password' => 'hashed',
|
||||
'semester' => 'Fall',
|
||||
]);
|
||||
@@ -81,4 +86,77 @@ class RequirePermissionMiddlewareTest extends TestCase
|
||||
$response->assertOk();
|
||||
$response->assertSee('ok');
|
||||
}
|
||||
|
||||
public function test_multi_auth_blocks_suspended_sanctum_user(): void
|
||||
{
|
||||
Route::middleware('multi.auth')->get('/test-active-account', fn () => 'ok');
|
||||
|
||||
$user = $this->createUser([
|
||||
'email' => 'suspended@example.com',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 1,
|
||||
]);
|
||||
|
||||
Sanctum::actingAs($user);
|
||||
|
||||
$this->get('/test-active-account', ['Accept' => 'application/json'])
|
||||
->assertStatus(403)
|
||||
->assertJsonPath('message', 'Account unavailable.');
|
||||
}
|
||||
|
||||
public function test_permission_middleware_blocks_unverified_user_even_with_permission(): void
|
||||
{
|
||||
$permissionId = DB::table('permissions')->insertGetId([
|
||||
'name' => 'edit_students',
|
||||
'description' => 'Edit students',
|
||||
]);
|
||||
|
||||
$roleId = DB::table('roles')->insertGetId([
|
||||
'name' => 'admin',
|
||||
'priority' => 1,
|
||||
]);
|
||||
|
||||
$user = $this->createUser([
|
||||
'email' => 'unverified-perm@example.com',
|
||||
'is_verified' => 0,
|
||||
'is_suspended' => 0,
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
'user_id' => $user->id,
|
||||
'role_id' => $roleId,
|
||||
]);
|
||||
|
||||
DB::table('role_permissions')->insert([
|
||||
'role_id' => $roleId,
|
||||
'permission_id' => $permissionId,
|
||||
'can_read' => 1,
|
||||
]);
|
||||
|
||||
Route::middleware('perm:edit_students')->get('/test-perm-unverified', fn () => 'ok');
|
||||
|
||||
$this->actingAs($user)->get('/test-perm-unverified', ['Accept' => 'application/json'])
|
||||
->assertStatus(403)
|
||||
->assertJsonPath('message', 'Account unavailable.');
|
||||
}
|
||||
|
||||
private function createUser(array $overrides = []): User
|
||||
{
|
||||
return User::query()->create(array_merge([
|
||||
'firstname' => 'Test',
|
||||
'lastname' => 'User',
|
||||
'email' => 'test-user@example.com',
|
||||
'cellphone' => '5555555555',
|
||||
'address_street' => '123 Main',
|
||||
'city' => 'City',
|
||||
'state' => 'CT',
|
||||
'zip' => '12345',
|
||||
'accept_school_policy' => 1,
|
||||
'status' => 'Active',
|
||||
'is_verified' => 1,
|
||||
'is_suspended' => 0,
|
||||
'password' => 'hashed',
|
||||
'semester' => 'Fall',
|
||||
], $overrides));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,15 +52,28 @@ class ParentAttendanceServiceTest extends TestCase
|
||||
]);
|
||||
|
||||
DB::table('attendance_data')->insert([
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-02',
|
||||
'status' => 'late',
|
||||
'reason' => 'Traffic',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
[
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-02',
|
||||
'status' => 'late',
|
||||
'reason' => 'Traffic',
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
],
|
||||
[
|
||||
'class_id' => 1,
|
||||
'class_section_id' => 1,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => '1',
|
||||
'date' => '2025-10-01',
|
||||
'status' => 'present',
|
||||
'reason' => null,
|
||||
'semester' => 'Fall',
|
||||
'school_year' => '2025-2026',
|
||||
],
|
||||
]);
|
||||
|
||||
$service = new ParentAttendanceService(new ParentConfigService);
|
||||
|
||||
Reference in New Issue
Block a user