buildRequest( match: $this->validMatchArray(), csrf: '', turnToken: 'irrelevant', ); $result = $support->verify($request); self::assertNotNull($result->response); self::assertSame(403, $result->response->status); self::assertNull($result->matchIdAndMatch); } public function testItRejectsARequestWithABadTurnToken(): void { [$csrf, $cookie] = CsrfToken::issue(self::SECRET); $match = $this->validMatch(); $matchId = '0123456789abcdef'; $badToken = TurnToken::issue(self::SECRET, $matchId, 'bravo', 1, 0); $support = new MatchActionSupport(self::SECRET); $request = $this->buildRequest( match: $this->validMatchArray(), csrf: $csrf, turnToken: $badToken, cookies: ['__csrf' => $cookie], ); $result = $support->verify($request); self::assertNotNull($result->response); self::assertSame(409, $result->response->status); $body = json_decode($result->response->body, true); self::assertSame('turn', $body['error'] ?? null); } public function testItRejectsAMalformedMatchState(): void { [$csrf, $cookie] = CsrfToken::issue(self::SECRET); $support = new MatchActionSupport(self::SECRET); $request = $this->buildRequest( match: ['this' => 'is not a match'], csrf: $csrf, turnToken: 'whatever', cookies: ['__csrf' => $cookie], ); $result = $support->verify($request); self::assertNotNull($result->response); self::assertSame(400, $result->response->status); } public function testItReturnsTheValidatedPairForAValidRequest(): void { $match = $this->validMatch(); $matchArray = $this->validMatchArray(); $matchId = '0123456789abcdef'; $token = TurnToken::issue( self::SECRET, $matchId, $match->activeTeamId, $match->round, count($match->actionLog) ); [$csrf, $cookie] = CsrfToken::issue(self::SECRET); $support = new MatchActionSupport(self::SECRET); $body = json_encode(['matchId' => $matchId, 'match' => $matchArray], JSON_THROW_ON_ERROR); $request = new Request( [], [], [], ['__csrf' => $cookie], [ 'HTTP_X_CSRF_TOKEN' => $csrf, 'HTTP_X_TURN_TOKEN' => $token, '__csrf_secret' => self::SECRET, 'CONTENT_TYPE' => 'application/json', ], '', 'POST', '/matches/current/move', 'application/json', $body ); $result = $support->verify($request); self::assertNull($result->response); self::assertNotNull($result->matchIdAndMatch); self::assertSame($matchId, $result->matchIdAndMatch['matchId']); self::assertInstanceOf(MatchState::class, $result->matchIdAndMatch['match']); } private function validMatch(): MatchState { return new MatchState( new Battlefield(8, 8), [ $this->makeUnit('alpha-1', 'alpha', 0, 0, Archetype::Defender), $this->makeUnit('alpha-2', 'alpha', 1, 0, Archetype::Defender), $this->makeUnit('alpha-3', 'alpha', 2, 0, Archetype::Defender), $this->makeUnit('bravo-1', 'bravo', 7, 7, Archetype::Striker), $this->makeUnit('bravo-2', 'bravo', 6, 7, Archetype::Striker), $this->makeUnit('bravo-3', 'bravo', 5, 7, Archetype::Striker), ], 'alpha', ); } private function makeUnit(string $id, string $teamId, int $x, int $y, Archetype $archetype): UnitState { return new UnitState( $id, $teamId, new Position($x, $y), 10, 10, 3, 3, 2, 2, false, $archetype, ); } /** @return array */ private function validMatchArray(): array { return \BattleForge\Application\ScenarioSerializer::matchToArray($this->validMatch()); } /** * @param array $match * @param array $cookies */ private function buildRequest(array $match, string $csrf, string $turnToken, array $cookies = []): Request { $body = json_encode(['matchId' => '0123456789abcdef', 'match' => $match], JSON_THROW_ON_ERROR); return new Request( [], [], [], $cookies, [ 'HTTP_X_CSRF_TOKEN' => $csrf, 'HTTP_X_TURN_TOKEN' => $turnToken, '__csrf_secret' => self::SECRET, 'CONTENT_TYPE' => 'application/json', ], '', 'POST', '/matches/current/move', 'application/json', $body ); } }