tkr/tests/Controller/AdminController/AdminControllerTest.php
Greg Sarjeant 0b0fd29913 simplify-dependency-injection (#44)
Closes https://gitea.subcultureofone.org/greg/tkr/issues/43

Use a global $app dictionary to manage global state rather than having complex class constructors that expect three input arguments. Update and fix tests. Add tests for Util class functions that broke in the refactor.

Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/44
Co-authored-by: Greg Sarjeant <greg@subcultureofone.org>
Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
2025-08-02 19:52:33 +00:00

393 lines
14 KiB
PHP

<?php
require_once dirname(dirname(dirname(__DIR__))) . "/config/bootstrap.php";
use PHPUnit\Framework\TestCase;
class AdminControllerTest extends TestCase
{
private PDO $mockPdo;
private ConfigModel $config;
private UserModel $user;
private string $tempLogDir;
protected function setUp(): void
{
// Set up temporary logging
$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
$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->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,
'user' => $this->user,
];
// Set log level on config for Log class
$this->config->logLevel = 1; // Allow DEBUG level logs
}
protected function tearDown(): void
{
// Clean up temp directory
if (is_dir($this->tempLogDir)) {
$this->deleteDirectory($this->tempLogDir);
}
}
private function deleteDirectory(string $dir): void
{
if (!is_dir($dir)) return;
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
is_dir($path) ? $this->deleteDirectory($path) : unlink($path);
}
rmdir($dir);
}
public function testGetAdminDataRegularMode(): void
{
$controller = new AdminController();
$data = $controller->getAdminData(false);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
// Should be the injected instances
$this->assertSame($this->config, $data['config']);
$this->assertSame($this->user, $data['user']);
$this->assertFalse($data['isSetup']);
}
public function testGetAdminDataSetupMode(): void
{
$controller = new AdminController();
$data = $controller->getAdminData(true);
// Should return proper structure
$this->assertArrayHasKey('config', $data);
$this->assertArrayHasKey('user', $data);
$this->assertArrayHasKey('isSetup', $data);
// Should be the injected instances
$this->assertSame($this->config, $data['config']);
$this->assertSame($this->user, $data['user']);
$this->assertTrue($data['isSetup']);
}
public function testProcessSettingsSaveWithEmptyData(): void
{
$controller = new AdminController();
$result = $controller->saveSettings([], false);
$this->assertFalse($result['success']);
$this->assertContains('No data provided', $result['errors']);
}
public function testProcessSettingsSaveValidationErrors(): void
{
$controller = new AdminController();
// Test data with multiple validation errors
$postData = [
'username' => '', // Missing username
'display_name' => '', // Missing display name
'website' => 'invalid-url', // Invalid URL
'site_title' => '', // Missing site title
'base_url' => '', // Missing base URL
'base_path' => 'invalid', // Invalid base path
'items_per_page' => 100, // Too high
'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']));
}
public function testProcessSettingsSaveValidData(): void
{
// Mock PDO to simulate successful database operations
$mockStatement = $this->createMock(PDOStatement::class);
$mockStatement->method('execute')->willReturn(true);
$mockStatement->method('fetchColumn')->willReturn(1); // Existing record count
$mockStatement->method('fetch')->willReturnOnConsecutiveCalls(
[
'site_title' => 'Updated Site',
'site_description' => 'Updated Description',
'base_url' => 'https://updated.com',
'base_path' => '/updated',
'items_per_page' => 15,
'css_id' => null,
'strict_accessibility' => true,
'log_level' => 2
],
[
'username' => 'newuser',
'display_name' => 'New User',
'website' => 'https://example.com',
'mood' => ''
]
);
$this->mockPdo->method('prepare')->willReturn($mockStatement);
$this->mockPdo->method('query')->willReturn($mockStatement);
// Create models with mocked PDO
$config = new ConfigModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['user'] = $user;
$controller = new AdminController();
$postData = [
'username' => 'newuser',
'display_name' => 'New User',
'website' => 'https://example.com',
'site_title' => 'Updated Site',
'site_description' => 'Updated Description',
'base_url' => 'https://updated.com',
'base_path' => '/updated',
'items_per_page' => 15,
'strict_accessibility' => 'on',
'log_level' => 2
];
$result = $controller->saveSettings($postData, false);
$this->assertTrue($result['success']);
$this->assertEmpty($result['errors']);
}
public function testProcessSettingsSaveWithPassword(): void
{
// Mock PDO for successful save operations
$mockStatement = $this->createMock(PDOStatement::class);
$mockStatement->method('execute')->willReturn(true);
$mockStatement->method('fetchColumn')->willReturn(1);
$mockStatement->method('fetch')->willReturnOnConsecutiveCalls(
[
'site_title' => 'Test Site',
'site_description' => 'Test Description',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10,
'css_id' => null,
'strict_accessibility' => true,
'log_level' => 2
],
[
'username' => 'testuser',
'display_name' => 'Test User',
'website' => '',
'mood' => ''
]
);
// Verify password hash is called
$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);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['user'] = $user;
$controller = new AdminController();
$postData = [
'username' => 'testuser',
'display_name' => 'Test User',
'site_title' => 'Test Site',
'site_description' => 'Test Description',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10,
'password' => 'newpassword',
'confirm_password' => 'newpassword'
];
$result = $controller->saveSettings($postData, false);
$this->assertTrue($result['success']);
}
public function testProcessSettingsSaveDatabaseError(): void
{
// Mock PDO to throw exception on save
$this->mockPdo->method('query')
->willThrowException(new PDOException("Database error"));
$config = new ConfigModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['user'] = $user;
$controller = new AdminController();
$postData = [
'username' => 'testuser',
'display_name' => 'Test User',
'site_title' => 'Test Site',
'site_description' => 'Test Description',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10
];
$result = $controller->saveSettings($postData, false);
$this->assertFalse($result['success']);
$this->assertContains('Failed to save settings', $result['errors']);
}
public function testLoggingOnAdminPageLoad(): void
{
$controller = new AdminController();
$controller->getAdminData(false);
// Check that logs were written
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Loading admin page', $logContent);
}
public function testLoggingOnSetupPageLoad(): void
{
$controller = new AdminController();
$controller->getAdminData(true);
// Check that logs were written
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Loading admin page (setup mode)', $logContent);
}
public function testLoggingOnValidationErrors(): void
{
$controller = new AdminController();
$postData = [
'username' => '', // Will cause validation error
'display_name' => 'Test User',
'site_title' => 'Test Site',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10
];
$controller->saveSettings($postData, false);
// Check that logs were written
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Settings validation failed', $logContent);
$this->assertStringContainsString('Validation error: Username is required', $logContent);
}
public function testLoggingOnSuccessfulSave(): void
{
// Mock successful database operations
$mockStatement = $this->createMock(PDOStatement::class);
$mockStatement->method('execute')->willReturn(true);
$mockStatement->method('fetchColumn')->willReturn(1);
$mockStatement->method('fetch')->willReturnOnConsecutiveCalls(
[
'site_title' => 'Test Site',
'site_description' => 'Test Description',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10,
'css_id' => null,
'strict_accessibility' => true,
'log_level' => 2
],
[
'username' => 'testuser',
'display_name' => 'Test User',
'website' => '',
'mood' => ''
]
);
$this->mockPdo->method('prepare')->willReturn($mockStatement);
$this->mockPdo->method('query')->willReturn($mockStatement);
$config = new ConfigModel($this->mockPdo);
$user = new UserModel($this->mockPdo);
// Update global $app with test models
global $app;
$app['config'] = $config;
$app['user'] = $user;
$controller = new AdminController();
$postData = [
'username' => 'testuser',
'display_name' => 'Test User',
'site_title' => 'Test Site',
'site_description' => 'Test Description',
'base_url' => 'https://example.com',
'base_path' => '/tkr',
'items_per_page' => 10
];
$controller->saveSettings($postData, false);
// Check that logs were written
$logFile = $this->tempLogDir . '/logs/tkr.log';
$this->assertFileExists($logFile);
$logContent = file_get_contents($logFile);
$this->assertStringContainsString('Processing settings for user: testuser', $logContent);
$this->assertStringContainsString('Site settings updated', $logContent);
$this->assertStringContainsString('User profile updated', $logContent);
}
}