assertArrayHasKey('action', $result); $this->assertSame('set', $result['action']); $this->assertArrayHasKey('nonce', $result); $this->assertIsString($result['nonce']); // 32 hex chars (random_bytes(16)) $this->assertSame(32, strlen($result['nonce'])); $this->assertMatchesRegularExpression('/^[a-f0-9]{32}$/', $result['nonce']); // 세션에 저장 확인 $this->assertArrayHasKey('_installer_session_probe', $_SESSION); $this->assertSame($result['nonce'], $_SESSION['_installer_session_probe']); } public function test_verify_action_matches_when_session_preserved(): void { $setResult = sessionProbeSet(); $verifyResult = sessionProbeVerify(); $this->assertSame('verify', $verifyResult['action']); $this->assertTrue($verifyResult['matched']); $this->assertSame($setResult['nonce'], $verifyResult['nonce'] ?? null); } public function test_verify_action_returns_unmatched_when_session_empty(): void { // set 호출 없이 verify — 세션 쿠키가 round-trip 되지 않은 시뮬레이션 $verifyResult = sessionProbeVerify(); $this->assertSame('verify', $verifyResult['action']); $this->assertFalse($verifyResult['matched']); } public function test_set_generates_distinct_nonces_on_repeated_calls(): void { $first = sessionProbeSet(); $second = sessionProbeSet(); // 매 호출마다 새 nonce — 이전 호출의 nonce 가 캐시되지 않음 $this->assertNotSame($first['nonce'], $second['nonce']); // 세션에는 마지막 set 의 nonce 만 남음 $this->assertSame($second['nonce'], $_SESSION['_installer_session_probe']); } public function test_verify_action_consumes_nonce(): void { sessionProbeSet(); sessionProbeVerify(); // 한 번 verify 한 nonce 는 재사용 방지를 위해 세션에서 제거되어야 함 $this->assertArrayNotHasKey('_installer_session_probe', $_SESSION); // 다음 verify 는 세션 빔 케이스와 동일하게 unmatched $secondVerify = sessionProbeVerify(); $this->assertFalse($secondVerify['matched']); } }