parseEmployees($this->roster()); $serialised = json_encode($directory->toCacheable(), JSON_THROW_ON_ERROR); $this->assertStringNotContainsString('s3cr3t', $serialised, 'The api key must not survive.'); $this->assertStringNotContainsString('apiKey', $serialised); $this->assertStringNotContainsString('1990', $serialised, 'The birthday must not survive.'); $this->assertStringNotContainsString('RFID', $serialised); $this->assertStringNotContainsString('personnelNumber', $serialised); } public function testItReadsTheFieldsWeActuallyUse(): void { $directory = (new ResponseParser())->parseEmployees($this->roster()); $theo = $directory->find('theo.test@example.com'); $this->assertNotNull($theo); $this->assertSame('Operations', $theo->getDepartment()); $this->assertSame('aaaaaaaa-894a-4e63-9c8d-5777e6afdc91', $theo->getUuid()); $this->assertFalse($theo->hasLeft()); } public function testADateOfSeparationMarksSomeoneAsGone(): void { $directory = (new ResponseParser())->parseEmployees($this->roster()); $this->assertTrue($directory->find('ex@example.com')->hasLeft()); } /** * Both systems have the address typed in by hand, so the one place they have to agree on * it cannot be case-sensitive. */ public function testLookupIgnoresCaseAndSurroundingSpace(): void { $directory = (new ResponseParser())->parseEmployees($this->roster()); $this->assertTrue($directory->has('THEO.TEST@example.com')); $this->assertTrue($directory->has(' theo.test@example.com ')); $this->assertFalse($directory->has('nobody@example.com')); $this->assertFalse($directory->has(null)); } public function testAnEntryWithoutAnAddressIsDropped(): void { $directory = (new ResponseParser())->parseEmployees([ ['mainDepartment' => 'Operations', 'email' => ''], ['mainDepartment' => 'Operations'], 'not an entry', ]); $this->assertTrue($directory->isEmpty()); } public function testItSurvivesTheCacheRoundTrip(): void { $directory = (new ResponseParser())->parseEmployees($this->roster()); $restored = EmployeeDirectory::fromCacheable($directory->toCacheable()); $this->assertSame($directory->count(), $restored->count()); $this->assertSame('Operations', $restored->find('theo.test@example.com')->getDepartment()); $this->assertTrue($restored->find('ex@example.com')->hasLeft()); } /** * An unreachable roster produces an empty one, and the importer reads that as "no roster" * rather than "nobody is registered". */ public function testAnEmptyDirectoryIsRecognisable(): void { $this->assertTrue(EmployeeDirectory::fromEmployees([])->isEmpty()); } private function roster(): array { return [ [ 'personnelNumber' => '4711', 'mainDepartment' => 'Standard-Abteilung', 'email' => 'rechnungen@example.com', 'givenName' => 'Admin', 'lastName' => 'Hausmanagement', 'birthday' => '1990-05-01', 'joinDate' => '2021-00-01', 'dateOfSeparation' => null, 'RFID' => 'abc', 'uuid' => 'bbbbbbbb-0000-0000-0000-000000000000', 'apiKey' => 's3cr3t-admin', ], [ 'personnelNumber' => '', 'mainDepartment' => 'Operations', 'email' => 'theo.test@example.com', 'givenName' => 'Theo', 'lastName' => 'Test', 'birthday' => null, 'joinDate' => '2021-10-22', 'dateOfSeparation' => null, 'RFID' => '', 'uuid' => 'aaaaaaaa-894a-4e63-9c8d-5777e6afdc91', 'apiKey' => 's3cr3t-theo', ], [ 'mainDepartment' => 'Operations', 'email' => 'ex@example.com', 'dateOfSeparation' => '2025-05-31', 'uuid' => 'cccccccc-0000-0000-0000-000000000000', 'apiKey' => 's3cr3t-ex', ], ]; } }