-
diff --git a/templates/partials/navbar.php b/templates/partials/navbar.php
index 4659bc1..2c9a6f2 100644
--- a/templates/partials/navbar.php
+++ b/templates/partials/navbar.php
@@ -1,35 +1,35 @@
-
+
\ No newline at end of file
diff --git a/tests/Controller/AdminController/AdminControllerTest.php b/tests/Controller/AdminController/AdminControllerTest.php
index 714440f..95494a8 100644
--- a/tests/Controller/AdminController/AdminControllerTest.php
+++ b/tests/Controller/AdminController/AdminControllerTest.php
@@ -7,32 +7,32 @@ use PHPUnit\Framework\TestCase;
class AdminControllerTest extends TestCase
{
private PDO $mockPdo;
- private ConfigModel $config;
+ private SettingsModel $settings;
private UserModel $user;
protected function setUp(): void
{
// Create mock PDO
$this->mockPdo = $this->createMock(PDO::class);
-
+
// Create real config and user objects with mocked PDO
- $this->config = new ConfigModel($this->mockPdo);
- $this->config->siteTitle = 'Test Site';
- $this->config->siteDescription = 'Test Description';
- $this->config->baseUrl = 'https://example.com';
- $this->config->basePath = '/tkr';
- $this->config->itemsPerPage = 10;
-
+ $this->settings = new SettingsModel($this->mockPdo);
+ $this->settings->siteTitle = 'Test Site';
+ $this->settings->siteDescription = 'Test Description';
+ $this->settings->baseUrl = 'https://example.com';
+ $this->settings->basePath = '/tkr';
+ $this->settings->itemsPerPage = 10;
+
$this->user = new UserModel($this->mockPdo);
$this->user->username = 'testuser';
$this->user->displayName = 'Test User';
$this->user->website = 'https://example.com';
-
+
// Set up global $app for simplified dependency access
global $app;
$app = [
'db' => $this->mockPdo,
- 'config' => $this->config,
+ 'settings' => $this->settings,
'user' => $this->user,
];
}
@@ -41,14 +41,14 @@ class AdminControllerTest extends TestCase
{
$controller = new AdminController();
$data = $controller->getAdminData(false);
-
+
// Should return proper structure
- $this->assertArrayHasKey('config', $data);
+ $this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
-
+
// Should be the injected instances
- $this->assertSame($this->config, $data['config']);
+ $this->assertSame($this->settings, $data['settings']);
$this->assertSame($this->user, $data['user']);
$this->assertFalse($data['isSetup']);
}
@@ -57,14 +57,14 @@ class AdminControllerTest extends TestCase
{
$controller = new AdminController();
$data = $controller->getAdminData(true);
-
+
// Should return proper structure
- $this->assertArrayHasKey('config', $data);
+ $this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
-
+
// Should be the injected instances
- $this->assertSame($this->config, $data['config']);
+ $this->assertSame($this->settings, $data['settings']);
$this->assertSame($this->user, $data['user']);
$this->assertTrue($data['isSetup']);
}
@@ -73,7 +73,7 @@ class AdminControllerTest extends TestCase
{
$controller = new AdminController();
$result = $controller->saveSettings([], false);
-
+
$this->assertFalse($result['success']);
$this->assertContains('No data provided', $result['errors']);
}
@@ -81,7 +81,7 @@ class AdminControllerTest extends TestCase
public function testProcessSettingsSaveValidationErrors(): void
{
$controller = new AdminController();
-
+
// Test data with multiple validation errors
$postData = [
'username' => '', // Missing username
@@ -94,12 +94,12 @@ class AdminControllerTest extends TestCase
'password' => 'test123',
'confirm_password' => 'different' // Passwords don't match
];
-
+
$result = $controller->saveSettings($postData, false);
-
+
$this->assertFalse($result['success']);
$this->assertNotEmpty($result['errors']);
-
+
// Should have multiple validation errors
$this->assertGreaterThan(5, count($result['errors']));
}
@@ -133,16 +133,16 @@ class AdminControllerTest extends TestCase
$this->mockPdo->method('query')->willReturn($mockStatement);
// Create models with mocked PDO
- $config = new ConfigModel($this->mockPdo);
+ $settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
-
+
// Update global $app with test models
global $app;
- $app['config'] = $config;
+ $app['settings'] = $settings;
$app['user'] = $user;
-
+
$controller = new AdminController();
-
+
$postData = [
'username' => 'newuser',
'display_name' => 'New User',
@@ -155,9 +155,9 @@ class AdminControllerTest extends TestCase
'strict_accessibility' => 'on',
'log_level' => 2
];
-
+
$result = $controller->saveSettings($postData, false);
-
+
$this->assertTrue($result['success']);
$this->assertEmpty($result['errors']);
}
@@ -191,20 +191,20 @@ class AdminControllerTest extends TestCase
$this->mockPdo->expects($this->atLeastOnce())
->method('prepare')
->willReturn($mockStatement);
-
+
$this->mockPdo->method('query')->willReturn($mockStatement);
// Create models with mocked PDO
- $config = new ConfigModel($this->mockPdo);
+ $settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
-
+
// Update global $app with test models
global $app;
- $app['config'] = $config;
+ $app['settings'] = $settings;
$app['user'] = $user;
-
+
$controller = new AdminController();
-
+
$postData = [
'username' => 'testuser',
'display_name' => 'Test User',
@@ -216,9 +216,9 @@ class AdminControllerTest extends TestCase
'password' => 'newpassword',
'confirm_password' => 'newpassword'
];
-
+
$result = $controller->saveSettings($postData, false);
-
+
$this->assertTrue($result['success']);
}
@@ -228,16 +228,16 @@ class AdminControllerTest extends TestCase
$this->mockPdo->method('query')
->willThrowException(new PDOException("Database error"));
- $config = new ConfigModel($this->mockPdo);
+ $settings = new SettingsModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
-
+
// Update global $app with test models
global $app;
- $app['config'] = $config;
+ $app['settings'] = $settings;
$app['user'] = $user;
-
+
$controller = new AdminController();
-
+
$postData = [
'username' => 'testuser',
'display_name' => 'Test User',
@@ -247,9 +247,9 @@ class AdminControllerTest extends TestCase
'base_path' => '/tkr',
'items_per_page' => 10
];
-
+
$result = $controller->saveSettings($postData, false);
-
+
$this->assertFalse($result['success']);
$this->assertContains('Failed to save settings', $result['errors']);
}
diff --git a/tests/Controller/FeedController/FeedControllerTest.php b/tests/Controller/FeedController/FeedControllerTest.php
index ba1e124..6d50840 100644
--- a/tests/Controller/FeedController/FeedControllerTest.php
+++ b/tests/Controller/FeedController/FeedControllerTest.php
@@ -7,7 +7,7 @@ class FeedControllerTest extends TestCase
{
private PDO $mockPdo;
private PDOStatement $mockStatement;
- private ConfigModel $mockConfig;
+ private SettingsModel $mockConfig;
private UserModel $mockUser;
private string $tempLogDir;
@@ -17,31 +17,31 @@ class FeedControllerTest extends TestCase
$this->tempLogDir = sys_get_temp_dir() . '/tkr_test_logs_' . uniqid();
mkdir($this->tempLogDir . '/logs', 0777, true);
Log::init($this->tempLogDir . '/logs/tkr.log');
-
+
// Create mock PDO and PDOStatement
$this->mockStatement = $this->createMock(PDOStatement::class);
$this->mockPdo = $this->createMock(PDO::class);
-
+
// Mock config with feed-relevant properties
- $this->mockConfig = new ConfigModel($this->mockPdo);
+ $this->mockConfig = new SettingsModel($this->mockPdo);
$this->mockConfig->itemsPerPage = 10;
$this->mockConfig->basePath = '/tkr';
$this->mockConfig->siteTitle = 'Test Site';
$this->mockConfig->siteDescription = 'Test Description';
$this->mockConfig->baseUrl = 'https://test.example.com';
-
+
// Mock user
$this->mockUser = new UserModel($this->mockPdo);
$this->mockUser->displayName = 'Test User';
-
+
// Set up global $app for simplified dependency access
global $app;
$app = [
'db' => $this->mockPdo,
- 'config' => $this->mockConfig,
+ 'settings' => $this->mockConfig,
'user' => $this->mockUser,
];
-
+
// Set log level on config for Log class
$this->mockConfig->logLevel = 1; // Allow DEBUG level logs
}
@@ -57,7 +57,7 @@ class FeedControllerTest extends TestCase
private function deleteDirectory(string $dir): void
{
if (!is_dir($dir)) return;
-
+
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
@@ -71,11 +71,11 @@ class FeedControllerTest extends TestCase
// Mock PDO prepare method to return our mock statement
$this->mockPdo->method('prepare')
->willReturn($this->mockStatement);
-
+
// Mock statement execute method
$this->mockStatement->method('execute')
->willReturn(true);
-
+
// Mock statement fetchAll to return our test data
$this->mockStatement->method('fetchAll')
->willReturn($tickData);
@@ -84,16 +84,16 @@ class FeedControllerTest extends TestCase
public function testControllerInstantiationWithNoTicks(): void
{
$this->setupMockDatabase([]);
-
+
$controller = new FeedController();
-
+
// Verify it was created successfully
$this->assertInstanceOf(FeedController::class, $controller);
-
+
// Check logs
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
-
+
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Loaded 0 ticks for feeds', $logContent);
}
@@ -104,18 +104,18 @@ class FeedControllerTest extends TestCase
['id' => 1, 'timestamp' => '2025-01-31 12:00:00', 'tick' => 'First tick'],
['id' => 2, 'timestamp' => '2025-01-31 13:00:00', 'tick' => 'Second tick'],
];
-
+
$this->setupMockDatabase($testTicks);
-
+
$controller = new FeedController();
-
+
// Verify it was created successfully
$this->assertInstanceOf(FeedController::class, $controller);
-
+
// Check logs
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
-
+
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Loaded 2 ticks for feeds', $logContent);
}
@@ -123,18 +123,18 @@ class FeedControllerTest extends TestCase
public function testControllerCallsDatabaseCorrectly(): void
{
$this->setupMockDatabase([]);
-
+
// Verify that PDO prepare is called with the correct SQL for tick loading
$this->mockPdo->expects($this->once())
->method('prepare')
->with('SELECT id, timestamp, tick FROM tick ORDER BY timestamp DESC LIMIT ? OFFSET ?')
->willReturn($this->mockStatement);
-
+
// Verify that execute is called with correct parameters (page 1, offset 0)
$this->mockStatement->expects($this->once())
->method('execute')
->with([10, 0]); // itemsPerPage=10, page 1 = offset 0
-
+
new FeedController();
}
@@ -143,16 +143,16 @@ class FeedControllerTest extends TestCase
$testTicks = [
['id' => 1, 'timestamp' => '2025-01-31 12:00:00', 'tick' => 'Test tick']
];
-
+
$this->setupMockDatabase($testTicks);
-
+
$controller = new FeedController();
-
+
// Capture output to prevent headers/content from affecting test
ob_start();
$controller->rss();
ob_end_clean();
-
+
// Check logs for RSS generation
$logFile = $this->tempLogDir . '/logs/tkr.log';
$logContent = file_get_contents($logFile);
@@ -165,16 +165,16 @@ class FeedControllerTest extends TestCase
['id' => 1, 'timestamp' => '2025-01-31 12:00:00', 'tick' => 'Test tick'],
['id' => 2, 'timestamp' => '2025-01-31 13:00:00', 'tick' => 'Another tick']
];
-
+
$this->setupMockDatabase($testTicks);
-
+
$controller = new FeedController();
-
+
// Capture output to prevent headers/content from affecting test
ob_start();
$controller->atom();
ob_end_clean();
-
+
// Check logs for Atom generation
$logFile = $this->tempLogDir . '/logs/tkr.log';
$logContent = file_get_contents($logFile);
diff --git a/tests/Controller/HomeController/HomeControllerTest.php b/tests/Controller/HomeController/HomeControllerTest.php
index f3bec85..a8460b3 100644
--- a/tests/Controller/HomeController/HomeControllerTest.php
+++ b/tests/Controller/HomeController/HomeControllerTest.php
@@ -7,33 +7,33 @@ class HomeControllerTest extends TestCase
{
private PDO $mockPdo;
private PDOStatement $mockStatement;
- private ConfigModel $mockConfig;
+ private SettingsModel $mockConfig;
private UserModel $mockUser;
protected function setUp(): void
{
// Reset Log state to prevent test pollution
Log::init(sys_get_temp_dir() . '/tkr_controller_test.log');
-
+
// Create mock PDO and PDOStatement
$this->mockStatement = $this->createMock(PDOStatement::class);
$this->mockPdo = $this->createMock(PDO::class);
-
+
// Mock config
- $this->mockConfig = new ConfigModel($this->mockPdo);
+ $this->mockConfig = new SettingsModel($this->mockPdo);
$this->mockConfig->itemsPerPage = 10;
$this->mockConfig->basePath = '/tkr';
-
+
// Mock user
$this->mockUser = new UserModel($this->mockPdo);
$this->mockUser->displayName = 'Test User';
$this->mockUser->mood = '😊';
-
+
// Set up global $app for simplified dependency access
global $app;
$app = [
'db' => $this->mockPdo,
- 'config' => $this->mockConfig,
+ 'settings' => $this->mockConfig,
'user' => $this->mockUser,
];
}
@@ -43,11 +43,11 @@ class HomeControllerTest extends TestCase
// Mock PDO prepare method to return our mock statement
$this->mockPdo->method('prepare')
->willReturn($this->mockStatement);
-
+
// Mock statement execute method
$this->mockStatement->method('execute')
->willReturn(true);
-
+
// Mock statement fetchAll to return our test data
$this->mockStatement->method('fetchAll')
->willReturn($tickData);
@@ -59,7 +59,7 @@ class HomeControllerTest extends TestCase
// Mock successful insert
$this->mockPdo->method('prepare')
->willReturn($this->mockStatement);
-
+
$this->mockStatement->method('execute')
->willReturn(true);
} else {
@@ -72,19 +72,19 @@ class HomeControllerTest extends TestCase
public function testGetHomeDataWithNoTicks(): void
{
$this->setupMockDatabase([]); // Empty array = no ticks
-
+
$controller = new HomeController();
$data = $controller->getHomeData(1);
-
+
// Should return proper structure
- $this->assertArrayHasKey('config', $data);
+ $this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('tickList', $data);
-
+
// Config and user should be the injected instances
- $this->assertSame($this->mockConfig, $data['config']);
+ $this->assertSame($this->mockConfig, $data['settings']);
$this->assertSame($this->mockUser, $data['user']);
-
+
// Should have tick list HTML (even if empty)
$this->assertIsString($data['tickList']);
}
@@ -97,17 +97,17 @@ class HomeControllerTest extends TestCase
['id' => 2, 'timestamp' => '2025-01-31 13:00:00', 'tick' => 'Second tick'],
['id' => 3, 'timestamp' => '2025-01-31 14:00:00', 'tick' => 'Third tick'],
];
-
+
$this->setupMockDatabase($testTicks);
-
+
$controller = new HomeController();
$data = $controller->getHomeData(1);
-
+
// Should return proper structure
- $this->assertArrayHasKey('config', $data);
+ $this->assertArrayHasKey('settings', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('tickList', $data);
-
+
// Should contain tick content in HTML
$this->assertStringContainsString('First tick', $data['tickList']);
$this->assertStringContainsString('Second tick', $data['tickList']);
@@ -117,18 +117,18 @@ class HomeControllerTest extends TestCase
public function testGetHomeDataCallsDatabaseCorrectly(): void
{
$this->setupMockDatabase([]);
-
+
// Verify that PDO prepare is called with the correct SQL
$this->mockPdo->expects($this->once())
->method('prepare')
->with('SELECT id, timestamp, tick FROM tick ORDER BY timestamp DESC LIMIT ? OFFSET ?')
->willReturn($this->mockStatement);
-
+
// Verify that execute is called with correct parameters for page 2
$this->mockStatement->expects($this->once())
->method('execute')
->with([10, 10]); // itemsPerPage=10, page 2 = offset 10
-
+
$controller = new HomeController();
$controller->getHomeData(2); // Page 2
}
@@ -136,28 +136,28 @@ class HomeControllerTest extends TestCase
public function testProcessTickSuccess(): void
{
$this->setupMockDatabaseForInsert(true);
-
+
// Verify the INSERT SQL is called correctly
$this->mockPdo->expects($this->once())
->method('prepare')
->with('INSERT INTO tick(timestamp, tick) values (?, ?)')
->willReturn($this->mockStatement);
-
+
// Verify execute is called with timestamp and content
$this->mockStatement->expects($this->once())
->method('execute')
->with($this->callback(function($params) {
// First param should be a timestamp, second should be the tick content
- return count($params) === 2
- && is_string($params[0])
+ return count($params) === 2
+ && is_string($params[0])
&& $params[1] === 'This is a test tick';
}));
-
+
$controller = new HomeController();
$postData = ['new_tick' => 'This is a test tick'];
-
+
$result = $controller->processTick($postData);
-
+
$this->assertTrue($result['success']);
$this->assertEquals('Tick saved successfully', $result['message']);
}
@@ -166,12 +166,12 @@ class HomeControllerTest extends TestCase
{
// PDO shouldn't be called at all for empty content
$this->mockPdo->expects($this->never())->method('prepare');
-
+
$controller = new HomeController();
$postData = ['new_tick' => ' ']; // Just whitespace
-
+
$result = $controller->processTick($postData);
-
+
$this->assertFalse($result['success']);
$this->assertEquals('Empty tick ignored', $result['message']);
}
@@ -180,12 +180,12 @@ class HomeControllerTest extends TestCase
{
// PDO shouldn't be called at all for missing field
$this->mockPdo->expects($this->never())->method('prepare');
-
+
$controller = new HomeController();
$postData = []; // No new_tick field
-
+
$result = $controller->processTick($postData);
-
+
$this->assertFalse($result['success']);
$this->assertEquals('No tick content provided', $result['message']);
}
@@ -193,31 +193,31 @@ class HomeControllerTest extends TestCase
public function testProcessTickTrimsWhitespace(): void
{
$this->setupMockDatabaseForInsert(true);
-
+
// Verify execute is called with trimmed content
$this->mockStatement->expects($this->once())
->method('execute')
->with($this->callback(function($params) {
return $params[1] === 'This has whitespace'; // Should be trimmed
}));
-
+
$controller = new HomeController();
$postData = ['new_tick' => ' This has whitespace '];
-
+
$result = $controller->processTick($postData);
-
+
$this->assertTrue($result['success']);
}
public function testProcessTickHandlesDatabaseError(): void
{
$this->setupMockDatabaseForInsert(false); // Will throw exception
-
+
$controller = new HomeController();
$postData = ['new_tick' => 'This will fail'];
-
+
$result = $controller->processTick($postData);
-
+
$this->assertFalse($result['success']);
$this->assertEquals('Failed to save tick', $result['message']);
}
diff --git a/tests/Controller/LogController/LogControllerTest.php b/tests/Controller/LogController/LogControllerTest.php
index 662bb56..92b2446 100644
--- a/tests/Controller/LogController/LogControllerTest.php
+++ b/tests/Controller/LogController/LogControllerTest.php
@@ -13,7 +13,7 @@ class LogControllerTest extends TestCase
{
$this->tempLogDir = sys_get_temp_dir() . '/tkr_test_logs_' . uniqid();
mkdir($this->tempLogDir, 0777, true);
-
+
$this->testLogFile = $this->tempLogDir . '/logs/tkr.log';
mkdir(dirname($this->testLogFile), 0777, true);
@@ -23,16 +23,16 @@ class LogControllerTest extends TestCase
// Set up global $app for simplified dependency access
$mockPdo = $this->createMock(PDO::class);
- $mockConfig = new ConfigModel($mockPdo);
+ $mockConfig = new SettingsModel($mockPdo);
$mockConfig->baseUrl = 'https://example.com';
$mockConfig->basePath = '/tkr/';
-
+
$mockUser = new UserModel($mockPdo);
-
+
global $app;
$app = [
'db' => $mockPdo,
- 'config' => $mockConfig,
+ 'settings' => $mockConfig,
'user' => $mockUser,
];
}
@@ -50,7 +50,7 @@ class LogControllerTest extends TestCase
private function deleteDirectory(string $dir): void
{
if (!is_dir($dir)) return;
-
+
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
@@ -64,14 +64,14 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData();
-
+
// Should return empty log entries but valid structure
$this->assertArrayHasKey('logEntries', $data);
$this->assertArrayHasKey('availableRoutes', $data);
$this->assertArrayHasKey('availableLevels', $data);
$this->assertArrayHasKey('currentLevelFilter', $data);
$this->assertArrayHasKey('currentRouteFilter', $data);
-
+
$this->assertEmpty($data['logEntries']);
$this->assertEmpty($data['availableRoutes']);
$this->assertEquals(['DEBUG', 'INFO', 'WARNING', 'ERROR'], $data['availableLevels']);
@@ -96,15 +96,15 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData();
-
+
// Should parse all valid entries and ignore invalid ones
$this->assertCount(5, $data['logEntries']);
-
+
// Verify entries are in reverse chronological order (newest first)
$entries = $data['logEntries'];
$this->assertEquals('Info without route', $entries[0]['message']);
$this->assertEquals('Debug home page', $entries[4]['message']);
-
+
// Verify entry structure
$firstEntry = $entries[0];
$this->assertArrayHasKey('timestamp', $firstEntry);
@@ -112,13 +112,13 @@ class LogControllerTest extends TestCase
$this->assertArrayHasKey('ip', $firstEntry);
$this->assertArrayHasKey('route', $firstEntry);
$this->assertArrayHasKey('message', $firstEntry);
-
+
// Test route extraction
$adminEntry = array_filter($entries, fn($e) => $e['message'] === 'Info admin page');
$adminEntry = array_values($adminEntry)[0];
$this->assertEquals('GET /admin', $adminEntry['route']);
$this->assertEquals('INFO', $adminEntry['level']);
-
+
// Test entry without route
$noRouteEntry = array_filter($entries, fn($e) => $e['message'] === 'Info without route');
$noRouteEntry = array_values($noRouteEntry)[0];
@@ -138,7 +138,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData('ERROR');
-
+
// Should only include ERROR entries
$this->assertCount(1, $data['logEntries']);
$this->assertEquals('ERROR', $data['logEntries'][0]['level']);
@@ -159,7 +159,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData('', 'GET /admin');
-
+
// Should only include GET /admin entries
$this->assertCount(1, $data['logEntries']);
$this->assertEquals('GET /admin', $data['logEntries'][0]['route']);
@@ -171,7 +171,7 @@ class LogControllerTest extends TestCase
{
$logContent = implode("\n", [
'[2025-01-31 12:00:00] ERROR: 127.0.0.1 [GET /admin] - Admin error',
- '[2025-01-31 12:01:00] INFO: 127.0.0.1 [GET /admin] - Admin info',
+ '[2025-01-31 12:01:00] INFO: 127.0.0.1 [GET /admin] - Admin info',
'[2025-01-31 12:02:00] ERROR: 127.0.0.1 [GET /] - Home error'
]);
@@ -180,7 +180,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData('ERROR', 'GET /admin');
-
+
// Should only include entries matching both filters
$this->assertCount(1, $data['logEntries']);
$this->assertEquals('ERROR', $data['logEntries'][0]['level']);
@@ -204,7 +204,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData();
-
+
// Should read from all log files, newest first
$this->assertCount(3, $data['logEntries']);
$this->assertEquals('Current log entry', $data['logEntries'][0]['message']);
@@ -227,7 +227,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData();
-
+
// Should extract unique routes, sorted
$expectedRoutes = ['GET /', 'GET /admin', 'POST /admin'];
$this->assertEquals($expectedRoutes, $data['availableRoutes']);
@@ -247,7 +247,7 @@ class LogControllerTest extends TestCase
// Uses global $app set up in setUp()
$controller = new LogController($this->tempLogDir);
$data = $controller->getLogData();
-
+
// Should only include valid entries, ignore invalid ones
$this->assertCount(2, $data['logEntries']);
$this->assertEquals('Another valid entry', $data['logEntries'][0]['message']);
diff --git a/tests/Controller/TickController/TickControllerTest.php b/tests/Controller/TickController/TickControllerTest.php
index 6f7b4d0..0f90d8f 100644
--- a/tests/Controller/TickController/TickControllerTest.php
+++ b/tests/Controller/TickController/TickControllerTest.php
@@ -6,29 +6,29 @@ use PHPUnit\Framework\TestCase;
class TickControllerTest extends TestCase
{
private $mockPdo;
- private $config;
+ private $settings;
private $user;
protected function setUp(): void
{
// Reset Log state to prevent test pollution
Log::init(sys_get_temp_dir() . '/tkr_controller_test.log');
-
+
// Set up mocks
$this->mockPdo = $this->createMock(PDO::class);
-
- $this->config = new ConfigModel($this->mockPdo);
- $this->config->baseUrl = 'https://example.com';
- $this->config->basePath = '/tkr/';
- $this->config->itemsPerPage = 10;
-
+
+ $this->settings = new SettingsModel($this->mockPdo);
+ $this->settings->baseUrl = 'https://example.com';
+ $this->settings->basePath = '/tkr/';
+ $this->settings->itemsPerPage = 10;
+
$this->user = new UserModel($this->mockPdo);
// Set up global $app for simplified dependency access
global $app;
$app = [
'db' => $this->mockPdo,
- 'config' => $this->config,
+ 'settings' => $this->settings,
'user' => $this->user,
];
}
@@ -60,10 +60,10 @@ class TickControllerTest extends TestCase
// Capture output since render() outputs directly
ob_start();
-
+
$controller = new TickController();
$controller->index(123);
-
+
$output = ob_get_clean();
// Should not be a 404 or 500 error
@@ -94,10 +94,10 @@ class TickControllerTest extends TestCase
// Capture output
ob_start();
-
+
$controller = new TickController();
$controller->index(999);
-
+
$output = ob_get_clean();
// Should return 404 error
@@ -123,10 +123,10 @@ class TickControllerTest extends TestCase
// Capture output
ob_start();
-
+
$controller = new TickController();
$controller->index(456);
-
+
$output = ob_get_clean();
// Should return 404 error for empty data
@@ -143,10 +143,10 @@ class TickControllerTest extends TestCase
// Capture output
ob_start();
-
+
$controller = new TickController();
$controller->index(123);
-
+
$output = ob_get_clean();
// Should return 500 error
diff --git a/tests/Feed/AtomGeneratorTest.php b/tests/Feed/AtomGeneratorTest.php
index cbe89c9..fcaf02d 100644
--- a/tests/Feed/AtomGeneratorTest.php
+++ b/tests/Feed/AtomGeneratorTest.php
@@ -7,12 +7,12 @@ class AtomGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->siteDescription = 'Test Description';
- $config->baseUrl = 'https://example.com';
- $config->basePath = '/tkr/';
- return $config;
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->siteDescription = 'Test Description';
+ $settings->baseUrl = 'https://example.com';
+ $settings->basePath = '/tkr/';
+ return $settings;
}
private function createSampleTicks() {
@@ -23,10 +23,10 @@ class AtomGeneratorTest extends TestCase
}
public function testCanGenerateValidAtom() {
- $config = $this->createMockConfig();
+ $settings = $this->createMockConfig();
$ticks = $this->createSampleTicks();
- $generator = new AtomGenerator($config, $ticks);
+ $generator = new AtomGenerator($settings, $ticks);
$xml = $generator->generate();
// Test XML structure
@@ -58,8 +58,8 @@ class AtomGeneratorTest extends TestCase
}
public function testCanHandleEmptyTickList() {
- $config = $this->createMockConfig();
- $generator = new AtomGenerator($config, []);
+ $settings = $this->createMockConfig();
+ $generator = new AtomGenerator($settings, []);
$xml = $generator->generate();
// Should still be valid Atom with no entries
@@ -85,7 +85,7 @@ class AtomGeneratorTest extends TestCase
}
public function testCanHandleSpecialCharactersAndUnicode() {
- $config = $this->createMockConfig();
+ $settings = $this->createMockConfig();
// Test various challenging characters
$ticks = [
@@ -111,7 +111,7 @@ class AtomGeneratorTest extends TestCase
]
];
- $generator = new AtomGenerator($config, $ticks);
+ $generator = new AtomGenerator($settings, $ticks);
$xml = $generator->generate();
// Test that emojis are preserved
diff --git a/tests/Feed/FeedGeneratorTest.php b/tests/Feed/FeedGeneratorTest.php
index 8fabfcf..c6e9eed 100644
--- a/tests/Feed/FeedGeneratorTest.php
+++ b/tests/Feed/FeedGeneratorTest.php
@@ -7,12 +7,12 @@ class FeedGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->siteDescription = 'Test Description';
- $config->baseUrl = 'https://example.com';
- $config->basePath = '/tkr/';
- return $config;
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->siteDescription = 'Test Description';
+ $settings->baseUrl = 'https://example.com';
+ $settings->basePath = '/tkr/';
+ return $settings;
}
private function createSampleTicks() {
@@ -22,11 +22,11 @@ class FeedGeneratorTest extends TestCase
];
}
- private function createTestGenerator($config = null, $ticks = null) {
- $config = $config ?? $this->createMockConfig();
+ private function createTestGenerator($settings = null, $ticks = null) {
+ $settings = $settings ?? $this->createMockConfig();
$ticks = $ticks ?? $this->createSampleTicks();
- return new class($config, $ticks) extends FeedGenerator {
+ return new class($settings, $ticks) extends FeedGenerator {
public function generate(): string {
return '
content';
}
@@ -69,12 +69,12 @@ class FeedGeneratorTest extends TestCase
public function testUrlMethodsHandleSubdomainConfiguration() {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->baseUrl = 'https://tkr.example.com';
- $config->basePath = '/';
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->baseUrl = 'https://tkr.example.com';
+ $settings->basePath = '/';
- $generator = $this->createTestGenerator($config, []);
+ $generator = $this->createTestGenerator($settings, []);
$this->assertEquals('https://tkr.example.com/', $generator->testGetSiteUrl());
$this->assertEquals('https://tkr.example.com/tick/456', $generator->testBuildTickUrl(456));
@@ -82,12 +82,12 @@ class FeedGeneratorTest extends TestCase
public function testUrlMethodsHandleEmptyBasePath() {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->baseUrl = 'https://example.com';
- $config->basePath = '';
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->baseUrl = 'https://example.com';
+ $settings->basePath = '';
- $generator = $this->createTestGenerator($config, []);
+ $generator = $this->createTestGenerator($settings, []);
$this->assertEquals('https://example.com/', $generator->testGetSiteUrl());
$this->assertEquals('https://example.com/tick/789', $generator->testBuildTickUrl(789));
@@ -106,12 +106,12 @@ class FeedGeneratorTest extends TestCase
foreach ($testCases as [$basePath, $expectedSiteUrl, $expectedTickUrl]) {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->baseUrl = 'https://example.com';
- $config->basePath = $basePath;
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->baseUrl = 'https://example.com';
+ $settings->basePath = $basePath;
- $generator = $this->createTestGenerator($config, []);
+ $generator = $this->createTestGenerator($settings, []);
$this->assertEquals($expectedSiteUrl, $generator->testGetSiteUrl(), "Failed for basePath: '$basePath'");
$this->assertEquals($expectedTickUrl, $generator->testBuildTickUrl(123), "Failed for basePath: '$basePath'");
diff --git a/tests/Feed/RssGeneratorTest.php b/tests/Feed/RssGeneratorTest.php
index b90a2c0..270728c 100644
--- a/tests/Feed/RssGeneratorTest.php
+++ b/tests/Feed/RssGeneratorTest.php
@@ -7,12 +7,12 @@ class RssGeneratorTest extends TestCase
{
private function createMockConfig() {
$mockPdo = $this->createMock(PDO::class);
- $config = new ConfigModel($mockPdo);
- $config->siteTitle = 'Test Site';
- $config->siteDescription = 'Test Description';
- $config->baseUrl = 'https://example.com';
- $config->basePath = '/tkr/';
- return $config;
+ $settings = new SettingsModel($mockPdo);
+ $settings->siteTitle = 'Test Site';
+ $settings->siteDescription = 'Test Description';
+ $settings->baseUrl = 'https://example.com';
+ $settings->basePath = '/tkr/';
+ return $settings;
}
private function createSampleTicks() {
@@ -23,10 +23,10 @@ class RssGeneratorTest extends TestCase
}
public function testCanGenerateValidRss() {
- $config = $this->createMockConfig();
+ $settings = $this->createMockConfig();
$ticks = $this->createSampleTicks();
- $generator = new RssGenerator($config, $ticks);
+ $generator = new RssGenerator($settings, $ticks);
$xml = $generator->generate();
// Test XML structure
@@ -56,8 +56,8 @@ class RssGeneratorTest extends TestCase
}
public function testCanHandleEmptyTickList() {
- $config = $this->createMockConfig();
- $generator = new RssGenerator($config, []);
+ $settings = $this->createMockConfig();
+ $generator = new RssGenerator($settings, []);
$xml = $generator->generate();
// Should still be valid RSS with no items
@@ -81,7 +81,7 @@ class RssGeneratorTest extends TestCase
}
public function testCanHandleSpecialCharactersAndUnicode() {
- $config = $this->createMockConfig();
+ $settings = $this->createMockConfig();
// Test various challenging characters
$ticks = [
@@ -107,7 +107,7 @@ class RssGeneratorTest extends TestCase
]
];
- $generator = new RssGenerator($config, $ticks);
+ $generator = new RssGenerator($settings, $ticks);
$xml = $generator->generate();
// Test that emojis are preserved
diff --git a/tests/Framework/Log/LogTest.php b/tests/Framework/Log/LogTest.php
index 9047827..ab225af 100644
--- a/tests/Framework/Log/LogTest.php
+++ b/tests/Framework/Log/LogTest.php
@@ -13,9 +13,9 @@ class LogTest extends TestCase
// Create a temporary directory for test logs
$this->tempLogDir = sys_get_temp_dir() . '/tkr_test_logs_' . uniqid();
mkdir($this->tempLogDir, 0777, true);
-
+
$this->testLogFile = $this->tempLogDir . '/tkr.log';
-
+
// Initialize Log with test file and reset route context
Log::init($this->testLogFile);
Log::setRouteContext('');
@@ -32,31 +32,31 @@ class LogTest extends TestCase
private function deleteDirectory(string $dir): void
{
if (!is_dir($dir)) return;
-
+
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
-
+
foreach ($iterator as $path) {
$path->isDir() ? rmdir($path->getRealPath()) : unlink($path->getRealPath());
}
rmdir($dir);
}
-
+
private function setLogLevel(int $level): void
{
global $app;
- $app = ['config' => (object)['logLevel' => $level]];
+ $app = ['settings' => (object)['logLevel' => $level]];
}
-
+
private function assertLogContains(string $message): void
{
$this->assertFileExists($this->testLogFile);
$logContent = file_get_contents($this->testLogFile);
$this->assertStringContainsString($message, $logContent);
}
-
+
private function assertLogDoesNotContain(string $message): void
{
$this->assertFileExists($this->testLogFile);
@@ -68,9 +68,9 @@ class LogTest extends TestCase
{
Log::setRouteContext('GET /admin');
$this->setLogLevel(1); // DEBUG level
-
+
Log::debug('Test message');
-
+
$logContent = file_get_contents($this->testLogFile);
$this->assertStringContainsString('[GET /admin]', $logContent);
$this->assertStringContainsString('Test message', $logContent);
@@ -80,11 +80,11 @@ class LogTest extends TestCase
{
Log::setRouteContext('');
$this->setLogLevel(1);
-
+
Log::info('Test without route');
-
+
$logContent = file_get_contents($this->testLogFile);
-
+
// Should match format without route context: [timestamp] LEVEL: IP - message
$this->assertMatchesRegularExpression(
'/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] INFO: .+ - Test without route/',
@@ -95,12 +95,12 @@ class LogTest extends TestCase
public function testLogLevelFiltering(): void
{
$this->setLogLevel(3); // WARNING level
-
+
Log::debug('Debug message'); // Should be filtered out
- Log::info('Info message'); // Should be filtered out
+ Log::info('Info message'); // Should be filtered out
Log::warning('Warning message'); // Should be logged
Log::error('Error message'); // Should be logged
-
+
$this->assertLogDoesNotContain('Debug message');
$this->assertLogDoesNotContain('Info message');
$this->assertLogContains('Warning message');
@@ -111,11 +111,11 @@ class LogTest extends TestCase
{
Log::setRouteContext('POST /admin');
$this->setLogLevel(1);
-
+
Log::error('Test error message');
-
+
$logContent = file_get_contents($this->testLogFile);
-
+
// Check log format: [timestamp] LEVEL: IP [route] - message
$this->assertMatchesRegularExpression(
'/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] ERROR: .+ \[POST \/admin\] - Test error message/',
@@ -126,15 +126,15 @@ class LogTest extends TestCase
public function testInitCreatesLogDirectory(): void
{
$newLogFile = $this->tempLogDir . '/nested/logs/test.log';
-
+
// Directory doesn't exist yet
$this->assertDirectoryDoesNotExist(dirname($newLogFile));
-
+
Log::init($newLogFile);
-
+
// init() should create the directory
$this->assertDirectoryExists(dirname($newLogFile));
-
+
// Verify we can actually write to it
$this->setLogLevel(1);
Log::info('Test directory creation');
@@ -144,35 +144,35 @@ class LogTest extends TestCase
public function testLogRotation(): void
{
$this->setLogLevel(1);
-
+
// Create a log file with exactly 1000 lines (the rotation threshold)
$logLines = str_repeat("[2025-01-31 12:00:00] INFO: 127.0.0.1 - Test line\n", 1000);
file_put_contents($this->testLogFile, $logLines);
-
+
// This should trigger rotation
Log::info('This should trigger rotation');
-
+
// Verify rotation happened
$this->assertFileExists($this->testLogFile . '.1');
$this->assertLogContains('This should trigger rotation');
}
-
+
public function testLogRotationLimitsFileCount(): void
{
$this->setLogLevel(1);
-
+
// Create 5 existing rotated log files (.1 through .5)
for ($i = 1; $i <= 5; $i++) {
file_put_contents($this->testLogFile . '.' . $i, "Old log file $i\n");
}
-
+
// Create main log file at rotation threshold
$logLines = str_repeat("[2025-01-31 12:00:00] INFO: 127.0.0.1 - Test line\n", 1000);
file_put_contents($this->testLogFile, $logLines);
-
+
// This should trigger rotation and delete the oldest file (.5)
Log::info('Trigger rotation with max files');
-
+
// Verify rotation happened and file count is limited
$this->assertFileExists($this->testLogFile . '.1'); // New rotated file
$this->assertFileExists($this->testLogFile . '.2'); // Old .1 became .2
@@ -180,7 +180,7 @@ class LogTest extends TestCase
$this->assertFileExists($this->testLogFile . '.4'); // Old .3 became .4
$this->assertFileExists($this->testLogFile . '.5'); // Old .4 became .5
$this->assertFileDoesNotExist($this->testLogFile . '.6'); // Old .5 was deleted
-
+
$this->assertLogContains('Trigger rotation with max files');
}
@@ -188,12 +188,12 @@ class LogTest extends TestCase
{
// Set up config without logLevel property (simulates missing config value)
global $app;
- $app = ['config' => (object)[]];
-
+ $app = ['settings' => (object)[]];
+
// Should not throw errors and should default to INFO level
Log::debug('Debug message'); // Should be filtered out (default INFO level = 2)
Log::info('Info message'); // Should be logged
-
+
$this->assertLogDoesNotContain('Debug message');
$this->assertLogContains('Info message');
}
diff --git a/tests/Framework/Util/UtilTest.php b/tests/Framework/Util/UtilTest.php
index cc43f39..eacda25 100644
--- a/tests/Framework/Util/UtilTest.php
+++ b/tests/Framework/Util/UtilTest.php
@@ -151,7 +151,7 @@ final class UtilTest extends TestCase
// Set up global $app with config
global $app;
$app = [
- 'config' => (object)['strictAccessibility' => $strictAccessibility]
+ 'settings' => (object)['strictAccessibility' => $strictAccessibility]
];
$result = Util::linkify($input);
@@ -162,12 +162,12 @@ final class UtilTest extends TestCase
// Test linkify without new window
global $app;
$app = [
- 'config' => (object)['strictAccessibility' => false]
+ 'settings' => (object)['strictAccessibility' => false]
];
$input = 'Visit https://example.com';
$expected = 'Visit
https://example.com';
-
+
$result = Util::linkify($input, false); // no new window
$this->assertEquals($expected, $result);
}
@@ -176,7 +176,7 @@ final class UtilTest extends TestCase
// Test basic case with REMOTE_ADDR
$_SERVER['REMOTE_ADDR'] = '192.168.1.100';
unset($_SERVER['HTTP_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_X_REAL_IP']);
-
+
$result = Util::getClientIp();
$this->assertEquals('192.168.1.100', $result);
}
@@ -187,7 +187,7 @@ final class UtilTest extends TestCase
$_SERVER['HTTP_X_FORWARDED_FOR'] = '10.0.0.2';
$_SERVER['HTTP_X_REAL_IP'] = '10.0.0.3';
$_SERVER['REMOTE_ADDR'] = '10.0.0.4';
-
+
$result = Util::getClientIp();
$this->assertEquals('10.0.0.1', $result); // Should use HTTP_CLIENT_IP
}
@@ -195,7 +195,7 @@ final class UtilTest extends TestCase
public function testGetClientIpUnknown(): void {
// Test when no IP is available
unset($_SERVER['HTTP_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER['HTTP_X_REAL_IP'], $_SERVER['REMOTE_ADDR']);
-
+
$result = Util::getClientIp();
$this->assertEquals('unknown', $result);
}
diff --git a/tkr-setup.php b/tkr-setup.php
index df63295..676af17 100644
--- a/tkr-setup.php
+++ b/tkr-setup.php
@@ -175,11 +175,11 @@ try {
echo "💾 Saving configuration...\n";
// Create/update settings
- $configModel = new ConfigModel($db);
- $configModel->siteTitle = $siteTitle;
- $configModel->baseUrl = $baseUrl;
- $configModel->basePath = $basePath;
- $config = $configModel->save();
+ $settingsModel = new SettingsModel($db);
+ $settingsModel->siteTitle = $siteTitle;
+ $settingsModel->baseUrl = $baseUrl;
+ $settingsModel->basePath = $basePath;
+ $settings = $settingsModel->save();
// Create admin user
$userModel = new UserModel($db);