<?php
use PHPUnit\Framework\TestCase;
class AppAuthGuardTest extends TestCase {
private $originalApp;
private $testApp;
protected function setUp(): void {
$this -> originalApp = app();
$this -> testApp = new TestableAppForGuard();
\Fury\Kernel\AppContainer::set_app($this -> testApp);
$this -> create_sessions_table();
}
protected function tearDown(): void {
\Fury\Kernel\AppContainer::set_app($this -> originalApp);
$_SERVER = [];
$_COOKIE = [];
app() -> thin_builder -> query("DROP TABLE IF EXISTS sessions");
}
private function create_sessions_table(): void {
app() -> thin_builder -> query("CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uid INTEGER,
status INTEGER DEFAULT 1,
token TEXT,
last_using_at TEXT,
update_at TEXT,
create_at TEXT
)");
}
public function test_non_api_uri_returns_null(): void {
$_SERVER['REQUEST_URI'] = '/index.php';
$result = $this -> testApp -> check_api_auth();
$this -> assertNull($result);
}
public function test_valid_bearer_token_passes(): void {
$token = app() -> sessions -> create(1); // create session for user 1
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $token;
$result = $this -> testApp -> check_api_auth();
$this -> assertNull($result);
}
public function test_valid_cookie_token_passes(): void {
$token = app() -> sessions -> create(1);
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$_COOKIE['auth_token'] = $token;
$result = $this -> testApp -> check_api_auth();
$this -> assertNull($result);
}
public function test_missing_token_returns_401(): void {
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$result = $this -> testApp -> check_api_auth();
$this -> assertNotNull($result);
$this -> assertSame(401, $result['code']);
$this -> assertSame('unauthorized', $result['body']['error_alias']);
}
public function test_invalid_token_returns_401(): void {
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer invalid_token_123';
$result = $this -> testApp -> check_api_auth();
$this -> assertNotNull($result);
$this -> assertSame(401, $result['code']);
}
public function test_rate_limit_exceeded_returns_429(): void {
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$_SERVER['REMOTE_ADDR'] = '1.2.3.4';
// Exhaust rate limit (60 requests in 1 minute window)
for ($i = 0; $i < 60; $i++) {
$this -> testApp -> check_api_auth();
}
$result = $this -> testApp -> check_api_auth();
$this -> assertNotNull($result);
$this -> assertSame(429, $result['code']);
$this -> assertSame('rate_limit_exceeded', $result['body']['error_alias']);
}
public function test_rate_limit_resets_after_window(): void {
$_SERVER['REQUEST_URI'] = '/api/v1/devices/list';
$_SERVER['REMOTE_ADDR'] = '1.2.3.5';
// Exhaust limit
for ($i = 0; $i < 60; $i++) {
$this -> testApp -> check_api_auth();
}
$result = $this -> testApp -> check_api_auth();
$this -> assertSame(429, $result['code']);
// Reset rate limiter storage to simulate window passing
$limiter = new \SHServ\Tools\RateLimiter(60, 60);
$limiter -> clear();
$token = app() -> sessions -> create(1);
$_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $token;
$result = $this -> testApp -> check_api_auth();
$this -> assertNull($result);
}
}
class TestableAppForGuard extends \SHServ\App {
public function app_init(): void {
$this -> utils = new \SHServ\Utils();
$this -> sessions = new \SHServ\Sessions();
}
}