| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- <?php
- /*
- * Copyright 2026 Alessandro Chiapparini <sprint_planer_web@chiapparini.org>
- * SPDX-License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * See the LICENSE file in the project root for the full license text.
- */
- declare(strict_types=1);
- namespace App\Tests\Controllers;
- use App\Controllers\AuditController;
- use PHPUnit\Framework\Attributes\DataProvider;
- use PHPUnit\Framework\TestCase;
- /**
- * R01-N12: pin AuditController::validateDateFilters. The repo concatenates
- * "T00:00:00Z" / "T23:59:59Z" onto whatever string it gets and binds the
- * result against the ISO-8601 `occurred_at` column — garbage in silently
- * hides rows. The controller is the gate; this test pins which inputs
- * pass through and which get dropped + flagged.
- */
- final class AuditControllerTest extends TestCase
- {
- /**
- * @return list<array{string,string,string,string,list<string>,string}>
- */
- public static function dateCases(): array
- {
- // [from, to, expectedFrom, expectedTo, expectedErrorKeys, label]
- return [
- ['', '', '', '', [], 'no filters → no errors'],
- ['2026-01-31', '', '2026-01-31', '', [], 'from-only valid'],
- ['', '2026-12-31', '', '2026-12-31', [], 'to-only valid'],
- ['2026-01-01', '2026-12-31', '2026-01-01', '2026-12-31', [], 'both valid'],
- ['2024/01/01', '', '', '', ['from_date'], 'slashes → from dropped, error reported'],
- ['', '2024/01/01', '', '', ['to_date'], 'slashes → to dropped, error reported'],
- ['2024/01/01', '2024/12/31', '', '', ['from_date', 'to_date'], 'both garbage → both dropped, both errors'],
- ['2026-01-31', 'tomorrow', '2026-01-31', '', ['to_date'], 'one-side invalid → only that side errors, the other passes'],
- ['2026-13-01', '', '', '', ['from_date'], 'impossible month → rejected'],
- ['2026-02-30', '', '', '', ['from_date'], 'impossible day for month → rejected (strict round-trip)'],
- ['2026-1-1', '', '', '', ['from_date'], 'unpadded → rejected (strict round-trip)'],
- [' 2026-01-01', '', '', '', ['from_date'], 'leading space → rejected'],
- ['2026-01-01 ', '', '', '', ['from_date'], 'trailing space → rejected'],
- ["'; DROP TABLE audit_log; --", '', '', '', ['from_date'], 'injection-shaped string → rejected'],
- ];
- }
- /** @param list<string> $expectedErrorKeys */
- #[DataProvider('dateCases')]
- public function testValidateDateFilters(
- string $from,
- string $to,
- string $expectedFrom,
- string $expectedTo,
- array $expectedErrorKeys,
- string $label,
- ): void {
- $out = AuditController::validateDateFilters($from, $to);
- $this->assertSame($expectedFrom, $out['from'], "{$label}: from");
- $this->assertSame($expectedTo, $out['to'], "{$label}: to");
- $this->assertSame(
- $expectedErrorKeys,
- array_keys($out['errors']),
- "{$label}: error keys",
- );
- // Error messages are non-empty when present (keeps the contract for
- // the view, which renders {{ dateErrors.from_date }} verbatim).
- foreach ($out['errors'] as $key => $msg) {
- $this->assertNotSame('', $msg, "{$label}: error message for {$key} is non-empty");
- }
- }
- public function testValidatorPreservesValidValuesVerbatim(): void
- {
- // Boundary that the repo concatenates onto: a leap day. Confirms
- // we don't normalise or reformat — the repo gets exactly what the
- // user typed (after gating).
- $out = AuditController::validateDateFilters('2024-02-29', '2024-02-29');
- $this->assertSame('2024-02-29', $out['from']);
- $this->assertSame('2024-02-29', $out['to']);
- $this->assertSame([], $out['errors']);
- }
- public function testValidatorRejectsNonLeapFebTwentyNine(): void
- {
- // 2025 is not a leap year. createFromFormat is lenient and silently
- // rolls over to 2025-03-01; the round-trip equality check we use
- // is what catches it.
- $out = AuditController::validateDateFilters('2025-02-29', '');
- $this->assertSame('', $out['from']);
- $this->assertArrayHasKey('from_date', $out['errors']);
- }
- }
|