buildRequest(post: ['id' => 'demo']); $response = $handler->handle($request, ['id' => 'demo']); self::assertSame(403, $response->status); self::assertStringContainsString('Forbidden', $response->body); } public function testItRejectsARequestWithAnInvalidCsrfToken(): void { $handler = new PostTeamEditor(); $request = $this->buildRequest(post: ['id' => 'demo', '_csrf' => 'tampered']); $response = $handler->handle($request, ['id' => 'demo']); self::assertSame(403, $response->status); } public function testItSavesAValidScenarioAndReturnsTheLocalStorageSnippet(): void { [$token, $cookie] = CsrfToken::issue(self::SECRET); $handler = new PostTeamEditor(); $request = $this->buildRequest( post: $this->validPost($token), cookies: ['__csrf' => $cookie], server: ['__csrf_secret' => self::SECRET], ); $response = $handler->handle($request, ['id' => 'demo']); self::assertSame(200, $response->status); self::assertStringContainsString('localStorage.setItem', $response->body); self::assertStringContainsString('scenario:demo', $response->body); } public function testItRejectsAnOutOfBoundsStatAndReRendersTheForm(): void { [$token, $cookie] = CsrfToken::issue(self::SECRET); $handler = new PostTeamEditor(); $post = $this->validPost($token); $post['teamA']['units'][0]['maxHealth'] = '9999'; $request = $this->buildRequest( post: $post, cookies: ['__csrf' => $cookie], server: ['__csrf_secret' => self::SECRET], ); $response = $handler->handle($request, ['id' => 'demo']); self::assertSame(200, $response->status); self::assertStringContainsString('bf-errors', $response->body); self::assertStringContainsString('outside archetype bounds', $response->body); } /** * @param array $post * @param array $cookies * @param array $server */ private function buildRequest(array $post, array $cookies = [], array $server = []): Request { return new Request([], $post, [], $cookies, $server, '', 'POST', '/scenarios/demo/edit/team', 'application/x-www-form-urlencoded', ''); } /** @return array */ private function validPost(string $token): array { return [ '_csrf' => $token, 'id' => 'demo', 'name' => 'Demo', 'battlefieldWidth' => '8', 'battlefieldHeight' => '8', 'teamA' => [ 'units' => [ ['id' => 'a1', 'archetype' => 'defender', 'maxHealth' => '12', 'attack' => '3', 'defense' => '4', 'speed' => '2', 'x' => '0', 'y' => '0'], ['id' => 'a2', 'archetype' => 'defender', 'maxHealth' => '12', 'attack' => '3', 'defense' => '4', 'speed' => '2', 'x' => '1', 'y' => '0'], ['id' => 'a3', 'archetype' => 'defender', 'maxHealth' => '12', 'attack' => '3', 'defense' => '4', 'speed' => '2', 'x' => '2', 'y' => '0'], ], ], 'teamB' => [ 'units' => [ ['id' => 'b1', 'archetype' => 'striker', 'maxHealth' => '8', 'attack' => '5', 'defense' => '2', 'speed' => '3', 'x' => '7', 'y' => '7'], ['id' => 'b2', 'archetype' => 'striker', 'maxHealth' => '8', 'attack' => '5', 'defense' => '2', 'speed' => '3', 'x' => '6', 'y' => '7'], ['id' => 'b3', 'archetype' => 'striker', 'maxHealth' => '8', 'attack' => '5', 'defense' => '2', 'speed' => '3', 'x' => '5', 'y' => '7'], ], ], 'victoryCondition' => 'eliminate_all', 'holdRoundsRequired' => '1', ]; } }