<?phpnamespace Tests;use App\Chat\ChatClient;use App\Chat\Member;use App\Net\FakeLink;use PHPUnit\Framework\TestCase;/** The client's side of the protocol, frame by frame, without a network. */final class ChatClientTest extends TestCase{ private FakeLink $link; private ChatClient $chat; /** @var list<array{0: string, 1: array}> */ private array $events = []; protected function setUp(): void { $this->link = new FakeLink(); $this->chat = new ChatClient($this->link, 'n=Ada&a=30&s=0&v=Lyon'); foreach (['logged', 'room', 'say', 'private', 'members', 'rooms', 'found', 'count', 'gone', 'ended', 'ejection'] as $e) { $this->chat->on($e, fn (...$args) => $this->events[] = [$e, $args]); } $this->link->open(); } private function last(string $event): array { foreach (array_reverse($this->events) as [$e, $args]) { if ($e === $event) { return $args; } } self::fail("no $event"); } public function testTheClientIntroducesItselfWithItsTempid(): void { self::assertSame(["0\t0\tn=Ada&a=30&s=0&v=Lyon"], $this->link->take()); } public function testAfterTheLoginItAsksWhatTheAppletAskedAndEntersTheRoom(): void { $this->link->take(); $this->link->serverSays('0', '1:2:3:0', '197', '0', 'Ada'); self::assertSame(['1:2:3:0', 'Ada'], $this->last('logged')); self::assertSame(["82\t1:2:3:0", "84\t", "200\t4", "200\t5", "20\tSalon\t0"], $this->link->take()); } public function testEnteringARoomAsksForItsMembers(): void { $this->link->serverSays('20', '0', 'Musique', '?', 'Bob'); self::assertSame(['Musique', 'Bob', 0], $this->last('room')); self::assertContains("85\t", $this->link->take()); } public function testTheMembersOfTheRoomAndWhoComesAndGoes(): void { $this->link->serverSays('85', '-1', '1:1:1:0', '0', 'Lea', '0', '23', '0', 'Lyon', '2:2:2:0', '1', 'Modo', '0', '40', '1', '?'); /** @var list<Member> $members */ $members = $this->last('members')[0]; self::assertSame(['Lea', 'Modo'], array_map(fn (Member $m) => $m->nickname, $members)); self::assertSame('', $members[1]->city, '"?" is the applet\'s empty field'); self::assertTrue($members[1]->isAdmin()); $this->link->serverSays('85', '1', '1:1:1:0'); self::assertCount(1, $this->last('members')[0]); } public function testEmptyFieldsVanishAsInTheAppletsTokenizer(): void { $this->link->serverBytes("70\t9:9:9:0\t0\t\tBob\t0100Salut\t\n"); self::assertSame(['9:9:9:0', 'Bob', '0100Salut'], $this->last('say')); } public function testPrivateMessagesAndTheirEcho(): void { $this->link->serverSays('72', '5:5:5:0', '0', 'Lea', '0100Coucou'); self::assertSame(['5:5:5:0', 'Lea', '0100Coucou', false], $this->last('private')); $this->link->serverSays('73', '5:5:5:0', '0', 'Lea', '0100Re'); self::assertTrue($this->last('private')[3]); } public function testASearchWithBothSexesHasASexColumn(): void { $this->link->serverSays('56', '-2:-2:-2', '1:1:1:0', 'Lea', '0', '23', '0', 'Lyon', 'Accueil', '0', '2:2:2:0', 'Max', '0', '31', '1', 'Lille', '?', '2'); /** @var list<Member> $found */ $found = $this->last('found')[1]; self::assertSame(['Lea', 0, 'Accueil'], [$found[0]->nickname, $found[0]->sex, $found[0]->where]); self::assertSame(['Max', 1, ''], [$found[1]->nickname, $found[1]->sex, $found[1]->where]); } public function testASearchForWomenHasNoSexColumn(): void { $this->link->serverSays('56', '-2:0:0', '1:1:1:0', 'Lea', '0', '23', 'Lyon', 'Accueil', '0'); self::assertSame(0, $this->last('found')[1][0]->sex); } public function testTheRoomsLists(): void { $this->link->serverSays('94', 'Accueil', '?', 'admin', '4', 'Musique', '?', 'admin', '0'); self::assertSame(['94', [['name' => 'Accueil', 'creator' => 'admin', 'count' => 4], ['name' => 'Musique', 'creator' => 'admin', 'count' => 0]]], $this->last('rooms')); } public function testThePingIsAnswered(): void { $this->link->take(); $this->link->serverSays('667'); self::assertSame(["667\t"], $this->link->take()); } public function testAnEjectionEndsTheSession(): void { $this->link->serverSays('0', '1:2:3:0', '197', '0', 'Ada'); $this->link->serverSays('555', '7:7:7:0', 'Modo'); self::assertSame(['ejected-by', 'Modo'], $this->last('ended')); self::assertTrue($this->link->closed); } public function testAFrameCutInTwoIsPutBackTogether(): void { $this->link->serverBytes("199\t4"); $this->link->serverBytes("2\n"); self::assertSame([42], $this->last('count')); }}