monthly-by-day engine + recoreco:run (v0.3)
This commit is contained in:
103
Test/OccurrenceCalculatorTest.php
Normal file
103
Test/OccurrenceCalculatorTest.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?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);
|
||||
Reference in New Issue
Block a user