Tests / test (push) Successful in 1m1s
Approved by Daniel Haefliger for develop and beta. Author dh_Agent, authenticated account dh. Preserve published battery, charging and overall Energy Pie changes. No deployment or plant control authorization.
146 lines
8.0 KiB
PHP
146 lines
8.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../libs/ManagerPrognoseFormular.php';
|
|
|
|
use Belevo\EnelixEMS\ManagerPrognoseFormular;
|
|
|
|
$count = 0;
|
|
function check(bool $ok, string $name): void
|
|
{
|
|
global $count;
|
|
if (!$ok) {
|
|
throw new RuntimeException('FAIL ' . $name);
|
|
}
|
|
$count++;
|
|
echo 'PASS ' . $name . PHP_EOL;
|
|
}
|
|
|
|
function fields(array $form): array
|
|
{
|
|
foreach ($form['elements'] as $element) {
|
|
if (($element['name'] ?? '') !== 'PrognoseForecast') {
|
|
continue;
|
|
}
|
|
$fields = [];
|
|
foreach ($element['items'] as $field) {
|
|
if (isset($field['name'])) {
|
|
if (isset($fields[$field['name']])) {
|
|
throw new RuntimeException('Duplicate field: ' . $field['name']);
|
|
}
|
|
$fields[$field['name']] = $field;
|
|
}
|
|
}
|
|
return $fields;
|
|
}
|
|
throw new RuntimeException('Forecast panel missing');
|
|
}
|
|
|
|
$anzeigen = ManagerPrognoseFormular::laufzeitAnzeige();
|
|
check(count($anzeigen) === 4, 'known historical runtime identifiers remain available');
|
|
foreach ($anzeigen as $ident => $name) {
|
|
check(str_starts_with($ident, 'NetzfahrplanV4'), 'stable existing identifier retained');
|
|
check(!str_contains($name, 'V4'), 'technical version removed from historical display name');
|
|
}
|
|
check(str_contains($anzeigen['NetzfahrplanV4Aktivtest'], 'Testbetrieb'), 'historical test object is not relabelled as production control');
|
|
$module = file_get_contents(__DIR__ . '/../../Manager/module.php');
|
|
check(str_contains($module, 'ManagerPrognoseFormular::laufzeitAnzeige()'), 'runtime display migration is applied');
|
|
check(str_contains($module, 'IPS_SetHidden($objektID, true)'), 'legacy diagnostics are hidden, not deleted');
|
|
check(str_contains($module, "RegisterPropertyBoolean('NetzfahrplanAktiv', false)"), 'legacy controller property remains stored');
|
|
check(str_contains($module, "RegisterPropertyInteger('PrognoseSendeintervall'"), 'internal sending interval remains stored');
|
|
$propertySources = $module;
|
|
foreach (glob(__DIR__ . '/../../libs/ManagerNetzfahrplanV4*Trait.php') ?: [] as $path) {
|
|
$propertySources .= file_get_contents($path);
|
|
}
|
|
foreach (['NetzfahrplanV4EmpfangAktiv', 'NetzfahrplanV4SchattenAktiv', 'NetzfahrplanV4MessdatenAktiv',
|
|
'NetzfahrplanV4RegeltestErlaubt', 'NetzfahrplanV4AktivtestErlaubt', 'NetzfahrplanV4WatchdogVerzichtErlaubt'] as $property) {
|
|
check(preg_match("/RegisterPropertyBoolean\\(\\s*'" . $property . "'/", $propertySources) === 1,
|
|
'hidden technical property remains registered: ' . $property);
|
|
}
|
|
|
|
$base = json_decode(file_get_contents(__DIR__ . '/../../Manager/form.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
$original = $base;
|
|
$state = ['prognoseAktiv' => true, 'lizenziert' => true, 'ready' => true,
|
|
'controlRequested' => false, 'controlActive' => false, 'state' => 'ready', 'reason' => 'Lokale Pruefungen bestanden.'];
|
|
$form = ManagerPrognoseFormular::integrieren($base, $state);
|
|
$f = fields($form);
|
|
check(count($form['elements']) === count($base['elements']), 'no additional panel');
|
|
$switches = array_keys(array_filter($f, static fn(array $field): bool => $field['type'] === 'CheckBox'));
|
|
check($switches === ['PrognoseAktiv', 'NetzfahrplanRegelungAktiv'], 'exactly learning and control switches are exposed');
|
|
check(str_contains($f['PrognoseAktiv']['onChange'], "'FormPrognoseAktiv'"), 'learning switch refreshes existing form guard');
|
|
check($f['NetzfahrplanRegelungAktiv']['enabled'] === true, 'ready licensed learning permits control request');
|
|
check(!isset($f['NetzfahrplanRegelungAktiv']['onChange']), 'control is a saved property, not a direct actuator action');
|
|
foreach (['NetzfahrplanAktiv', 'PrognoseSendeintervall', 'NetzfahrplanV4EmpfangAktiv',
|
|
'NetzfahrplanV4SchattenAktiv', 'NetzfahrplanV4MessdatenAktiv', 'NetzfahrplanV4RegeltestErlaubt',
|
|
'NetzfahrplanV4AktivtestErlaubt', 'NetzfahrplanV4WatchdogVerzichtErlaubt',
|
|
'PrognoseNetzfahrplanStart', 'PrognoseNetzfahrplanStop', 'PrognoseNetzfahrplanPruefen'] as $name) {
|
|
check(!isset($f[$name]), 'legacy or technical operator field removed: ' . $name);
|
|
}
|
|
$buttons = array_filter($f, static fn(array $field): bool => $field['type'] === 'Button');
|
|
check(array_keys($buttons) === ['PrognoseLizenzVerwalten'], 'only the portal link remains as a button');
|
|
check($buttons['PrognoseLizenzVerwalten']['link'] === true, 'portal button is a link');
|
|
check(str_contains($buttons['PrognoseLizenzVerwalten']['onClick'], 'https://license.enelix.ch'), 'existing license portal retained');
|
|
check(!str_contains($buttons['PrognoseLizenzVerwalten']['onClick'], 'IPS_RequestAction'), 'portal link cannot dispatch a command');
|
|
check($f['PrognoseNetzfahrplanStatus']['caption'] === 'Bereit: Lokale Pruefungen bestanden.', 'status displays current readiness and reason');
|
|
foreach ($base['elements'] as $index => $element) {
|
|
if (($element['name'] ?? '') !== 'PrognoseForecast') {
|
|
check($form['elements'][$index] === $element, 'unrelated panel unchanged: ' . ($element['caption'] ?? $index));
|
|
}
|
|
}
|
|
|
|
foreach ([false, true] as $learning) {
|
|
foreach ([false, true] as $licensed) {
|
|
foreach ([false, true] as $ready) {
|
|
$changed = array_replace($state, ['prognoseAktiv' => $learning, 'lizenziert' => $licensed, 'ready' => $ready]);
|
|
$expected = $learning && $licensed && $ready;
|
|
check(ManagerPrognoseFormular::startErlaubt($changed) === $expected, 'all three start guards are required');
|
|
check(fields(ManagerPrognoseFormular::integrieren($base, $changed))['NetzfahrplanRegelungAktiv']['enabled'] === $expected,
|
|
'inactive control request follows start guard');
|
|
$changed['controlRequested'] = true;
|
|
check(fields(ManagerPrognoseFormular::integrieren($base, $changed))['NetzfahrplanRegelungAktiv']['enabled'] === true,
|
|
'requested control can be switched off despite any failed guard');
|
|
}
|
|
}
|
|
}
|
|
foreach (['prognoseAktiv', 'lizenziert', 'ready'] as $key) {
|
|
foreach ([null, 0, 1, 'true', 'false', []] as $invalid) {
|
|
check(!ManagerPrognoseFormular::startErlaubt(array_replace($state, [$key => $invalid])), 'nonboolean guard fails closed: ' . $key);
|
|
}
|
|
$missing = $state;
|
|
unset($missing[$key]);
|
|
check(!ManagerPrognoseFormular::startErlaubt($missing), 'missing guard fails closed: ' . $key);
|
|
}
|
|
check(!ManagerPrognoseFormular::startErlaubt([]), 'missing state fails closed');
|
|
foreach (['disabled' => 'Aus', 'collecting' => 'Daten sammeln', 'learning' => 'Lernen',
|
|
'ready' => 'Bereit', 'active' => 'Aktiv', 'interrupted' => 'Unterbrochen'] as $key => $label) {
|
|
check(ManagerPrognoseFormular::statustext(['state' => $key, 'reason' => 'Pruefgrund']) === $label . ': Pruefgrund',
|
|
'workflow state and reason visible: ' . $key);
|
|
}
|
|
check(str_starts_with(ManagerPrognoseFormular::statustext(['state' => 'future-state']), 'Status unbekannt:'), 'unknown workflow status is not reported as ready');
|
|
check(str_contains(ManagerPrognoseFormular::statustext([]), 'Aktuellen Status abwarten.'), 'missing reason has a waiting state');
|
|
|
|
$missing = $base;
|
|
$missing['elements'] = array_values(array_filter($missing['elements'], static fn(array $e): bool => ($e['name'] ?? '') !== 'PrognoseForecast'));
|
|
try {
|
|
ManagerPrognoseFormular::integrieren($missing, $state);
|
|
check(false, 'missing forecast anchor rejected');
|
|
} catch (LogicException $e) {
|
|
check(true, 'missing forecast anchor rejected');
|
|
}
|
|
$duplicate = $base;
|
|
foreach ($base['elements'] as $element) {
|
|
if (($element['name'] ?? '') === 'PrognoseForecast') {
|
|
$duplicate['elements'][] = $element;
|
|
}
|
|
}
|
|
try {
|
|
ManagerPrognoseFormular::integrieren($duplicate, $state);
|
|
check(false, 'duplicate forecast anchor rejected');
|
|
} catch (LogicException $e) {
|
|
check(true, 'duplicate forecast anchor rejected');
|
|
}
|
|
check($form === ManagerPrognoseFormular::integrieren($base, $state), 'repeated rendering is deterministic');
|
|
check($base === $original, 'base form and historical configuration are not mutated');
|
|
echo "Total: $count checks. Form generation only; no IPS or device calls.\n";
|