Add TickController tests and logs (#50)
Closes https://gitea.subcultureofone.org/greg/tkr/issues/45 Reviewed-on: https://gitea.subcultureofone.org/greg/tkr/pulls/50 Co-authored-by: Greg Sarjeant <greg@subcultureofone.org> Co-committed-by: Greg Sarjeant <greg@subcultureofone.org>
This commit is contained in:
parent
0b0fd29913
commit
2e82f946ae
@ -1,12 +1,29 @@
|
||||
<?php
|
||||
|
||||
class TickController extends Controller{
|
||||
//public function index(string $year, string $month, string $day, string $hour, string $minute, string $second){
|
||||
public function index(int $id){
|
||||
global $app;
|
||||
|
||||
$tickModel = new TickModel($app['db'], $app['config']);
|
||||
$vars = $tickModel->get($id);
|
||||
$this->render('tick.php', $vars);
|
||||
Log::debug("Fetching tick with ID: {$id}");
|
||||
|
||||
try {
|
||||
$tickModel = new TickModel($app['db'], $app['config']);
|
||||
$vars = $tickModel->get($id);
|
||||
|
||||
if (empty($vars) || !isset($vars['tick'])) {
|
||||
Log::warning("Tick not found for ID: {$id}");
|
||||
http_response_code(404);
|
||||
echo '<h1>404 - Tick Not Found</h1>';
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info("Successfully loaded tick {$id}: " . substr($vars['tick'], 0, 50) . (strlen($vars['tick']) > 50 ? '...' : ''));
|
||||
$this->render('tick.php', $vars);
|
||||
|
||||
} catch (Exception $e) {
|
||||
Log::error("Failed to load tick {$id}: " . $e->getMessage());
|
||||
http_response_code(500);
|
||||
echo '<h1>500 - Internal Server Error</h1>';
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,11 @@ class TickModel {
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// TODO: Test for existence of row and handle absence.
|
||||
// Handle case where tick doesn't exist
|
||||
if ($row === false || empty($row) || !isset($row['timestamp']) || !isset($row['tick'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'tickTime' => $row['timestamp'],
|
||||
'tick' => $row['tick'],
|
||||
|
273
tests/Controller/TickController/TickControllerTest.php
Normal file
273
tests/Controller/TickController/TickControllerTest.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TickControllerTest extends TestCase
|
||||
{
|
||||
private $mockPdo;
|
||||
private $config;
|
||||
private $user;
|
||||
private string $tempLogDir;
|
||||
private string $testLogFile;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// Set up log capture
|
||||
$this->tempLogDir = sys_get_temp_dir() . '/tkr_test_logs_' . uniqid();
|
||||
mkdir($this->tempLogDir, 0777, true);
|
||||
|
||||
$this->testLogFile = $this->tempLogDir . '/tkr.log';
|
||||
Log::init($this->testLogFile);
|
||||
Log::setRouteContext('GET tick/123');
|
||||
|
||||
// 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->config->logLevel = 1; // DEBUG level for testing
|
||||
|
||||
$this->user = new UserModel($this->mockPdo);
|
||||
|
||||
// Set up global $app for simplified dependency access
|
||||
global $app;
|
||||
$app = [
|
||||
'db' => $this->mockPdo,
|
||||
'config' => $this->config,
|
||||
'user' => $this->user,
|
||||
];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
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 testIndexWithValidTick(): void
|
||||
{
|
||||
// Set up mock database response for successful tick retrieval
|
||||
$expectedTickData = [
|
||||
'tickTime' => '2025-01-31 12:00:00',
|
||||
'tick' => 'This is a test tick with some content'
|
||||
];
|
||||
|
||||
$mockStatement = $this->createMock(PDOStatement::class);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('execute')
|
||||
->with([123]);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(PDO::FETCH_ASSOC)
|
||||
->willReturn([
|
||||
'timestamp' => '2025-01-31 12:00:00',
|
||||
'tick' => 'This is a test tick with some content'
|
||||
]);
|
||||
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willReturn($mockStatement);
|
||||
|
||||
// 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
|
||||
$this->assertStringNotContainsString('404', $output);
|
||||
$this->assertStringNotContainsString('500', $output);
|
||||
|
||||
// Should contain the tick content (through the template)
|
||||
// Note: We can't easily test the full template rendering without more setup,
|
||||
// but we can verify no error occurred
|
||||
|
||||
// Verify logging
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Fetching tick with ID: 123', $logContent);
|
||||
$this->assertStringContainsString('Successfully loaded tick 123: This is a test tick with some content', $logContent);
|
||||
}
|
||||
|
||||
public function testIndexWithNonexistentTick(): void
|
||||
{
|
||||
// Mock database returns null/empty for non-existent tick
|
||||
$mockStatement = $this->createMock(PDOStatement::class);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('execute')
|
||||
->with([999]);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(PDO::FETCH_ASSOC)
|
||||
->willReturn(false); // No row found
|
||||
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willReturn($mockStatement);
|
||||
|
||||
// Capture output
|
||||
ob_start();
|
||||
|
||||
$controller = new TickController();
|
||||
$controller->index(999);
|
||||
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Should return 404 error
|
||||
$this->assertStringContainsString('404 - Tick Not Found', $output);
|
||||
|
||||
// Verify logging
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Fetching tick with ID: 999', $logContent);
|
||||
$this->assertStringContainsString('Tick not found for ID: 999', $logContent);
|
||||
}
|
||||
|
||||
public function testIndexWithEmptyTickData(): void
|
||||
{
|
||||
// Mock database returns empty array (edge case)
|
||||
$mockStatement = $this->createMock(PDOStatement::class);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('execute')
|
||||
->with([456]);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(PDO::FETCH_ASSOC)
|
||||
->willReturn([]); // Empty array
|
||||
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willReturn($mockStatement);
|
||||
|
||||
// Capture output
|
||||
ob_start();
|
||||
|
||||
$controller = new TickController();
|
||||
$controller->index(456);
|
||||
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Should return 404 error for empty data
|
||||
$this->assertStringContainsString('404 - Tick Not Found', $output);
|
||||
|
||||
// Verify logging
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Tick not found for ID: 456', $logContent);
|
||||
}
|
||||
|
||||
public function testIndexWithDatabaseException(): void
|
||||
{
|
||||
// Mock database throws exception
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willThrowException(new PDOException('Database connection failed'));
|
||||
|
||||
// Capture output
|
||||
ob_start();
|
||||
|
||||
$controller = new TickController();
|
||||
$controller->index(123);
|
||||
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Should return 500 error
|
||||
$this->assertStringContainsString('500 - Internal Server Error', $output);
|
||||
|
||||
// Verify error logging
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Failed to load tick 123: Database connection failed', $logContent);
|
||||
}
|
||||
|
||||
public function testIndexWithLongTickContent(): void
|
||||
{
|
||||
// Test logging truncation for long tick content
|
||||
$longContent = str_repeat('This is a very long tick content that should be truncated in the logs. ', 10);
|
||||
|
||||
$mockStatement = $this->createMock(PDOStatement::class);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('execute')
|
||||
->with([789]);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(PDO::FETCH_ASSOC)
|
||||
->willReturn([
|
||||
'timestamp' => '2025-01-31 15:30:00',
|
||||
'tick' => $longContent
|
||||
]);
|
||||
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willReturn($mockStatement);
|
||||
|
||||
// Capture output
|
||||
ob_start();
|
||||
|
||||
$controller = new TickController();
|
||||
$controller->index(789);
|
||||
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Verify logging shows truncated content with ellipsis
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Successfully loaded tick 789:', $logContent);
|
||||
$this->assertStringContainsString('...', $logContent); // Should be truncated
|
||||
|
||||
// Verify the log doesn't contain the full long content
|
||||
$this->assertStringNotContainsString($longContent, $logContent);
|
||||
}
|
||||
|
||||
public function testIndexWithShortTickContent(): void
|
||||
{
|
||||
// Test that short content is not truncated in logs
|
||||
$shortContent = 'Short tick';
|
||||
|
||||
$mockStatement = $this->createMock(PDOStatement::class);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('execute')
|
||||
->with([100]);
|
||||
$mockStatement->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(PDO::FETCH_ASSOC)
|
||||
->willReturn([
|
||||
'timestamp' => '2025-01-31 09:15:00',
|
||||
'tick' => $shortContent
|
||||
]);
|
||||
|
||||
$this->mockPdo->expects($this->once())
|
||||
->method('prepare')
|
||||
->with('SELECT timestamp, tick FROM tick WHERE id=?')
|
||||
->willReturn($mockStatement);
|
||||
|
||||
// Capture output
|
||||
ob_start();
|
||||
|
||||
$controller = new TickController();
|
||||
$controller->index(100);
|
||||
|
||||
$output = ob_get_clean();
|
||||
|
||||
// Verify logging shows full content without ellipsis
|
||||
$logContent = file_get_contents($this->testLogFile);
|
||||
$this->assertStringContainsString('Successfully loaded tick 100: Short tick', $logContent);
|
||||
$this->assertStringNotContainsString('...', $logContent); // Should NOT be truncated
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user