104 lines
3.0 KiB
PHP
104 lines
3.0 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Standalone test for the RecoReco date math -- no Kanboard / PHPUnit harness needed.
|
|
* Run with: php Test/OccurrenceCalculatorTest.php
|
|
*
|
|
* OccurrenceCalculator is pure, so this exercises the highest-risk correctness surface (month
|
|
* lengths, leap years, the last-day rule, the drift-free clamp).
|
|
*/
|
|
|
|
require __DIR__.'/../Model/OccurrenceCalculator.php';
|
|
|
|
use Kanboard\Plugin\RecoReco\Model\OccurrenceCalculator;
|
|
|
|
date_default_timezone_set('UTC');
|
|
|
|
$calc = new OccurrenceCalculator();
|
|
$failures = 0;
|
|
|
|
function ts($str)
|
|
{
|
|
return strtotime($str.' UTC');
|
|
}
|
|
|
|
function check($label, $got, $expected, &$failures)
|
|
{
|
|
$ok = $got === $expected;
|
|
$g = $got === null ? 'null' : date('Y-m-d H:i', $got);
|
|
$e = $expected === null ? 'null' : date('Y-m-d H:i', $expected);
|
|
echo ($ok ? 'PASS' : 'FAIL').' '.$label.' got='.$g.' expected='.$e."\n";
|
|
if (! $ok) {
|
|
$failures++;
|
|
}
|
|
}
|
|
|
|
// 1. Plain day-of-month: the 15th, every month.
|
|
check(
|
|
'monthly 15th, next after Jan 20',
|
|
$calc->occurrenceFrom(ts('2023-01-15 10:30'), 'monthly_day', false, ts('2023-01-20 00:00'), false),
|
|
ts('2023-02-15 10:30'),
|
|
$failures
|
|
);
|
|
|
|
// 2. Drift-free clamp: the 30th -> Feb clamps to 28 -> back to 30 in March (NOT 28).
|
|
check(
|
|
'30th -> Feb (non-leap) clamps to 28',
|
|
$calc->occurrenceFrom(ts('2023-01-30 09:00'), 'monthly_day', false, ts('2023-01-30 09:00'), false),
|
|
ts('2023-02-28 09:00'),
|
|
$failures
|
|
);
|
|
check(
|
|
'30th -> March returns to 30 (no drift)',
|
|
$calc->occurrenceFrom(ts('2023-01-30 09:00'), 'monthly_day', false, ts('2023-02-28 09:00'), false),
|
|
ts('2023-03-30 09:00'),
|
|
$failures
|
|
);
|
|
|
|
// 3. Last-day rule: last day of each month.
|
|
check(
|
|
'last-day: Jan 31 -> Feb 28 (non-leap)',
|
|
$calc->occurrenceFrom(ts('2023-01-31 08:00'), 'monthly_day', true, ts('2023-01-31 08:00'), false),
|
|
ts('2023-02-28 08:00'),
|
|
$failures
|
|
);
|
|
check(
|
|
'last-day: Feb -> March 31',
|
|
$calc->occurrenceFrom(ts('2023-01-31 08:00'), 'monthly_day', true, ts('2023-02-28 08:00'), false),
|
|
ts('2023-03-31 08:00'),
|
|
$failures
|
|
);
|
|
|
|
// 4. Leap year: last day of Feb 2024 is the 29th.
|
|
check(
|
|
'last-day: Feb 2024 -> 29th (leap)',
|
|
$calc->occurrenceFrom(ts('2024-01-31 08:00'), 'monthly_day', true, ts('2024-02-01 00:00'), false),
|
|
ts('2024-02-29 08:00'),
|
|
$failures
|
|
);
|
|
|
|
// 5. Inclusive vs exclusive at an exact occurrence.
|
|
check(
|
|
'inclusive returns the same instant',
|
|
$calc->occurrenceFrom(ts('2023-03-10 12:00'), 'monthly_day', false, ts('2023-03-10 12:00'), true),
|
|
ts('2023-03-10 12:00'),
|
|
$failures
|
|
);
|
|
check(
|
|
'exclusive skips to next month',
|
|
$calc->occurrenceFrom(ts('2023-03-10 12:00'), 'monthly_day', false, ts('2023-03-10 12:00'), false),
|
|
ts('2023-04-10 12:00'),
|
|
$failures
|
|
);
|
|
|
|
// 6. Unsupported frequency (until v1.1) returns null.
|
|
check(
|
|
'weekly not supported yet -> null',
|
|
$calc->occurrenceFrom(ts('2023-03-10 12:00'), 'weekly', false, ts('2023-03-10 12:00'), false),
|
|
null,
|
|
$failures
|
|
);
|
|
|
|
echo "\n".($failures === 0 ? 'ALL PASSED' : $failures.' FAILURE(S)')."\n";
|
|
exit($failures === 0 ? 0 : 1);
|