Next healthcheck due date, plus a test suite (hc-nkq)
Lets a technician record when a site should next be looked at, and surfaces that in the dashboard so it can be used for planning. Server: - migration 0002 adds healthchecks.next_due, a VARCHAR(10) 'YYYY-MM-DD' calendar date rather than a timestamp — it's a diary date with no time-of-day, and a timestamp would render as the wrong day off-server. - Kept per-session rather than on a sites table, so the history of what was scheduled when is preserved. A site's current next-due is the value on its most recent session: if the latest visit scheduled nothing, the site reads as unscheduled rather than showing the just-completed visit as overdue. - Validate::optionalDate() round-trips through createFromFormat, which rejects both '2026-2-3' and '2026-02-30' (silently rolled to March 2nd). - next_due follows the same missing-vs-null PUT contract as finished_at, and lives in its own Store::setNextDue() so finishing or reopening a session never disturbs the date and vice versa. - Dashboard shows it on the site list, the per-site session table and the session detail, flagged overdue / today / soon (within a fortnight). Plugin: - Date control on both the active and finished panels — the moment you know when to return is often wrap-up, after the report is generated. - Write-through like every other mutation. Bad input is rejected with a notice rather than silently clearing an existing date. - Included in both the Markdown and HTML reports. Tests (new — tests/, export-ignored from the plugin zip): - Hand-rolled harness, no composer/PHPUnit, in keeping with the repo. - 42 tests: plugin-side date parsing units, plus server integration tests driven over real HTTP against a temp `php -S` instance with a throwaway SQLite DB, so a real server/config.php is never touched. - Mutation-checked: dropping the array_key_exists guard on PUT fails three tests, as intended.
This commit is contained in:
@@ -70,6 +70,7 @@ final class ATT_HC_Session {
|
||||
'site_url' => $reporting_url,
|
||||
'wp_version' => get_bloginfo('version'),
|
||||
'php_version' => PHP_VERSION,
|
||||
'next_due' => null,
|
||||
'steps' => [],
|
||||
];
|
||||
|
||||
@@ -180,6 +181,12 @@ final class ATT_HC_Session {
|
||||
public function php_version(): string { return (string) ($this->data['php_version'] ?? ''); }
|
||||
public function data(): array { return $this->data; }
|
||||
|
||||
/** Scheduled date of the next healthcheck for this site, as 'YYYY-MM-DD', or null. */
|
||||
public function next_due(): ?string {
|
||||
$raw = $this->data['next_due'] ?? null;
|
||||
return is_string($raw) && $raw !== '' ? $raw : null;
|
||||
}
|
||||
|
||||
public function step_state(string $step_id): array {
|
||||
return $this->data['steps'][$step_id] ?? [
|
||||
'status' => self::STATUS_NOT_STARTED,
|
||||
@@ -214,6 +221,40 @@ final class ATT_HC_Session {
|
||||
update_option(ATT_HC_OPT_SESSION, $this->data, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule (or clear, with null) the next healthcheck for this site.
|
||||
*
|
||||
* Write-through like every other mutation: the server PUT has to succeed
|
||||
* before the local cache moves. Allowed on a finished session too — the
|
||||
* natural moment to decide when to come back is at wrap-up, which may be
|
||||
* after the report has been generated.
|
||||
*
|
||||
* @param string|null $date 'YYYY-MM-DD', or null to clear.
|
||||
* @throws ATT_HC_Api_Exception
|
||||
*/
|
||||
public function set_next_due(?string $date): void {
|
||||
$date = $date === null ? null : self::sanitise_due_date($date);
|
||||
ATT_HC_Api::update_healthcheck($this->id(), ['next_due' => $date]);
|
||||
$this->data['next_due'] = $date;
|
||||
update_option(ATT_HC_OPT_SESSION, $this->data, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise a user-entered date to 'YYYY-MM-DD', or null if it isn't a real
|
||||
* calendar date in that format.
|
||||
*
|
||||
* The round-trip comparison is what makes this strict: createFromFormat
|
||||
* happily accepts '2026-2-3' and rolls '2026-02-30' forward into March, and
|
||||
* both re-format to something other than what was typed.
|
||||
*/
|
||||
public static function sanitise_due_date(string $raw): ?string {
|
||||
$raw = trim($raw);
|
||||
if ($raw === '') return null;
|
||||
$date = DateTimeImmutable::createFromFormat('!Y-m-d', $raw);
|
||||
if ($date === false || $date->format('Y-m-d') !== $raw) return null;
|
||||
return $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the session finished. Server PUT first, then cache update.
|
||||
*
|
||||
@@ -294,6 +335,9 @@ final class ATT_HC_Session {
|
||||
'site_url' => (string) $remote['reporting_url'],
|
||||
'wp_version' => (string) ($remote['wp_version'] ?? ''),
|
||||
'php_version' => (string) ($remote['php_version'] ?? ''),
|
||||
'next_due' => isset($remote['next_due']) && $remote['next_due'] !== null && $remote['next_due'] !== ''
|
||||
? (string) $remote['next_due']
|
||||
: null,
|
||||
'steps' => [],
|
||||
'autocheck' => [],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user