Files
Enelix-EMS/tests/ManagerModulstrukturTest.php
T
2026-09-27 16:39:15 +00:00

324 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS\Tests;
use PHPUnit\Framework\TestCase;
final class ManagerModulstrukturTest extends TestCase
{
public function testManagerIstAlsSymconModulVollstaendig(): void
{
foreach (['module.json', 'form.json', 'module.php'] as $datei) {
self::assertFileExists(__DIR__ . '/../Manager/' . $datei);
}
$metadaten = json_decode(
(string) file_get_contents(__DIR__ . '/../Manager/module.json'),
true,
512,
JSON_THROW_ON_ERROR
);
self::assertSame('Manager', $metadaten['name']);
self::assertSame('Enelix', $metadaten['vendor']);
self::assertSame(['Manager'], $metadaten['aliases']);
self::assertSame(3, $metadaten['type']);
self::assertMatchesRegularExpression('/^\{[0-9A-F-]{36}\}$/', $metadaten['id']);
}
public function testFormularEnthaeltDieZentralenManagerProperties(): void
{
$formular = json_decode(
(string) file_get_contents(__DIR__ . '/../Manager/form.json'),
true,
512,
JSON_THROW_ON_ERROR
);
$json = json_encode($formular, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
foreach ([
'NetzleistungVariableID',
'VerbraucherZuordnung',
'AutomatischeVerbraucherZuordnung',
'Lastspitzenmodus',
'Monatsgrenzen',
'SollwertSolarladen',
'KeepAlive',
'VerbraucherTimeout',
'MesswertMaxAlter',
'Umschaltdifferenz',
'EinspeisebegrenzungAktiv',
'Einspeisegrenze',
'Einspeisetoleranz',
'NetzfahrplanAktiv',
'IstleistungVariableID',
'BegrenzungVariableID',
'Begrenzungsart',
'DiagnosevariablenAnzeigen',
'LoggingEin',
'Lizenzcode',
'LizenzPruefen',
'LizenzVerwalten',
'LizenzInstallationID',
'LizenzInformation',
'VerbraucherAktualisieren',
'AnlagenWechselrichter',
'AnlagenPVFlaechen',
'AnlagenBatterien',
'PrognosePVVariableID',
'PrognoseHausverbrauchVariableID',
'PrognoseSOCVariableID',
'PrognoseSendeintervall',
'StoerueberwachungAktiv',
'StoerungsSendeintervall',
] as $property) {
self::assertStringContainsString($property, $json);
}
foreach (['Regelintervall', 'Vorgabeintervall', 'UmschaltschwelleUeberschuss',
'UmschaltschwelleLastspitze', 'Optionale obere Anschluesse', 'LizenzAnschluss'] as $entfernt
) {
self::assertStringNotContainsString($entfernt, $json);
}
foreach (['Grundeinstellungen', 'Erweiterte Einstellungen', 'Prognose / Forecast',
'SDL / VGT', 'Lizenzierung', 'Stoerueberwachung'] as $bereich
) {
self::assertStringContainsString($bereich, $json);
}
self::assertSame('Lizenzierung', $formular['elements'][0]['caption']);
self::assertStringContainsString('https://license.enelix.ch', $json);
self::assertStringContainsString('"link":true', $json);
self::assertStringContainsString(
"'SuchbereichID' => \$SuchbereichID",
$json
);
self::assertStringContainsString(
"'Zuordnung' => \$AutomatischeVerbraucherZuordnung",
$json
);
}
public function testManagerTrenntDiagnosevariablenUndDebugLogging(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString(
"RegisterPropertyBoolean('DiagnosevariablenAnzeigen', false)",
$inhalt
);
self::assertStringContainsString("ReadPropertyBoolean('DiagnosevariablenAnzeigen')", $inhalt);
self::assertStringContainsString('UnregisterVariable($ident)', $inhalt);
self::assertStringContainsString("ReadPropertyBoolean('LoggingEin')", $inhalt);
self::assertStringContainsString('SendDebug(', $inhalt);
}
public function testManagerBindetDieLizenzierungFailClosedAn(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertFileExists(__DIR__ . '/../libs/Lizenzpruefung.php');
self::assertStringContainsString(
"RegisterPropertyString('Lizenzcode', '')",
$inhalt
);
self::assertStringContainsString(
"https://license.enelix.ch/api/v1/licenses/activate",
$inhalt
);
self::assertStringContainsString("RegisterAttributeString('LizenzInstallationID'", $inhalt);
self::assertSame(
1,
preg_match('/public function Create\(\): void.*?public function ApplyChanges\(\): void/s', $inhalt, $treffer)
);
self::assertStringNotContainsString(
"WriteAttributeString('LizenzInstallationID'",
$treffer[0]
);
self::assertStringContainsString(
"parent::ApplyChanges();\n\n \$this->initialisiereLizenzInstallationID();",
$inhalt
);
self::assertSame(1, substr_count(
$inhalt,
"WriteAttributeString('LizenzInstallationID'"
));
self::assertStringContainsString("RegisterAttributeString('LizenzLease'", $inhalt);
self::assertStringContainsString("RegisterAttributeString('LizenzcodeHash'", $inhalt);
self::assertStringContainsString('STATUS_LIZENZ_UNGUELTIG', $inhalt);
self::assertStringContainsString('aktualisiereLizenzfreigabe(', $inhalt);
self::assertStringContainsString('pruefeVerbraucherlizenzen(', $inhalt);
self::assertStringContainsString('lizenzBerechtigungenPassen(', $inhalt);
self::assertStringContainsString('peakShavingLizenziert(', $inhalt);
self::assertStringContainsString('Aus (Peak-Shaving-Lizenz fehlt)', $inhalt);
self::assertStringContainsString('Aus (Peak-Shaving-Lizenz verfuegbar)', $inhalt);
self::assertStringContainsString(
'Konstant und Monatlich benoetigen eine Peak-Shaving-Lizenz.',
$inhalt
);
foreach (['battery', 'buffer_storage', 'ev_charger', 'consumer_single', 'boiler_multi'] as $katalog) {
self::assertStringContainsString("'Katalog' => '" . $katalog . "'", $inhalt);
}
foreach ([
'{B7C54AF4-AD7D-4FE4-B75D-203693906251}',
'{15879A4E-D0C2-4495-83DE-46E1E462591E}',
'{0D94913C-0F31-4C29-A685-6EB4AE58E55D}',
'{32F65988-BE55-4FD6-9FFF-CF1C4C8628F8}',
'{C92D5EEF-9632-47A5-9659-4B02BF40FBE9}',
'{437FB683-517F-4FEC-8CCB-FE6B0A62B69E}',
] as $modulID) {
self::assertStringContainsString("'" . $modulID . "' =>", $inhalt);
}
self::assertStringContainsString("'Name' => 'Pufferspeicher'", $inhalt);
self::assertStringContainsString("'Name' => 'Batteriespeicher'", $inhalt);
self::assertStringContainsString(
'Bitte Auswahl reduzieren oder Lizenz erweitern.',
$inhalt
);
foreach ([
"'NichtGeregelt'",
"'NichtGeregelteGeraete'",
"'InstanzStatus'",
"'LetzteMeldung'",
"'GemeldeteBetriebsart'",
"'Verfuegbar'",
] as $detailfeld) {
self::assertStringContainsString($detailfeld, $inhalt);
}
self::assertStringNotContainsString("'publicKey' =>", $inhalt);
}
public function testManagerSynchronisiertDieTopologieMitSeparatemGeraetezugang(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString(
'https://license.enelix.ch/api/v1/installations/%s/prognosis/topology',
$inhalt
);
self::assertStringContainsString(
"RegisterAttributeString('PrognoseInstallationsToken', '')",
$inhalt
);
self::assertStringContainsString('JSON_PRESERVE_ZERO_FRACTION', $inhalt);
self::assertStringContainsString("'issueDeviceToken' =>", $inhalt);
self::assertStringContainsString("unset(\$neueLease['deviceToken'])", $inhalt);
self::assertStringContainsString(
"|| \$this->ReadPropertyBoolean('StoerueberwachungAktiv'))",
$inhalt
);
self::assertStringContainsString(
"\$this->ReadPropertyString('Lizenzcode'),\n \$geraetezugangFehlt",
$inhalt
);
self::assertStringContainsString('synchronisiereAnlagentopologie()', $inhalt);
self::assertStringContainsString(
'https://license.enelix.ch/api/v1/installations/%s/prognosis/telemetry',
$inhalt
);
self::assertStringContainsString("'PrognoseSenden',", $inhalt);
self::assertStringContainsString("'capturedAt' => gmdate(", $inhalt);
foreach (["'PV'", "'Hausverbrauch'", "'Netzleistung'", "'SOC'"] as $messwert) {
self::assertStringContainsString($messwert, $inhalt);
}
self::assertStringContainsString("'Authorization: Bearer ' . \$token", $inhalt);
self::assertStringNotContainsString(
"protokolliere('Prognosesynchronisation', \$token)",
$inhalt
);
$leasePruefung = strpos($inhalt, 'Lizenzpruefung::pruefeLease(');
$tokenSpeicherung = strpos(
$inhalt,
"WriteAttributeString('PrognoseInstallationsToken', \$geraeteToken)"
);
self::assertNotFalse($leasePruefung);
self::assertNotFalse($tokenSpeicherung);
self::assertTrue(
$leasePruefung < $tokenSpeicherung,
'Der Gerätezugang darf erst nach validierter Lizenzantwort gespeichert werden.'
);
}
public function testManagerUebertraegtStoerungsvollsnapshotsMitRetry(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertFileExists(__DIR__ . '/../libs/StoerungsSnapshot.php');
self::assertStringContainsString(
'https://license.enelix.ch/api/v1/installations/%s/faults',
$inhalt
);
foreach ([
"RegisterPropertyBoolean('StoerueberwachungAktiv', false)",
"RegisterPropertyInteger('StoerungsSendeintervall', 300)",
"RegisterAttributeString('StoerungsSnapshot', '{}')",
"RegisterAttributeString('StoerungsGesendeterHash', '')",
"RegisterAttributeInteger('StoerungsNaechsterVersuch', 0)",
"'StoerungenSenden',",
'StoerungsSnapshot::erstellen(',
'StoerungsSnapshot::zustandHash(',
"CURLOPT_CUSTOMREQUEST => 'PUT'",
] as $erwartet) {
self::assertStringContainsString($erwartet, $inhalt);
}
self::assertStringNotContainsString(
'"name":"StoermeldeAnschluss"',
str_replace(' ', '', (string) file_get_contents(__DIR__ . '/../Manager/form.json'))
);
self::assertGreaterThanOrEqual(
3,
substr_count(
$inhalt,
"|| \$this->ReadPropertyBoolean('StoerueberwachungAktiv'))"
),
'Der Gerätezugang muss beim Anwenden, Erneuern und Anfordern berücksichtigt werden.'
);
}
public function testManagerRegeltDieAnlagenweiteEinspeisegrenzeUndDenNetzfahrplan(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertFileExists(__DIR__ . '/../libs/EinspeiseRegler.php');
self::assertStringContainsString('EinspeiseRegler::verteile(', $inhalt);
self::assertStringContainsString('EinspeiseRegler::stellwert(', $inhalt);
self::assertStringContainsString("RegisterAttributeString('LetztePvGrenzen'", $inhalt);
self::assertStringContainsString("RegisterAttributeString('Netzfahrplan'", $inhalt);
self::assertStringContainsString(
'https://license.enelix.ch/api/v1/installations/%s/prognosis/schedule',
$inhalt
);
self::assertStringContainsString('Lizenzpruefung::NETZFAHRPLAN', $inhalt);
self::assertStringContainsString('aktuelleNetzfahrplanVorgabe()', $inhalt);
self::assertStringContainsString('gibEinspeisebegrenzungFrei()', $inhalt);
}
public function testManagerVerwendetDieNeuenZeitdefaults(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString("RegisterPropertyInteger('KeepAlive', 60)", $inhalt);
self::assertStringContainsString("RegisterPropertyInteger('VerbraucherTimeout', 60)", $inhalt);
self::assertStringContainsString("RegisterPropertyInteger('MesswertMaxAlter', 60)", $inhalt);
self::assertStringContainsString("RegisterPropertyFloat('Umschaltdifferenz', 5.0)", $inhalt);
self::assertStringNotContainsString("RegisterPropertyInteger('Regelintervall'", $inhalt);
self::assertStringNotContainsString("RegisterPropertyInteger('Vorgabeintervall'", $inhalt);
$verbraucherBasis = (string) file_get_contents(__DIR__ . '/../libs/VerbraucherBasisTrait.php');
self::assertStringContainsString(
"RegisterPropertyInteger('VorgabeTimeout', 120)",
$verbraucherBasis
);
}
public function testManagerImplementiertDenGemeinsamenEmpfangsvertrag(): void
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString('implements ManagerSchnittstelle', $inhalt);
self::assertStringContainsString("require_once __DIR__ . '/../libs/Anlagentopologie.php'", $inhalt);
self::assertStringContainsString('public function AnlagentopologieExportieren(): string', $inhalt);
self::assertStringContainsString('VerbraucherdatenEmpfangen(array $daten)', $inhalt);
self::assertStringContainsString("'ManagerdatenEmpfangen'", $inhalt);
}
}