reconstruction of the project
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Badges;
|
||||
|
||||
use App\Services\Badges\BadgeFormDataService;
|
||||
use App\Services\Badges\BadgeTextFormatter;
|
||||
use App\Services\Badges\BadgeUserLookupService;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BadgeFormDataServiceTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_build_formats_users_and_assigns_latest_class_for_teacherish_roles(): void
|
||||
{
|
||||
$lookup = Mockery::mock(BadgeUserLookupService::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$lookup->shouldReceive('fetchStaffList')
|
||||
->once()
|
||||
->with('2025-2026', [10, 20])
|
||||
->andReturn([
|
||||
[
|
||||
'id' => 10,
|
||||
'firstname' => 'John',
|
||||
'lastname' => 'Doe',
|
||||
'roles' => 'teacher_assistant,admin',
|
||||
],
|
||||
[
|
||||
'id' => 20,
|
||||
'firstname' => 'Sara',
|
||||
'lastname' => 'Smith',
|
||||
'roles' => 'staff',
|
||||
],
|
||||
]);
|
||||
|
||||
$lookup->shouldReceive('getLatestClassForUser')
|
||||
->once()
|
||||
->with(10, '2025-2026')
|
||||
->andReturn([
|
||||
'class_section_id' => 5,
|
||||
'class_section_name' => '3-A',
|
||||
]);
|
||||
|
||||
$lookup->shouldReceive('getAvailableSchoolYears')
|
||||
->once()
|
||||
->andReturn(['2025-2026', '2024-2025']);
|
||||
|
||||
$service = new BadgeFormDataService($lookup, $formatter);
|
||||
|
||||
$result = $service->build('2025-2026', [10, 20], 'teacher');
|
||||
|
||||
$this->assertSame('2025-2026', $result['selectedYear']);
|
||||
$this->assertSame([10, 20], $result['selectedUserIds']);
|
||||
$this->assertSame('teacher', $result['active_role']);
|
||||
$this->assertCount(2, $result['users']);
|
||||
|
||||
$this->assertSame(10, $result['users'][0]['user_id']);
|
||||
$this->assertSame('Teacher Assistant, Admin', $result['users'][0]['roles']);
|
||||
$this->assertSame('teacher_assistant', $result['users'][0]['role_name_raw']);
|
||||
$this->assertSame('Teacher Assistant', $result['users'][0]['role_name']);
|
||||
$this->assertSame(5, $result['users'][0]['class_section_id']);
|
||||
$this->assertSame('3-A', $result['users'][0]['class_section_name']);
|
||||
|
||||
$this->assertSame(20, $result['users'][1]['user_id']);
|
||||
$this->assertSame('Staff', $result['users'][1]['roles']);
|
||||
$this->assertNull($result['users'][1]['class_section_id']);
|
||||
}
|
||||
|
||||
public function test_build_defaults_invalid_active_role_to_teacher(): void
|
||||
{
|
||||
$lookup = Mockery::mock(BadgeUserLookupService::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$lookup->shouldReceive('fetchStaffList')
|
||||
->once()
|
||||
->andReturn([]);
|
||||
|
||||
$lookup->shouldReceive('getAvailableSchoolYears')
|
||||
->once()
|
||||
->andReturn([]);
|
||||
|
||||
$service = new BadgeFormDataService($lookup, $formatter);
|
||||
|
||||
$result = $service->build('2025-2026', [], 'invalid-role');
|
||||
|
||||
$this->assertSame('teacher', $result['active_role']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Badges;
|
||||
|
||||
use App\Services\Badges\BadgePdfService;
|
||||
use App\Services\Badges\BadgePrintLogService;
|
||||
use App\Services\Badges\BadgeTextFormatter;
|
||||
use App\Services\Badges\BadgeUserLookupService;
|
||||
use Mockery;
|
||||
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BadgePdfServiceTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_generate_returns_pdf_response_and_logs_prints(): void
|
||||
{
|
||||
$lookup = Mockery::mock(BadgeUserLookupService::class);
|
||||
$printLog = Mockery::mock(BadgePrintLogService::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$lookup->shouldReceive('getUserInfoById')
|
||||
->once()
|
||||
->with(10, '2025-2026')
|
||||
->andReturn([
|
||||
'user_id' => 10,
|
||||
'name' => 'John Doe',
|
||||
'role' => 'Teacher',
|
||||
'roles' => 'Teacher',
|
||||
'role_name_raw' => 'teacher',
|
||||
'roles_raw' => 'teacher',
|
||||
'class_section_name' => '3-A',
|
||||
'school_name' => 'Al Rahma Sunday School',
|
||||
'school_logo' => null,
|
||||
'isgl_logo' => null,
|
||||
'school_year' => '2025-2026',
|
||||
]);
|
||||
|
||||
$printLog->shouldReceive('logSafely')
|
||||
->once()
|
||||
->with([10], 1, '2025-2026', [], [], 1);
|
||||
|
||||
$service = new BadgePdfService($lookup, $printLog, $formatter);
|
||||
|
||||
$response = $service->generate(
|
||||
userIds: [10],
|
||||
schoolYear: '2025-2026',
|
||||
rolesMap: [],
|
||||
classesMap: [],
|
||||
actorId: 1
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(SymfonyResponse::class, $response);
|
||||
$this->assertSame('application/pdf', $response->headers->get('Content-Type'));
|
||||
$this->assertStringContainsString('inline; filename="Staff_Badges.pdf"', $response->headers->get('Content-Disposition'));
|
||||
$this->assertNotEmpty($response->getContent());
|
||||
}
|
||||
|
||||
public function test_generate_returns_empty_state_pdf_when_no_valid_users_found(): void
|
||||
{
|
||||
$lookup = Mockery::mock(BadgeUserLookupService::class);
|
||||
$printLog = Mockery::mock(BadgePrintLogService::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$lookup->shouldReceive('getUserInfoById')
|
||||
->once()
|
||||
->with(99, null)
|
||||
->andReturn(null);
|
||||
|
||||
$printLog->shouldReceive('logSafely')
|
||||
->once()
|
||||
->with([99], null, null, [], [], 1);
|
||||
|
||||
$service = new BadgePdfService($lookup, $printLog, $formatter);
|
||||
|
||||
$response = $service->generate(
|
||||
userIds: [99],
|
||||
schoolYear: null,
|
||||
rolesMap: [],
|
||||
classesMap: [],
|
||||
actorId: null
|
||||
);
|
||||
|
||||
$this->assertSame('application/pdf', $response->headers->get('Content-Type'));
|
||||
$this->assertNotEmpty($response->getContent());
|
||||
}
|
||||
|
||||
public function test_generate_deduplicates_same_badge_content(): void
|
||||
{
|
||||
$lookup = Mockery::mock(BadgeUserLookupService::class);
|
||||
$printLog = Mockery::mock(BadgePrintLogService::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$userInfo = [
|
||||
'user_id' => 10,
|
||||
'name' => 'John Doe',
|
||||
'role' => 'Teacher',
|
||||
'roles' => 'Teacher',
|
||||
'role_name_raw' => 'teacher',
|
||||
'roles_raw' => 'teacher',
|
||||
'class_section_name' => '3-A',
|
||||
'school_name' => 'Al Rahma Sunday School',
|
||||
'school_logo' => null,
|
||||
'isgl_logo' => null,
|
||||
'school_year' => '2025-2026',
|
||||
];
|
||||
|
||||
$lookup->shouldReceive('getUserInfoById')->twice()->andReturn($userInfo);
|
||||
|
||||
$printLog->shouldReceive('logSafely')
|
||||
->once()
|
||||
->with([10], 7, '2025-2026', [], [], 1);
|
||||
|
||||
$service = new BadgePdfService($lookup, $printLog, $formatter);
|
||||
|
||||
$response = $service->generate(
|
||||
userIds: [10, 10],
|
||||
schoolYear: '2025-2026',
|
||||
rolesMap: [],
|
||||
classesMap: [],
|
||||
actorId: 7
|
||||
);
|
||||
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
$this->assertSame('application/pdf', $response->headers->get('Content-Type'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Badges;
|
||||
|
||||
use App\Models\BadgePrintLog;
|
||||
use App\Services\Badges\BadgePrintLogService;
|
||||
use App\Services\Badges\BadgeTextFormatter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BadgePrintLogServiceTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_get_status_normalizes_ids_before_calling_model(): void
|
||||
{
|
||||
$model = Mockery::mock(BadgePrintLog::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$expected = [
|
||||
5 => ['count' => 2, 'last_printed_at' => '2026-03-06 10:00:00', 'last_printed_by' => 1],
|
||||
];
|
||||
|
||||
$model->shouldReceive('getStatus')
|
||||
->once()
|
||||
->with([5, 7], '2025-2026')
|
||||
->andReturn($expected);
|
||||
|
||||
$service = new BadgePrintLogService($model, $formatter);
|
||||
|
||||
$result = $service->getStatus(['5', '7', '5', ''], '2025-2026');
|
||||
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
public function test_log_normalizes_ids_and_returns_inserted_count(): void
|
||||
{
|
||||
$model = Mockery::mock(BadgePrintLog::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$model->shouldReceive('logPrints')
|
||||
->once()
|
||||
->with(
|
||||
[10, 20],
|
||||
99,
|
||||
'2025-2026',
|
||||
['10' => 'Teacher'],
|
||||
['10' => '3-A'],
|
||||
1
|
||||
)
|
||||
->andReturn(2);
|
||||
|
||||
$service = new BadgePrintLogService($model, $formatter);
|
||||
|
||||
$result = $service->log(
|
||||
userIds: ['10', '20', '10'],
|
||||
actorId: 99,
|
||||
schoolYear: '2025-2026',
|
||||
rolesMap: ['10' => 'Teacher'],
|
||||
classesMap: ['10' => '3-A'],
|
||||
copies: 1
|
||||
);
|
||||
|
||||
$this->assertSame(2, $result);
|
||||
}
|
||||
|
||||
public function test_log_safely_swallows_exceptions_and_logs_error(): void
|
||||
{
|
||||
Log::spy();
|
||||
|
||||
$model = Mockery::mock(BadgePrintLog::class);
|
||||
$formatter = new BadgeTextFormatter();
|
||||
|
||||
$model->shouldReceive('logPrints')
|
||||
->once()
|
||||
->andThrow(new \RuntimeException('table missing'));
|
||||
|
||||
$service = new BadgePrintLogService($model, $formatter);
|
||||
|
||||
$service->logSafely(
|
||||
userIds: [1, 2],
|
||||
actorId: 5,
|
||||
schoolYear: '2025-2026'
|
||||
);
|
||||
|
||||
Log::shouldHaveReceived('error')->once();
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Badges;
|
||||
|
||||
use App\Services\Badges\BadgeTextFormatter;
|
||||
use FPDF;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BadgeTextFormatterTest extends TestCase
|
||||
{
|
||||
protected BadgeTextFormatter $formatter;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->formatter = new BadgeTextFormatter();
|
||||
}
|
||||
|
||||
public function test_normalize_ids_filters_and_deduplicates(): void
|
||||
{
|
||||
$result = $this->formatter->normalizeIds([
|
||||
' 5 ',
|
||||
'',
|
||||
null,
|
||||
'7',
|
||||
5,
|
||||
'0',
|
||||
-1,
|
||||
'12',
|
||||
]);
|
||||
|
||||
$this->assertSame([5, 7, 12], $result);
|
||||
}
|
||||
|
||||
public function test_norm_removes_extra_spaces_and_control_chars(): void
|
||||
{
|
||||
$input = " Head \x07 of School ";
|
||||
$result = $this->formatter->norm($input);
|
||||
|
||||
$this->assertSame('Head of School', $result);
|
||||
}
|
||||
|
||||
public function test_format_role_handles_special_words_and_acronyms(): void
|
||||
{
|
||||
$this->assertSame('Head of School', $this->formatter->formatRole('HEAD_OF_SCHOOL'));
|
||||
$this->assertSame('Teacher Assistant', $this->formatter->formatRole('teacher_assistant'));
|
||||
$this->assertSame('HR Manager', $this->formatter->formatRole('hr manager'));
|
||||
$this->assertSame('PTA Head', $this->formatter->formatRole('pta head'));
|
||||
}
|
||||
|
||||
public function test_format_roles_csv_formats_each_role(): void
|
||||
{
|
||||
$result = $this->formatter->formatRolesCsv('teacher_assistant, head_of_school, hr');
|
||||
|
||||
$this->assertSame('Teacher Assistant, Head of School, HR', $result);
|
||||
}
|
||||
|
||||
public function test_format_class_normalizes_known_values(): void
|
||||
{
|
||||
$this->assertSame('KG', $this->formatter->formatClass('kindergarten'));
|
||||
$this->assertSame('KG', $this->formatter->formatClass('kg'));
|
||||
$this->assertSame('Youth', $this->formatter->formatClass('youth'));
|
||||
$this->assertSame('3-A', $this->formatter->formatClass('3-A'));
|
||||
}
|
||||
|
||||
public function test_resolve_role_prefers_first_available_candidate(): void
|
||||
{
|
||||
$info = [
|
||||
'role_name' => '',
|
||||
'roles' => 'teacher_assistant, admin',
|
||||
'job_title' => 'ignored because roles wins first',
|
||||
];
|
||||
|
||||
$result = $this->formatter->resolveRole($info);
|
||||
|
||||
$this->assertSame('teacher_assistant', $result);
|
||||
}
|
||||
|
||||
public function test_to_pdf_returns_string(): void
|
||||
{
|
||||
$result = $this->formatter->toPdf('Al Rahma Sunday School');
|
||||
|
||||
$this->assertIsString($result);
|
||||
$this->assertNotSame('', $result);
|
||||
}
|
||||
|
||||
public function test_fit_text_returns_size_within_bounds(): void
|
||||
{
|
||||
$pdf = new FPDF();
|
||||
$pdf->AddPage();
|
||||
$pdf->SetFont('Arial', '', 12);
|
||||
|
||||
$size = $this->formatter->fitText($pdf, 'Very Long Badge Label Text', 11, 7, 50);
|
||||
|
||||
$this->assertGreaterThanOrEqual(7, $size);
|
||||
$this->assertLessThanOrEqual(11, $size);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user