add all controllers logic

This commit is contained in:
root
2026-03-11 17:53:15 -04:00
parent 3e6c577085
commit 2ef71cc92b
421 changed files with 12009 additions and 5211 deletions
@@ -0,0 +1,17 @@
<?php
namespace Tests\Unit\Resources\Frontend;
use App\Http\Resources\Frontend\FrontendPageResource;
use Tests\TestCase;
class FrontendPageResourceTest extends TestCase
{
public function test_resource_returns_page(): void
{
$resource = new FrontendPageResource(['page' => 'index']);
$payload = $resource->toArray(request());
$this->assertSame('index', $payload['page']);
}
}
@@ -0,0 +1,22 @@
<?php
namespace Tests\Unit\Resources\Frontend;
use App\Http\Resources\Frontend\PageContentResource;
use Tests\TestCase;
class PageContentResourceTest extends TestCase
{
public function test_resource_returns_content(): void
{
$resource = new PageContentResource([
'type' => 'privacy_policy',
'content' => '<h1>Privacy</h1>',
]);
$payload = $resource->toArray(request());
$this->assertSame('privacy_policy', $payload['type']);
$this->assertSame('<h1>Privacy</h1>', $payload['content']);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Tests\Unit\Resources\Messaging;
use App\Http\Resources\Messaging\MessageResource;
use App\Models\Message;
use Tests\TestCase;
class MessageResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$message = new Message([
'id' => 1,
'sender_id' => 2,
'recipient_id' => 3,
'subject' => 'Hello',
'message' => 'Body',
'sent_datetime' => now(),
'read_status' => 0,
'message_number' => 'MSG-1',
'priority' => 'normal',
'status' => 'sent',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$payload = (new MessageResource($message))->toArray(request());
$this->assertArrayHasKey('id', $payload);
$this->assertArrayHasKey('subject', $payload);
$this->assertArrayHasKey('message_number', $payload);
}
}
@@ -0,0 +1,18 @@
<?php
namespace Tests\Unit\Resources\Messaging;
use App\Http\Resources\Messaging\RecipientResource;
use Tests\TestCase;
class RecipientResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$resource = new RecipientResource(['id' => 5, 'name' => 'Test User']);
$payload = $resource->toArray(request());
$this->assertSame(5, $payload['id']);
$this->assertSame('Test User', $payload['name']);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Tests\Unit\Resources\Settings;
use App\Http\Resources\Settings\SettingsResource;
use Tests\TestCase;
class SettingsResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$resource = new SettingsResource([
'site_name' => 'App',
'administrator_email' => 'admin@example.com',
'timezone' => 'UTC',
]);
$payload = $resource->toArray(request());
$this->assertSame('App', $payload['site_name']);
$this->assertSame('admin@example.com', $payload['administrator_email']);
$this->assertSame('UTC', $payload['timezone']);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Tests\Unit\Resources\Staff;
use App\Http\Resources\Staff\StaffResource;
use App\Models\Staff;
use Tests\TestCase;
class StaffResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$staff = new Staff([
'id' => 1,
'user_id' => 2,
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'test@example.com',
'role_name' => 'teacher',
'active_role' => 'teacher',
'school_year' => '2025-2026',
]);
$payload = (new StaffResource($staff))->toArray(request());
$this->assertSame(1, $payload['id']);
$this->assertSame('teacher', $payload['role_name']);
}
}
@@ -0,0 +1,22 @@
<?php
namespace Tests\Unit\Resources\Support;
use App\Http\Resources\Support\ContactMessageResource;
use Tests\TestCase;
class ContactMessageResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$resource = new ContactMessageResource([
'email_sent' => true,
'recipient' => 'support@example.com',
]);
$payload = $resource->toArray(request());
$this->assertTrue($payload['email_sent']);
$this->assertSame('support@example.com', $payload['recipient']);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Tests\Unit\Resources\Support;
use App\Http\Resources\Support\SupportRequestResource;
use App\Models\SupportRequest;
use Tests\TestCase;
class SupportRequestResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$model = new SupportRequest([
'id' => 1,
'user_id' => 2,
'subject' => 'Help',
'message' => 'Need help',
'status' => 'open',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$payload = (new SupportRequestResource($model))->toArray(request());
$this->assertSame(1, $payload['id']);
$this->assertSame('Help', $payload['subject']);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Tests\Unit\Resources\Ui;
use App\Http\Resources\Ui\UiStyleResource;
use App\Models\Preferences;
use Tests\TestCase;
class UiStyleResourceTest extends TestCase
{
public function test_resource_returns_expected_shape(): void
{
$prefs = new Preferences([
'style_color' => 'blue',
'menu_color' => 'custom',
'menu_custom_bg' => '#112233',
'menu_custom_text' => '#FFFFFF',
'menu_custom_mode' => 'dark',
]);
$payload = (new UiStyleResource($prefs))->toArray(request());
$this->assertSame('blue', $payload['style_color']);
$this->assertSame('custom', $payload['menu_color']);
}
}
@@ -0,0 +1,33 @@
<?php
namespace Tests\Unit\Services\Attendance;
use App\Models\LateSlipLog;
use App\Services\Attendance\LateSlipLogCommandService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class LateSlipLogCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_delete_removes_log(): void
{
$log = LateSlipLog::query()->create([
'school_year' => '2025-2026',
'semester' => 'Fall',
'student_name' => 'Student X',
'slip_date' => '2025-09-01',
'time_in' => '08:05:00',
'grade' => '4',
'reason' => 'Traffic',
'admin_name' => 'Admin User',
]);
$service = app(LateSlipLogCommandService::class);
$deleted = $service->delete($log);
$this->assertTrue($deleted);
$this->assertDatabaseMissing('late_slip_logs', ['id' => $log->id]);
}
}
@@ -0,0 +1,37 @@
<?php
namespace Tests\Unit\Services\Attendance;
use App\Services\Attendance\LateSlipLogQueryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LateSlipLogQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_paginate_filters_by_date(): void
{
DB::table('late_slip_logs')->insert([
[
'school_year' => '2025-2026',
'semester' => 'Fall',
'student_name' => 'Student A',
'slip_date' => '2025-09-01',
],
[
'school_year' => '2025-2026',
'semester' => 'Fall',
'student_name' => 'Student B',
'slip_date' => '2025-10-01',
],
]);
$service = app(LateSlipLogQueryService::class);
$pager = $service->paginate(['date_from' => '2025-10-01'], 1, 10);
$this->assertSame(1, $pager->total());
$this->assertSame('Student B', $pager->items()[0]->student_name);
}
}
@@ -10,24 +10,24 @@ class RegistrationCaptchaServiceTest extends TestCase
{
use RefreshDatabase;
public function test_generate_sets_session_value(): void
public function test_generate_sets_cache_value(): void
{
$service = new RegistrationCaptchaService();
$value = $service->generate(6);
$this->assertNotEmpty($value);
$this->assertSame($value, session()->get('captcha_answer'));
$this->assertTrue($service->verify($value));
}
public function test_verify_and_clear(): void
{
$service = new RegistrationCaptchaService();
session()->put('captcha_answer', 'ABC123');
$value = $service->generate(6);
$this->assertTrue($service->verify('ABC123'));
$this->assertTrue($service->verify($value));
$this->assertFalse($service->verify('WRONG'));
$service->clear();
$this->assertNull(session()->get('captcha_answer'));
$this->assertFalse($service->verify($value));
}
}
@@ -35,7 +35,8 @@ class RegistrationServiceTest extends TestCase
'is_active' => 1,
]);
session()->put('captcha_answer', 'ABCD');
$captchaService = new RegistrationCaptchaService();
$captcha = $captchaService->generate(4);
$emailService = Mockery::mock(EmailService::class);
$emailService->shouldReceive('send')->once()->andReturn(true);
@@ -44,7 +45,7 @@ class RegistrationServiceTest extends TestCase
$emailService,
new SchoolIdService(),
new RegistrationFormatterService(new PhoneFormatterService()),
new RegistrationCaptchaService()
$captchaService
);
$result = $service->register([
@@ -60,7 +61,7 @@ class RegistrationServiceTest extends TestCase
'state' => 'CT',
'zip' => '12345',
'accept_school_policy' => 1,
'captcha' => 'ABCD',
'captcha' => $captcha,
'is_parent' => 1,
'no_second_parent_info' => 1,
]);
@@ -114,7 +115,8 @@ class RegistrationServiceTest extends TestCase
'token' => 'token',
]);
session()->put('captcha_answer', 'ABCD');
$captchaService = new RegistrationCaptchaService();
$captcha = $captchaService->generate(4);
$emailService = Mockery::mock(EmailService::class);
$emailService->shouldNotReceive('send');
@@ -123,7 +125,7 @@ class RegistrationServiceTest extends TestCase
$emailService,
new SchoolIdService(),
new RegistrationFormatterService(new PhoneFormatterService()),
new RegistrationCaptchaService()
$captchaService
);
$result = $service->register([
@@ -139,7 +141,7 @@ class RegistrationServiceTest extends TestCase
'state' => 'CT',
'zip' => '12345',
'accept_school_policy' => 1,
'captcha' => 'ABCD',
'captcha' => $captcha,
'is_parent' => 1,
'no_second_parent_info' => 1,
]);
@@ -1,42 +0,0 @@
<?php
namespace Tests\Unit\Services\Auth;
use App\Services\Auth\SessionActivityService;
use Illuminate\Session\ArraySessionHandler;
use Illuminate\Session\Store;
use Tests\TestCase;
class SessionActivityServiceTest extends TestCase
{
public function test_set_and_get_last_activity(): void
{
$store = $this->makeSessionStore();
$service = new SessionActivityService($store);
$service->setLastActivity(12345);
$this->assertTrue($service->hasLastActivity());
$this->assertSame(12345, $service->getLastActivity());
}
public function test_clear_last_activity(): void
{
$store = $this->makeSessionStore();
$service = new SessionActivityService($store);
$service->setLastActivity(12345);
$service->clearLastActivity();
$this->assertFalse($service->hasLastActivity());
}
private function makeSessionStore(): Store
{
$handler = new ArraySessionHandler(10);
$store = new Store('test', $handler);
$store->start();
return $store;
}
}
@@ -1,22 +0,0 @@
<?php
namespace Tests\Unit\Services\Auth;
use App\Services\Auth\SessionTimeoutConfigService;
use Tests\TestCase;
class SessionTimeoutConfigServiceTest extends TestCase
{
public function test_config_returns_expected_keys(): void
{
$service = new SessionTimeoutConfigService();
$config = $service->config();
$this->assertArrayHasKey('timeout', $config);
$this->assertArrayHasKey('warning_time', $config);
$this->assertArrayHasKey('check_interval', $config);
$this->assertArrayHasKey('logout_url', $config);
$this->assertArrayHasKey('keep_alive_url', $config);
$this->assertArrayHasKey('check_url', $config);
}
}
@@ -1,51 +0,0 @@
<?php
namespace Tests\Unit\Services\Auth;
use App\Config\SessionTimeout;
use App\Services\Auth\SessionActivityService;
use App\Services\Auth\SessionTimeoutService;
use Mockery;
use Tests\TestCase;
class SessionTimeoutServiceTest extends TestCase
{
public function test_check_timeout_returns_expired_when_missing_activity(): void
{
$activity = Mockery::mock(SessionActivityService::class);
$activity->shouldReceive('hasLastActivity')->andReturn(false);
$activity->shouldReceive('clearLastActivity')->once();
$activity->shouldReceive('invalidate')->once();
$service = new SessionTimeoutService($activity);
$result = $service->checkTimeout();
$this->assertSame('expired', $result['status']);
}
public function test_check_timeout_returns_warning(): void
{
$activity = Mockery::mock(SessionActivityService::class);
$activity->shouldReceive('hasLastActivity')->andReturn(true);
$activity->shouldReceive('getLastActivity')->andReturn(time() - (SessionTimeout::WARNING_THRESHOLD + 10));
$service = new SessionTimeoutService($activity);
$result = $service->checkTimeout();
$this->assertSame('warning', $result['status']);
}
public function test_ping_returns_expired_when_timed_out(): void
{
$activity = Mockery::mock(SessionActivityService::class);
$activity->shouldReceive('hasLastActivity')->andReturn(true);
$activity->shouldReceive('getLastActivity')->andReturn(time() - (SessionTimeout::TIMEOUT_DURATION + 10));
$activity->shouldReceive('clearLastActivity')->once();
$activity->shouldReceive('invalidate')->once();
$service = new SessionTimeoutService($activity);
$result = $service->pingActivity();
$this->assertSame('expired', $result['status']);
}
}
@@ -0,0 +1,44 @@
<?php
namespace Tests\Unit\Services\Auth;
use App\Services\Auth\UserRoleService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class UserRoleServiceTest extends TestCase
{
use RefreshDatabase;
public function test_has_permission_for_crud_checks_roles(): void
{
$permissionId = DB::table('permissions')->insertGetId([
'name' => 'manage_students',
'description' => 'Manage students',
]);
$roleId = DB::table('roles')->insertGetId([
'name' => 'admin',
'priority' => 1,
]);
DB::table('user_roles')->insert([
'user_id' => 5,
'role_id' => $roleId,
]);
DB::table('role_permissions')->insert([
'role_id' => $roleId,
'permission_id' => $permissionId,
'can_read' => 1,
'can_manage' => 0,
]);
$service = new UserRoleService();
$roleIds = $service->getRoleIds(5);
$this->assertTrue($service->hasPermissionForCrud($roleIds, 'manage_students', 'read'));
$this->assertFalse($service->hasPermissionForCrud($roleIds, 'manage_students', 'delete'));
}
}
@@ -0,0 +1,33 @@
<?php
namespace Tests\Unit\Services\ClassPreparation;
use App\Services\ClassPreparation\ClassPreparationAdjustmentWriterService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ClassPreparationAdjustmentWriterServiceTest extends TestCase
{
use RefreshDatabase;
public function test_save_adjustments_persists_allowed_items(): void
{
$service = app(ClassPreparationAdjustmentWriterService::class);
$count = $service->saveAdjustments('101', '2025-2026', [
'Small Table' => 2,
'Unknown Item' => 5,
], ['Small Table']);
$this->assertSame(1, $count);
$this->assertDatabaseHas('class_prep_adjustments', [
'class_section_id' => '101',
'school_year' => '2025-2026',
'item_name' => 'Small Table',
'adjustment' => 2,
]);
$this->assertDatabaseMissing('class_prep_adjustments', [
'item_name' => 'Unknown Item',
]);
}
}
@@ -0,0 +1,105 @@
<?php
namespace Tests\Unit\Services\ClassPreparation;
use App\Services\ClassPreparation\ClassPreparationAdjustmentService;
use App\Services\ClassPreparation\ClassPreparationCalculatorService;
use App\Services\ClassPreparation\ClassPreparationContextService;
use App\Services\ClassPreparation\ClassPreparationLogService;
use App\Services\ClassPreparation\ClassPreparationPrintService;
use App\Services\ClassPreparation\ClassPreparationRosterService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
use Tests\TestCase;
class ClassPreparationPrintServiceTest extends TestCase
{
use RefreshDatabase;
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function test_print_prep_creates_log(): void
{
$this->seedPrepData();
$service = app(ClassPreparationPrintService::class);
$payload = $service->printPrep('101', '2025-2026', 'Fall');
$this->assertTrue($payload['ok']);
$this->assertDatabaseHas('class_preparation_log', [
'class_section_id' => 101,
'school_year' => '2025-2026',
]);
}
public function test_print_prep_handles_log_failure(): void
{
$context = Mockery::mock(ClassPreparationContextService::class);
$roster = Mockery::mock(ClassPreparationRosterService::class);
$calculator = Mockery::mock(ClassPreparationCalculatorService::class);
$adjustments = Mockery::mock(ClassPreparationAdjustmentService::class);
$logs = Mockery::mock(ClassPreparationLogService::class);
$context->shouldReceive('getSemester')->andReturn('Fall');
$context->shouldReceive('hasRosterForSemester')->andReturn(true);
$roster->shouldReceive('getStudentCountForSection')->andReturn(10);
$calculator->shouldReceive('getClassLevelBySection')->andReturn(2);
$calculator->shouldReceive('calculatePrepItems')->andReturn(['Small Table' => 2]);
$adjustments->shouldReceive('applyAdjustments')->andReturn([['Small Table' => 2], []]);
$logs->shouldReceive('createLog')->andReturn(false);
$service = new ClassPreparationPrintService($context, $roster, $calculator, $adjustments, $logs);
$payload = $service->printPrep('101', '2025-2026', 'Fall');
$this->assertFalse($payload['ok']);
$this->assertNull($payload['printed_at']);
}
private function seedPrepData(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
DB::table('classSection')->insert([
'id' => 1,
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('students')->insert([
'school_id' => 'S-1',
'firstname' => 'Student',
'lastname' => 'One',
'age' => 8,
'gender' => 'Male',
'photo_consent' => 1,
'parent_id' => 1,
'year_of_registration' => '2025',
'school_year' => '2025-2026',
'semester' => 'Fall',
'is_active' => 1,
]);
DB::table('student_class')->insert([
'student_id' => 1,
'class_section_id' => 101,
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('inventory_categories')->insert([
'type' => 'classroom',
'name' => 'Small Table',
]);
}
}
@@ -0,0 +1,73 @@
<?php
namespace Tests\Unit\Services\ClassSections;
use App\Models\User;
use App\Services\ClassSections\ClassAttendanceService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ClassAttendanceServiceTest extends TestCase
{
use RefreshDatabase;
public function test_get_attendance_payload_includes_students(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
DB::table('classSection')->insert([
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('students')->insert([
'school_id' => 'S-1',
'firstname' => 'Student',
'lastname' => 'One',
'age' => 8,
'gender' => 'Male',
'photo_consent' => 1,
'parent_id' => 1,
'year_of_registration' => '2025',
'school_year' => '2025-2026',
'semester' => 'Fall',
'is_active' => 1,
]);
DB::table('student_class')->insert([
'student_id' => 1,
'class_section_id' => 101,
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$teacher = User::query()->create([
'firstname' => 'Teach',
'lastname' => 'Er',
'email' => 'teacher@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = app(ClassAttendanceService::class);
$payload = $service->getAttendancePayload(101, $teacher->id);
$this->assertSame('1-A', $payload['class_name']);
$this->assertCount(1, $payload['students']);
}
}
@@ -0,0 +1,36 @@
<?php
namespace Tests\Unit\Services\ClassSections;
use App\Models\ClassSection;
use App\Services\ClassSections\ClassSectionCommandService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ClassSectionCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_create_update_delete(): void
{
$service = app(ClassSectionCommandService::class);
$section = $service->create([
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$this->assertInstanceOf(ClassSection::class, $section);
$this->assertDatabaseHas('classSection', ['id' => $section->id]);
$updated = $service->update($section, ['class_section_name' => '1-B']);
$this->assertSame('1-B', $updated->class_section_name);
$deleted = $service->delete($section);
$this->assertTrue($deleted);
$this->assertDatabaseMissing('classSection', ['id' => $section->id]);
}
}
@@ -0,0 +1,47 @@
<?php
namespace Tests\Unit\Services\ClassSections;
use App\Services\ClassSections\ClassSectionQueryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ClassSectionQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_list_filters_by_search(): void
{
DB::table('classes')->insert([
'class_name' => 'Class 1',
'schedule' => 'Monday',
'capacity' => 30,
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('classSection')->insert([
[
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
],
[
'class_id' => 1,
'class_section_id' => 102,
'class_section_name' => '2-B',
'semester' => 'Fall',
'school_year' => '2025-2026',
],
]);
$service = app(ClassSectionQueryService::class);
$pager = $service->list(['search' => '1-A'], 1, 10);
$this->assertSame(1, $pager->total());
$this->assertSame('1-A', $pager->items()[0]->class_section_name);
}
}
@@ -0,0 +1,31 @@
<?php
namespace Tests\Unit\Services\ClassSections;
use App\Services\ClassSections\ClassSectionSeedService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ClassSectionSeedServiceTest extends TestCase
{
use RefreshDatabase;
public function test_seed_defaults_creates_rows(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
$service = app(ClassSectionSeedService::class);
$count = $service->seedDefaults();
$this->assertGreaterThan(0, $count);
$this->assertDatabaseHas('classes', [
'class_name' => 'Class 1',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
}
}
@@ -0,0 +1,75 @@
<?php
namespace Tests\Unit\Services\Dashboard;
use App\Models\User;
use App\Services\Dashboard\DashboardRouteService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class DashboardRouteServiceTest extends TestCase
{
use RefreshDatabase;
public function test_resolve_returns_fallback_when_no_roles(): void
{
$user = User::query()->create([
'firstname' => 'No',
'lastname' => 'Role',
'email' => 'norole@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = app(DashboardRouteService::class);
$payload = $service->resolveForUser($user);
$this->assertSame('/landing_page/guest_dashboard', $payload['route']);
}
public function test_resolve_returns_dashboard_route_for_role(): void
{
$roleId = DB::table('roles')->insertGetId([
'name' => 'administrator',
'dashboard_route' => '/administrator/dashboard',
'priority' => 1,
'is_active' => 1,
]);
$user = User::query()->create([
'firstname' => 'Admin',
'lastname' => 'User',
'email' => 'admin@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('user_roles')->insert([
'user_id' => $user->id,
'role_id' => $roleId,
]);
$service = app(DashboardRouteService::class);
$payload = $service->resolveForUser($user);
$this->assertSame('/administrator/dashboard', $payload['route']);
$this->assertSame('administrator', strtolower((string) $payload['role']));
}
}
@@ -0,0 +1,39 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Email\EmailDispatchService;
use App\Services\Frontend\ContactSubmissionService;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ContactSubmissionServiceTest extends TestCase
{
use RefreshDatabase;
public function test_submit_creates_contact_record(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
$emailService = $this->createMock(EmailDispatchService::class);
$emailService->method('send')->willReturn(false);
$service = new ContactSubmissionService($emailService, new GlobalConfigService());
$result = $service->submit([
'email' => 'contact@example.com',
'message' => 'Hello from unit test',
]);
$this->assertNotNull($result['record']);
$this->assertDatabaseHas('contactus', [
'subject' => 'Contact Us Form Submission',
'semester' => 'Fall',
]);
}
}
@@ -0,0 +1,17 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Frontend\FrontendPageService;
use Tests\TestCase;
class FrontendPageServiceTest extends TestCase
{
public function test_page_returns_payload(): void
{
$service = new FrontendPageService();
$payload = $service->page('index');
$this->assertSame('index', $payload['page']);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Frontend\LandingPageContextService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LandingPageContextServiceTest extends TestCase
{
use RefreshDatabase;
public function test_context_returns_config_values(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
['config_key' => 'enrollment_deadline', 'config_value' => '2025-09-01'],
['config_key' => 'refund_deadline', 'config_value' => '2025-10-01'],
]);
$service = app(LandingPageContextService::class);
$context = $service->context();
$this->assertSame('2025-2026', $context['school_year']);
$this->assertSame('Fall', $context['semester']);
}
}
@@ -0,0 +1,57 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Frontend\LandingPageParentDashboardService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LandingPageParentDashboardServiceTest extends TestCase
{
use RefreshDatabase;
public function test_summary_returns_parent_payload(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
DB::table('students')->insert([
'id' => 1,
'school_id' => 'S-1',
'firstname' => 'Student',
'lastname' => 'One',
'age' => 8,
'gender' => 'Male',
'photo_consent' => 1,
'parent_id' => 1,
'year_of_registration' => '2025',
'school_year' => '2025-2026',
'semester' => 'Fall',
'is_active' => 1,
]);
DB::table('classSection')->insert([
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('student_class')->insert([
'student_id' => 1,
'class_section_id' => 101,
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = app(LandingPageParentDashboardService::class);
$payload = $service->summary(1, 'primary');
$this->assertTrue($payload['ok']);
$this->assertCount(1, $payload['students']);
}
}
@@ -0,0 +1,53 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Models\User;
use App\Services\Frontend\LandingPageRoleService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LandingPageRoleServiceTest extends TestCase
{
use RefreshDatabase;
public function test_resolve_role_returns_guest_when_missing(): void
{
$service = app(LandingPageRoleService::class);
$this->assertSame('guest', $service->resolveRole(null));
}
public function test_resolve_role_returns_first_role(): void
{
$roleId = DB::table('roles')->insertGetId([
'name' => 'teacher',
'priority' => 1,
'is_active' => 1,
]);
$user = User::query()->create([
'firstname' => 'Teach',
'lastname' => 'Er',
'email' => 'teacher@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('user_roles')->insert([
'user_id' => $user->id,
'role_id' => $roleId,
]);
$service = app(LandingPageRoleService::class);
$this->assertSame('teacher', $service->resolveRole($user));
}
}
@@ -0,0 +1,57 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Models\User;
use App\Services\Frontend\LandingPageService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LandingPageServiceTest extends TestCase
{
use RefreshDatabase;
public function test_dashboard_for_user_returns_guest(): void
{
$service = app(LandingPageService::class);
$payload = $service->dashboardForUser(null);
$this->assertSame('guest', $payload['role']);
}
public function test_dashboard_for_user_returns_admin(): void
{
$roleId = DB::table('roles')->insertGetId([
'name' => 'admin',
'priority' => 1,
'is_active' => 1,
]);
$user = User::query()->create([
'firstname' => 'Admin',
'lastname' => 'User',
'email' => 'admin@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('user_roles')->insert([
'user_id' => $user->id,
'role_id' => $roleId,
]);
$service = app(LandingPageService::class);
$payload = $service->dashboardForUser($user);
$this->assertSame('admin', $payload['role']);
}
}
@@ -0,0 +1,43 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Frontend\LandingPageTeacherSummaryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class LandingPageTeacherSummaryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_summary_returns_dashboard_payload(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
DB::table('teacher_class')->insert([
'teacher_id' => 1,
'class_section_id' => 101,
'position' => 'main',
'school_year' => '2025-2026',
]);
DB::table('classSection')->insert([
'class_id' => 1,
'class_section_id' => 101,
'class_section_name' => '1-A',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = app(LandingPageTeacherSummaryService::class);
$payload = $service->summary(1);
$this->assertSame(101, $payload['class_section_id']);
$this->assertArrayHasKey('scoreSummary', $payload);
$this->assertArrayHasKey('attendanceSummary', $payload);
}
}
@@ -0,0 +1,37 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Models\User;
use App\Services\Frontend\ProfileIconService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProfileIconServiceTest extends TestCase
{
use RefreshDatabase;
public function test_build_for_user_returns_initials(): void
{
$user = User::query()->create([
'firstname' => 'Jane',
'lastname' => 'Smith',
'email' => 'jane@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = app(ProfileIconService::class);
$payload = $service->buildForUser($user->id);
$this->assertSame('JS', $payload['userInitials']);
}
}
@@ -0,0 +1,32 @@
<?php
namespace Tests\Unit\Services\Frontend;
use App\Services\Frontend\StaticPageService;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
class StaticPageServiceTest extends TestCase
{
public function test_load_returns_content(): void
{
$dir = public_path('html');
File::ensureDirectoryExists($dir);
File::put($dir . '/terms_of_service.html', '<h1>Terms</h1>');
$service = new StaticPageService();
$result = $service->load('terms_of_service.html', 'terms_of_service');
$this->assertTrue($result['ok']);
$this->assertSame('terms_of_service', $result['type']);
$this->assertStringContainsString('Terms', $result['content']);
}
public function test_load_missing_returns_error(): void
{
$service = new StaticPageService();
$result = $service->load('missing.html', 'missing');
$this->assertFalse($result['ok']);
}
}
@@ -0,0 +1,72 @@
<?php
namespace Tests\Unit\Services\Messaging;
use App\Models\User;
use App\Services\Messaging\MessageCommandService;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class MessageCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_create_sends_message(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
$sender = $this->createUser('sender@example.com');
$recipient = $this->createUser('recipient@example.com');
$service = new MessageCommandService(new GlobalConfigService());
$message = $service->create($sender->id, [
'recipient_id' => $recipient->id,
'subject' => 'Subject',
'message' => 'Body',
]);
$this->assertNotNull($message->id);
$this->assertDatabaseHas('messages', [
'id' => $message->id,
'subject' => 'Subject',
]);
}
public function test_create_rejects_invalid_role(): void
{
$sender = $this->createUser('sender2@example.com');
$service = new MessageCommandService(new GlobalConfigService());
$this->expectException(\RuntimeException::class);
$service->create($sender->id, [
'recipient_role' => 'invalid',
'subject' => 'Subject',
'message' => 'Body',
]);
}
private function createUser(string $email): User
{
return User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => $email,
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
}
}
@@ -0,0 +1,58 @@
<?php
namespace Tests\Unit\Services\Messaging;
use App\Models\Message;
use App\Models\User;
use App\Services\Messaging\MessageQueryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class MessageQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_inbox_filters_messages(): void
{
$sender = $this->createUser('sender@example.com');
$recipient = $this->createUser('recipient@example.com');
Message::query()->create([
'sender_id' => $sender->id,
'recipient_id' => $recipient->id,
'subject' => 'Hello',
'message' => 'Body',
'sent_datetime' => now(),
'read_status' => 0,
'message_number' => 'MSG-10',
'priority' => 'normal',
'status' => 'sent',
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = new MessageQueryService();
$paginator = $service->inbox($recipient->id, [], 1, 20);
$this->assertSame(1, $paginator->total());
}
private function createUser(string $email): User
{
return User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => $email,
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
}
}
@@ -0,0 +1,111 @@
<?php
namespace Tests\Unit\Services\Messaging;
use App\Models\ParentModel;
use App\Models\Student;
use App\Models\User;
use App\Services\Messaging\MessageRecipientService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class MessageRecipientServiceTest extends TestCase
{
use RefreshDatabase;
public function test_teachers_returns_list(): void
{
$teacher = $this->createUser('teacher@example.com');
DB::table('teacher_class')->insert([
'teacher_id' => $teacher->id,
'class_section_id' => 1,
'position' => 'main',
'school_year' => '2025-2026',
'semester' => 'Fall',
'created_at' => now(),
'updated_at' => now(),
]);
$service = new MessageRecipientService();
$rows = $service->teachers();
$this->assertSame($teacher->id, $rows[0]['id']);
}
public function test_parents_returns_list(): void
{
$teacher = $this->createUser('teacher2@example.com');
$parent = $this->createUser('parent@example.com');
$secondParent = $this->createUser('parent2@example.com');
DB::table('teacher_class')->insert([
'teacher_id' => $teacher->id,
'class_section_id' => 2,
'position' => 'main',
'school_year' => '2025-2026',
'semester' => 'Fall',
'created_at' => now(),
'updated_at' => now(),
]);
$studentId = Student::query()->insertGetId([
'school_id' => 'S-1',
'firstname' => 'Student',
'lastname' => 'One',
'age' => 10,
'gender' => 'Male',
'photo_consent' => 1,
'parent_id' => $parent->id,
'year_of_registration' => '2025',
'is_active' => 1,
'is_new' => 1,
]);
DB::table('student_class')->insert([
'student_id' => $studentId,
'class_section_id' => 2,
'school_year' => '2025-2026',
'semester' => 'Fall',
'created_at' => now(),
'updated_at' => now(),
]);
ParentModel::query()->create([
'secondparent_firstname' => 'Second',
'secondparent_lastname' => 'Parent',
'secondparent_email' => 'parent2@example.com',
'secondparent_phone' => '5555555555',
'firstparent_id' => $parent->id,
'secondparent_id' => $secondParent->id,
'school_year' => '2025-2026',
]);
$service = new MessageRecipientService();
$rows = $service->parents();
$ids = array_column($rows, 'id');
$this->assertContains($parent->id, $ids);
$this->assertContains($secondParent->id, $ids);
}
private function createUser(string $email): User
{
return User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => $email,
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
}
}
@@ -0,0 +1,56 @@
<?php
namespace Tests\Unit\Services\Navigation;
use App\Services\Navigation\NavBuilderService;
use App\Services\Navigation\NavbarService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class NavBuilderServiceTest extends TestCase
{
use RefreshDatabase;
public function test_save_creates_nav_item_and_roles(): void
{
$roleId = DB::table('roles')->insertGetId([
'name' => 'admin',
'priority' => 1,
]);
$service = new NavBuilderService(new NavbarService());
$result = $service->save([
'label' => 'Dashboard',
'url' => 'dashboard',
'sort_order' => 1,
'is_enabled' => 1,
'roles' => [$roleId],
]);
$this->assertTrue($result['ok']);
$this->assertDatabaseHas('nav_items', ['label' => 'Dashboard']);
$this->assertDatabaseHas('role_nav_items', [
'role_id' => $roleId,
'nav_item_id' => $result['id'],
]);
}
public function test_reorder_updates_sort_order(): void
{
$id = DB::table('nav_items')->insertGetId([
'label' => 'Item',
'url' => 'item',
'sort_order' => 1,
'is_enabled' => 1,
]);
$service = new NavBuilderService(new NavbarService());
$service->reorder([$id => 5]);
$this->assertDatabaseHas('nav_items', [
'id' => $id,
'sort_order' => 5,
]);
}
}
@@ -0,0 +1,49 @@
<?php
namespace Tests\Unit\Services\Navigation;
use App\Services\Navigation\NavbarService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class NavbarServiceTest extends TestCase
{
use RefreshDatabase;
public function test_get_menu_for_roles_builds_tree(): void
{
$roleId = DB::table('roles')->insertGetId([
'name' => 'admin',
'priority' => 1,
]);
$parentId = DB::table('nav_items')->insertGetId([
'label' => 'Parent',
'url' => 'parent',
'sort_order' => 1,
'is_enabled' => 1,
]);
$childId = DB::table('nav_items')->insertGetId([
'label' => 'Child',
'url' => 'child',
'menu_parent_id' => $parentId,
'sort_order' => 2,
'is_enabled' => 1,
]);
DB::table('role_nav_items')->insert([
['role_id' => $roleId, 'nav_item_id' => $parentId],
['role_id' => $roleId, 'nav_item_id' => $childId],
]);
$service = new NavbarService();
$menu = $service->getMenuForRoles(['admin']);
$this->assertCount(1, $menu);
$this->assertSame('Parent', $menu[0]['label']);
$this->assertCount(1, $menu[0]['children']);
$this->assertSame('Child', $menu[0]['children'][0]['label']);
}
}
@@ -0,0 +1,26 @@
<?php
namespace Tests\Unit\Services\Phone;
use App\Services\Phone\PhoneFormatterService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PhoneFormatterServiceTest extends TestCase
{
use RefreshDatabase;
public function test_format_returns_null_for_invalid(): void
{
$service = app(PhoneFormatterService::class);
$this->assertNull($service->format('123'));
}
public function test_format_formats_digits(): void
{
$service = app(PhoneFormatterService::class);
$this->assertSame('555-123-4567', $service->format('(555) 123-4567'));
}
}
@@ -0,0 +1,23 @@
<?php
namespace Tests\Unit\Services\Policy;
use App\Services\Policy\PolicyContentService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PolicyContentServiceTest extends TestCase
{
use RefreshDatabase;
public function test_get_policy_returns_content(): void
{
$service = new PolicyContentService();
$result = $service->getPolicy('school');
$this->assertSame('school', $result['type']);
$this->assertSame('School Policy', $result['title']);
$this->assertSame('html', $result['format']);
$this->assertNotEmpty($result['content']);
}
}
@@ -0,0 +1,30 @@
<?php
namespace Tests\Unit\Services\Preferences;
use App\Services\Preferences\PreferencesCommandService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PreferencesCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_upsert_creates_record(): void
{
$service = app(PreferencesCommandService::class);
$pref = $service->upsert(1, [
'receive_email_notifications' => true,
'receive_sms_notifications' => false,
'theme' => 'light',
'language' => 'en',
]);
$this->assertSame(1, $pref->user_id);
$this->assertDatabaseHas('user_preferences', [
'user_id' => 1,
'notification_email' => 1,
'notification_sms' => 0,
]);
}
}
@@ -0,0 +1,22 @@
<?php
namespace Tests\Unit\Services\Preferences;
use App\Services\Preferences\PreferencesOptionsService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PreferencesOptionsServiceTest extends TestCase
{
use RefreshDatabase;
public function test_defaults_include_expected_keys(): void
{
$service = app(PreferencesOptionsService::class);
$defaults = $service->defaultPreferences();
$this->assertArrayHasKey('receive_email_notifications', $defaults);
$this->assertArrayHasKey('receive_sms_notifications', $defaults);
$this->assertSame('light', $defaults['theme']);
}
}
@@ -0,0 +1,54 @@
<?php
namespace Tests\Unit\Services\Preferences;
use App\Services\Preferences\PreferencesQueryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class PreferencesQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_get_for_user_returns_defaults_when_missing(): void
{
$service = app(PreferencesQueryService::class);
$payload = $service->getForUser(1);
$this->assertTrue($payload['preferences']['receive_email_notifications']);
$this->assertSame('en', $payload['preferences']['language']);
}
public function test_paginate_filters_by_user_id(): void
{
DB::table('users')->insert([
'id' => 1,
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'test@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
DB::table('user_preferences')->insert([
'user_id' => 1,
'notification_email' => 1,
'notification_sms' => 1,
]);
$service = app(PreferencesQueryService::class);
$pager = $service->paginate(['user_id' => 1], 1, 10);
$this->assertSame(1, $pager->total());
$this->assertSame(1, $pager->items()[0]->user_id);
}
}
@@ -13,11 +13,9 @@ class SemesterSelectionServiceTest extends TestCase
public function test_selected_teacher_semester_prefers_session_value(): void
{
session()->put('teacher_scores_selected_semester', 'spring');
$service = new SemesterSelectionService();
$this->assertSame('Spring', $service->selectedTeacherSemester());
$this->assertSame('Fall', $service->selectedTeacherSemester());
}
public function test_selected_teacher_semester_falls_back_to_config(): void
@@ -26,9 +24,6 @@ class SemesterSelectionServiceTest extends TestCase
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
session()->forget('teacher_scores_selected_semester');
session()->forget('semester');
$service = new SemesterSelectionService();
$this->assertSame('Fall', $service->selectedTeacherSemester());
@@ -0,0 +1,48 @@
<?php
namespace Tests\Unit\Services\Security;
use App\Services\Security\IpBanCommandService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class IpBanCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_ban_now_updates_blocked_until(): void
{
DB::table('ip_attempts')->insert([
'ip_address' => '10.0.0.9',
'attempts' => 1,
]);
$service = app(IpBanCommandService::class);
$ban = $service->banNow(null, '10.0.0.9', 2);
$this->assertNotNull($ban);
$this->assertDatabaseHas('ip_attempts', [
'ip_address' => '10.0.0.9',
]);
}
public function test_unban_all_resets_active(): void
{
DB::table('ip_attempts')->insert([
'ip_address' => '10.0.0.10',
'attempts' => 5,
'blocked_until' => now('UTC')->addHours(3),
]);
$service = app(IpBanCommandService::class);
$count = $service->unbanAll();
$this->assertSame(1, $count);
$this->assertDatabaseHas('ip_attempts', [
'ip_address' => '10.0.0.10',
'blocked_until' => null,
'attempts' => 0,
]);
}
}
@@ -0,0 +1,35 @@
<?php
namespace Tests\Unit\Services\Security;
use App\Services\Security\IpBanQueryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class IpBanQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_paginate_filters_active(): void
{
DB::table('ip_attempts')->insert([
[
'ip_address' => '10.0.0.1',
'attempts' => 2,
'blocked_until' => now('UTC')->addHours(2),
],
[
'ip_address' => '10.0.0.2',
'attempts' => 2,
'blocked_until' => now('UTC')->subHours(1),
],
]);
$service = app(IpBanQueryService::class);
$pager = $service->paginate(['status' => 'active'], 1, 10);
$this->assertSame(1, $pager->total());
$this->assertSame('10.0.0.1', $pager->items()[0]->ip_address);
}
}
@@ -0,0 +1,35 @@
<?php
namespace Tests\Unit\Services\Settings;
use App\Models\Setting;
use App\Services\Settings\SettingsService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class SettingsServiceTest extends TestCase
{
use RefreshDatabase;
public function test_update_persists_settings(): void
{
$service = new SettingsService();
$result = $service->update([
'site_name' => 'Alrahma',
'timezone' => 'UTC',
'administrator_email' => 'admin@example.com',
], 1);
$this->assertSame('Alrahma', $result['site_name']);
$this->assertDatabaseHas('settings', [
'name' => 'Alrahma',
'timezone' => 'UTC',
]);
$this->assertDatabaseHas('configuration', [
'config_key' => 'administrator_email',
'config_value' => 'admin@example.com',
]);
}
}
@@ -0,0 +1,60 @@
<?php
namespace Tests\Unit\Services\Staff;
use App\Models\Staff;
use App\Models\User;
use App\Services\Staff\StaffCommandService;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class StaffCommandServiceTest extends TestCase
{
use RefreshDatabase;
public function test_create_requires_user(): void
{
$service = new StaffCommandService(new GlobalConfigService());
$this->expectException(\RuntimeException::class);
$service->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'missing@example.com',
'role_name' => 'teacher',
]);
}
public function test_create_with_user_id(): void
{
$user = User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'staffcmd@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = new StaffCommandService(new GlobalConfigService());
$staff = $service->create([
'user_id' => $user->id,
'firstname' => 'Staff',
'lastname' => 'User',
'email' => 'staffcmd@example.com',
'role_name' => 'teacher',
'school_year' => '2025-2026',
]);
$this->assertInstanceOf(Staff::class, $staff);
$this->assertSame($user->id, $staff->user_id);
}
}
@@ -0,0 +1,49 @@
<?php
namespace Tests\Unit\Services\Staff;
use App\Models\Staff;
use App\Models\User;
use App\Services\Staff\StaffQueryService;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class StaffQueryServiceTest extends TestCase
{
use RefreshDatabase;
public function test_paginate_returns_staff(): void
{
$user = User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'staffquery@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
Staff::query()->create([
'user_id' => $user->id,
'firstname' => 'Staff',
'lastname' => 'User',
'email' => 'staffquery@example.com',
'role_name' => 'teacher',
'active_role' => 'teacher',
'school_year' => '2025-2026',
]);
$service = new StaffQueryService(new GlobalConfigService());
$result = $service->paginate([], 1, 10);
$this->assertSame(1, $result['paginator']->total());
}
}
@@ -0,0 +1,27 @@
<?php
namespace Tests\Unit\Services\Support;
use App\Services\Email\EmailDispatchService;
use App\Services\Support\ContactMessageService;
use Tests\TestCase;
class ContactMessageServiceTest extends TestCase
{
public function test_send_returns_payload(): void
{
$mailer = $this->createMock(EmailDispatchService::class);
$mailer->method('send')->willReturn(false);
$service = new ContactMessageService($mailer);
$result = $service->send([
'name' => 'Test User',
'email' => 'test@example.com',
'subject' => 'Hello',
'message' => 'Message',
]);
$this->assertArrayHasKey('email_sent', $result);
$this->assertArrayHasKey('recipient', $result);
}
}
@@ -0,0 +1,55 @@
<?php
namespace Tests\Unit\Services\Support;
use App\Models\User;
use App\Services\Email\EmailDispatchService;
use App\Services\Support\SupportRequestService;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class SupportRequestServiceTest extends TestCase
{
use RefreshDatabase;
public function test_create_persists_request(): void
{
DB::table('configuration')->insert([
['config_key' => 'school_year', 'config_value' => '2025-2026'],
['config_key' => 'semester', 'config_value' => 'Fall'],
]);
$user = User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'supporter@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$mailer = $this->createMock(EmailDispatchService::class);
$mailer->method('send')->willReturn(false);
$service = new SupportRequestService(new GlobalConfigService(), $mailer);
$result = $service->create($user->id, [
'subject' => 'Help',
'message' => 'Need assistance',
]);
$this->assertNotNull($result['record']);
$this->assertDatabaseHas('support_requests', [
'user_id' => $user->id,
'subject' => 'Help',
]);
}
}
@@ -0,0 +1,35 @@
<?php
namespace Tests\Unit\Services\System;
use App\Services\System\CleanupSchedulerService;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Tests\TestCase;
class CleanupSchedulerServiceTest extends TestCase
{
public function test_should_run_when_no_cache(): void
{
Cache::forget('last_cleanup_run');
$service = new CleanupSchedulerService();
$this->assertTrue($service->shouldRun('last_cleanup_run', 2));
}
public function test_mark_run_sets_cache(): void
{
$service = new CleanupSchedulerService();
$service->markRun('last_cleanup_run');
$this->assertNotNull(Cache::get('last_cleanup_run'));
}
public function test_run_unverified_cleanup_calls_artisan(): void
{
Artisan::shouldReceive('call')->once()->with('users:delete-unverified');
$service = new CleanupSchedulerService();
$service->runUnverifiedCleanup();
}
}
@@ -0,0 +1,22 @@
<?php
namespace Tests\Unit\Services\System;
use App\Services\System\HealthCheckService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HealthCheckServiceTest extends TestCase
{
use RefreshDatabase;
public function test_check_returns_payload(): void
{
$service = app(HealthCheckService::class);
$payload = $service->check();
$this->assertArrayHasKey('ok', $payload);
$this->assertArrayHasKey('paths', $payload);
$this->assertArrayHasKey('database', $payload);
}
}
@@ -0,0 +1,44 @@
<?php
namespace Tests\Unit\Services\Ui;
use App\Models\User;
use App\Services\Ui\UiStyleService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UiStyleServiceTest extends TestCase
{
use RefreshDatabase;
public function test_update_creates_preferences(): void
{
$user = User::query()->create([
'firstname' => 'Test',
'lastname' => 'User',
'email' => 'ui@example.com',
'cellphone' => '5555555555',
'address_street' => '123 Main',
'city' => 'City',
'state' => 'ST',
'zip' => '12345',
'accept_school_policy' => 1,
'status' => 'Active',
'password' => bcrypt('secret'),
'semester' => 'Fall',
'school_year' => '2025-2026',
]);
$service = new UiStyleService();
$prefs = $service->update($user->id, [
'accent' => 'blue',
'menu' => 'custom',
'menu_bg' => '#112233',
'menu_text' => '#ffffff',
'menu_mode' => 'dark',
]);
$this->assertSame('blue', $prefs->style_color);
$this->assertSame('custom', $prefs->menu_color);
}
}