add services logic

This commit is contained in:
root
2026-03-11 01:38:20 -04:00
parent 182036cc41
commit 3e6c577085
47 changed files with 918 additions and 1772 deletions
@@ -0,0 +1,51 @@
<?php
namespace Tests\Feature\Console;
use App\Events\DeleteUnverifiedUser;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class DeleteUnverifiedUsersCommandTest extends TestCase
{
use RefreshDatabase;
public function test_command_dispatches_event_for_expired_user(): void
{
DB::table('users')->insert([
'id' => 20,
'firstname' => 'Old',
'lastname' => 'User',
'email' => 'old@example.com',
'status' => 'Inactive',
'is_verified' => 0,
'created_at' => now()->subDays(2),
'updated_at' => now()->subDays(2),
]);
DB::table('users')->insert([
'id' => 21,
'firstname' => 'Recent',
'lastname' => 'User',
'email' => 'recent@example.com',
'status' => 'Inactive',
'is_verified' => 0,
'created_at' => now(),
'updated_at' => now(),
]);
Event::fake();
$this->artisan('users:delete-unverified', ['--timeout' => 3600])->assertExitCode(0);
Event::assertDispatched(DeleteUnverifiedUser::class, function ($event) {
return $event->userId === 20;
});
Event::assertNotDispatched(DeleteUnverifiedUser::class, function ($event) {
return $event->userId === 21;
});
}
}
@@ -0,0 +1,23 @@
<?php
namespace Tests\Unit\Listeners;
use App\Events\DeleteUnverifiedUser;
use App\Listeners\DeleteUnverifiedUserListener;
use App\Services\Users\DeleteUnverifiedUserService;
use Mockery;
use Tests\TestCase;
class DeleteUnverifiedUserListenerTest extends TestCase
{
public function test_listener_invokes_service(): void
{
$service = Mockery::mock(DeleteUnverifiedUserService::class);
$service->shouldReceive('deleteAfterTimeout')->once()->with(15)->andReturn(['ok' => true]);
$listener = new DeleteUnverifiedUserListener($service);
$listener->handle(new DeleteUnverifiedUser(15));
$this->assertTrue(true);
}
}
@@ -0,0 +1,51 @@
<?php
namespace Tests\Unit\Services\Attendance;
use App\Services\Attendance\AttendanceCommentService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class AttendanceCommentServiceTest extends TestCase
{
use RefreshDatabase;
public function test_comment_from_score_uses_template_and_name(): void
{
DB::table('attendance_comment_template')->insert([
[
'min_score' => 90,
'max_score' => 100,
'template_text' => '{name} attendance is excellent.',
'is_active' => 1,
],
[
'min_score' => 0,
'max_score' => 89,
'template_text' => 'needs improvement in attendance.',
'is_active' => 1,
],
]);
$service = new AttendanceCommentService();
$comment = $service->commentFromScore(95, 'James');
$this->assertSame("James' attendance is excellent.", $comment);
}
public function test_comment_from_score_falls_back_to_this_student(): void
{
DB::table('attendance_comment_template')->insert([
'min_score' => 0,
'max_score' => 100,
'template_text' => 'needs improvement in attendance.',
'is_active' => 1,
]);
$service = new AttendanceCommentService();
$comment = $service->commentFromScore(70);
$this->assertSame("This student's needs improvement in attendance.", $comment);
}
}
@@ -0,0 +1,42 @@
<?php
namespace Tests\Unit\Services\Auth;
use App\Services\Auth\PermissionCheckService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class PermissionCheckServiceTest extends TestCase
{
use RefreshDatabase;
public function test_has_permission_returns_true_when_assigned(): void
{
$permissionId = DB::table('permissions')->insertGetId([
'name' => 'edit_students',
'description' => 'Edit students',
]);
$roleId = DB::table('roles')->insertGetId([
'name' => 'admin',
'priority' => 1,
]);
DB::table('user_roles')->insert([
'user_id' => 10,
'role_id' => $roleId,
]);
DB::table('role_permissions')->insert([
'role_id' => $roleId,
'permission_id' => $permissionId,
'can_read' => 1,
]);
$service = new PermissionCheckService();
$this->assertTrue($service->hasPermission(10, 'edit_students'));
$this->assertFalse($service->hasPermission(10, 'missing_perm'));
}
}
@@ -0,0 +1,36 @@
<?php
namespace Tests\Unit\Services\School;
use App\Services\School\SemesterSelectionService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class SemesterSelectionServiceTest extends TestCase
{
use RefreshDatabase;
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());
}
public function test_selected_teacher_semester_falls_back_to_config(): void
{
DB::table('configuration')->insert([
['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,40 @@
<?php
namespace Tests\Unit\Services\Scores;
use App\Models\AttendanceRecord;
use App\Models\CalendarEvent;
use App\Models\Configuration;
use App\Services\Scores\AttendanceCalculator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class AttendanceCalculatorTest extends TestCase
{
use RefreshDatabase;
public function test_calculate_uses_configured_days(): void
{
DB::table('configuration')->insert([
['config_key' => 'total_semester1_days', 'config_value' => '10'],
]);
DB::table('attendance_record')->insert([
'class_section_id' => 1,
'student_id' => 10,
'school_id' => 'S1',
'total_absence' => 2,
'total_late' => 0,
'total_presence' => 0,
'total_attendance' => 0,
'semester' => 'Fall',
'school_year' => '2024-2025',
]);
$service = new AttendanceCalculator(new AttendanceRecord(), new Configuration(), new CalendarEvent());
$result = $service->calculate(10, 'Fall', '2024-2025');
$this->assertSame(90.0, $result['attendance_score']);
}
}
@@ -0,0 +1,55 @@
<?php
namespace Tests\Unit\Services\Scores;
use App\Models\Homework;
use App\Services\Scores\HomeworkCalculator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class HomeworkCalculatorTest extends TestCase
{
use RefreshDatabase;
public function test_calculate_returns_average_score(): void
{
DB::table('homework')->insert([
[
'student_id' => 5,
'school_id' => 'S1',
'class_section_id' => 1,
'updated_by' => 1,
'homework_index' => 1,
'score' => 80,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 5,
'school_id' => 'S1',
'class_section_id' => 1,
'updated_by' => 1,
'homework_index' => 2,
'score' => 90,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 5,
'school_id' => 'S1',
'class_section_id' => 1,
'updated_by' => 1,
'homework_index' => 3,
'score' => null,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
]);
$service = new HomeworkCalculator(new Homework());
$result = $service->calculate(5, 'Fall', '2024-2025', 1);
$this->assertSame(85.0, $result['homework_avg']);
}
}
@@ -0,0 +1,55 @@
<?php
namespace Tests\Unit\Services\Scores;
use App\Models\Project;
use App\Services\Scores\ProjectCalculator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ProjectCalculatorTest extends TestCase
{
use RefreshDatabase;
public function test_calculate_returns_average_score(): void
{
DB::table('project')->insert([
[
'student_id' => 9,
'school_id' => 'S1',
'class_section_id' => 1,
'updated_by' => 1,
'project_index' => 1,
'score' => 95,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 9,
'school_id' => 'S1',
'class_section_id' => 1,
'updated_by' => 1,
'project_index' => 2,
'score' => 85,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 9,
'school_id' => 'S1',
'class_section_id' => 2,
'updated_by' => 1,
'project_index' => 3,
'score' => 100,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
]);
$service = new ProjectCalculator(new Project());
$result = $service->calculate(9, 'Fall', '2024-2025', 1);
$this->assertSame(90.0, $result['project_avg']);
}
}
@@ -0,0 +1,64 @@
<?php
namespace Tests\Unit\Services\Scores;
use App\Services\Scores\QuizCalculator;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class QuizCalculatorTest extends TestCase
{
use RefreshDatabase;
public function test_calculate_returns_average_score(): void
{
DB::table('quiz')->insert([
[
'student_id' => 7,
'school_id' => 'S1',
'class_section_id' => 2,
'updated_by' => 1,
'quiz_index' => 1,
'score' => 70,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 7,
'school_id' => 'S1',
'class_section_id' => 2,
'updated_by' => 1,
'quiz_index' => 2,
'score' => 90,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 7,
'school_id' => 'S1',
'class_section_id' => 3,
'updated_by' => 1,
'quiz_index' => 3,
'score' => 100,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
[
'student_id' => 7,
'school_id' => 'S1',
'class_section_id' => 2,
'updated_by' => 1,
'quiz_index' => 4,
'score' => null,
'semester' => 'Fall',
'school_year' => '2024-2025',
],
]);
$service = new QuizCalculator();
$result = $service->calculate(7, 'Fall', '2024-2025', 2);
$this->assertSame(80.0, $result['quiz_avg']);
}
}
@@ -0,0 +1,18 @@
<?php
namespace Tests\Unit\Services\Security;
use App\Services\Security\Pbkdf2Hasher;
use Tests\TestCase;
class Pbkdf2HasherTest extends TestCase
{
public function test_hash_and_verify(): void
{
$hasher = new Pbkdf2Hasher();
$hash = $hasher->hash('secret');
$this->assertTrue($hasher->verify('secret', $hash));
$this->assertFalse($hasher->verify('wrong', $hash));
}
}
@@ -0,0 +1,26 @@
<?php
namespace Tests\Unit\Services\System;
use App\Services\System\GlobalConfigService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class GlobalConfigServiceTest extends TestCase
{
use RefreshDatabase;
public function test_get_semester_and_school_year(): void
{
DB::table('configuration')->insert([
['config_key' => 'semester', 'config_value' => 'Fall'],
['config_key' => 'school_year', 'config_value' => '2024-2025'],
]);
$service = new GlobalConfigService();
$this->assertSame('Fall', $service->getSemester());
$this->assertSame('2024-2025', $service->getSchoolYear());
}
}
@@ -0,0 +1,18 @@
<?php
namespace Tests\Unit\Services\System;
use App\Services\System\TimeService;
use Tests\TestCase;
class TimeServiceTest extends TestCase
{
public function test_to_utc_and_to_local_round_trip(): void
{
$service = new TimeService();
$utc = $service->toUtc('2025-01-01 12:00:00', 'America/New_York');
$local = $service->toLocal($utc, 'UTC', 'America/New_York');
$this->assertSame('2025-01-01 12:00:00', $local);
}
}
@@ -0,0 +1,68 @@
<?php
namespace Tests\Unit\Services\Users;
use App\Services\School\AccountEventService;
use App\Services\Users\DeleteUnverifiedUserService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
use Tests\TestCase;
class DeleteUnverifiedUserServiceTest extends TestCase
{
use RefreshDatabase;
public function test_delete_after_timeout_deletes_user(): void
{
DB::table('users')->insert([
'id' => 10,
'firstname' => 'Unverified',
'lastname' => 'User',
'email' => 'u@example.com',
'status' => 'Inactive',
'is_verified' => 0,
'created_at' => now()->subDays(2),
'updated_at' => now()->subDays(2),
]);
DB::table('user_roles')->insert([
'user_id' => 10,
'role_id' => 1,
]);
$accountEvents = Mockery::mock(AccountEventService::class);
$accountEvents->shouldReceive('deleteUnverifiedUser')->once();
$service = new DeleteUnverifiedUserService($accountEvents);
$result = $service->deleteAfterTimeout(10, 3600);
$this->assertTrue($result['ok']);
$this->assertDatabaseMissing('users', ['id' => 10]);
$this->assertDatabaseMissing('user_roles', ['user_id' => 10]);
}
public function test_delete_after_timeout_skips_recent_user(): void
{
DB::table('users')->insert([
'id' => 11,
'firstname' => 'Recent',
'lastname' => 'User',
'email' => 'recent@example.com',
'status' => 'Inactive',
'is_verified' => 0,
'created_at' => now(),
'updated_at' => now(),
]);
$accountEvents = Mockery::mock(AccountEventService::class);
$accountEvents->shouldNotReceive('deleteUnverifiedUser');
$service = new DeleteUnverifiedUserService($accountEvents);
$result = $service->deleteAfterTimeout(11, 86400);
$this->assertFalse($result['ok']);
$this->assertSame('not_expired', $result['reason']);
$this->assertDatabaseHas('users', ['id' => 11]);
}
}