Tests / test (push) Failing after 49s
Merge the V4 development history with current manager, SDL and setpoint fixes. Retain guarded trial behavior and consolidate controls in Prognose / Forecast. Exclude the unverified accounting-evidence change; no runtime deployment or new dispatch permission. Validated: 357 PHPUnit tests / 1758 assertions, 150 PHP syntax checks, 310 backend tests, 16 portal tests and isolated UI/receiver checks. develop and beta publication explicitly approved by Daniel Haefliger.
416 lines
18 KiB
PHP
416 lines
18 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',
|
|
'PrognoseAktiv',
|
|
'SDLAktiv',
|
|
'SDLLeistungVariableID',
|
|
'SDLLeistungsfaktor',
|
|
'SDLSOCVariableID',
|
|
'SDLEnergieflussAnzeigen',
|
|
'SDLDiagrammeAnzeigen',
|
|
'PrognoseLizenzInformation',
|
|
'NetzfahrplanAktiv',
|
|
'NetzbezugEnergieVariableID',
|
|
'NetzeinspeisungEnergieVariableID',
|
|
'IstleistungVariableID',
|
|
'ErzeugungsenergieVariableID',
|
|
'Erzeugungsenergiefaktor',
|
|
'BegrenzungVariableID',
|
|
'Begrenzungsart',
|
|
'DiagnosevariablenAnzeigen',
|
|
'LoggingEin',
|
|
'Lizenzcode',
|
|
'LizenzPruefen',
|
|
'LizenzVerwalten',
|
|
'LizenzInstallationID',
|
|
'LizenzInformation',
|
|
'VerbraucherAktualisieren',
|
|
'AnlagenWechselrichter',
|
|
'AnlagenPVFlaechen',
|
|
'AnlagenBatterien',
|
|
'Istleistungsfaktor',
|
|
'LeistungVariableID',
|
|
'Leistungsfaktor',
|
|
'SOCVariableID',
|
|
'LadeenergieVariableID',
|
|
'EntladeenergieVariableID',
|
|
'Energiefaktor',
|
|
'EnergyPieAnzeigen',
|
|
'EnergiediagrammeAnzeigen',
|
|
'FunFactsAnzeigen',
|
|
'EnergieflussAnzeigen',
|
|
'PrognoseSendeintervall',
|
|
'StoerueberwachungAktiv',
|
|
'StoerungsSendeintervall',
|
|
] as $property) {
|
|
self::assertStringContainsString($property, $json);
|
|
}
|
|
foreach (['Regelintervall', 'Vorgabeintervall', 'UmschaltschwelleUeberschuss',
|
|
'UmschaltschwelleLastspitze', 'Optionale obere Anschluesse', 'LizenzAnschluss',
|
|
'Einspeisetoleranz', 'ACEinspeisegrenze_kW', 'ACBezugsgrenze_kW'] 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::assertSame(1, preg_match(
|
|
'/public function ApplyChanges\(\): void(.*?)public function /s',
|
|
$inhalt,
|
|
$applyChanges
|
|
));
|
|
self::assertMatchesRegularExpression(
|
|
'/parent::ApplyChanges\(\);.*?\$this->initialisiereLizenzInstallationID\(\);/s',
|
|
$applyChanges[1]
|
|
);
|
|
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', 'heat_pump', 'boiler_multi'] as $katalog) {
|
|
self::assertStringContainsString("'Katalog' => '" . $katalog . "'", $inhalt);
|
|
}
|
|
foreach ([
|
|
'{B7C54AF4-AD7D-4FE4-B75D-203693906251}',
|
|
'{15879A4E-D0C2-4495-83DE-46E1E462591E}',
|
|
'{A31C9274-54F7-4BD2-9804-7AF23E86C4D1}',
|
|
'{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::assertGreaterThanOrEqual(
|
|
3,
|
|
substr_count($inhalt, '$this->geraetezugangBenoetigt()'),
|
|
'Der Geraetezugang muss beim Anwenden, Erneuern und Anfordern beruecksichtigt werden.'
|
|
);
|
|
self::assertStringContainsString(
|
|
"\$this->ReadPropertyString('Lizenzcode'),\n \$geraetezugangFehlt",
|
|
$inhalt
|
|
);
|
|
self::assertStringContainsString('synchronisiereAnlagentopologie()', $inhalt);
|
|
self::assertStringContainsString('private function geraetezugangBenoetigt(): bool', $inhalt);
|
|
self::assertStringContainsString(
|
|
"if (\$this->aktualisiereLizenzfreigabe(\$wert, true)) {",
|
|
$inhalt
|
|
);
|
|
self::assertStringNotContainsString(
|
|
"if (!\$this->ReadPropertyBoolean('PrognoseAktiv')) {\n \$this->setzeDiagnosewert('Prognosestatus', 'NichtVerwendet');\n return;\n }\n\n try {",
|
|
$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 testManagerRegeltAktuelleVerbraucherTrotzFehlenderRueckmeldungen(): void
|
|
{
|
|
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
|
|
|
|
self::assertStringNotContainsString("if (!\$synchronisiert) {", $inhalt);
|
|
self::assertStringContainsString(
|
|
"'Synchronisiert' => \$synchronisiert",
|
|
$inhalt
|
|
);
|
|
self::assertStringContainsString(
|
|
"'NichtGeregelt' => \$nichtGeregelt",
|
|
$inhalt
|
|
);
|
|
self::assertStringContainsString(
|
|
"\$ergebnis = ManagerRegler::verteile(",
|
|
$inhalt
|
|
);
|
|
}
|
|
|
|
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::assertStringContainsString(
|
|
"if (\$token === '') {\n"
|
|
. " \$this->aktualisiereLizenzfreigabe(\n"
|
|
. " \$this->ReadPropertyString('Lizenzcode'),\n"
|
|
. " true",
|
|
$inhalt
|
|
);
|
|
self::assertGreaterThanOrEqual(
|
|
3,
|
|
substr_count($inhalt, '$this->geraetezugangBenoetigt()'),
|
|
'Der Geraetezugang muss beim Anwenden, Erneuern und Anfordern beruecksichtigt 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('Lizenzpruefung::PROGNOSE_PV', $inhalt);
|
|
self::assertStringContainsString('Lizenzpruefung::PROGNOSE_VERBRAUCH', $inhalt);
|
|
self::assertStringContainsString('prognoseLizenzStatustext(', $inhalt);
|
|
self::assertStringContainsString(
|
|
"foreach (['PeakShavingTitel', 'EinspeisebegrenzungAktiv', 'Einspeisegrenze']",
|
|
$inhalt
|
|
);
|
|
self::assertStringContainsString('aktuelleNetzfahrplanVorgabe()', $inhalt);
|
|
self::assertStringContainsString('gibEinspeisebegrenzungFrei()', $inhalt);
|
|
self::assertStringNotContainsString("RegisterPropertyFloat('Einspeisetoleranz'", $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);
|
|
}
|
|
|
|
public function testVerbrauchermeldungPlantRegelungOhneSynchronenRueckruf(): void
|
|
{
|
|
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
|
|
self::assertStringContainsString(
|
|
"RegisterTimer(\n 'RegelungVerzoegert'",
|
|
$inhalt
|
|
);
|
|
self::assertStringContainsString("case 'RegelnVerzoegert':", $inhalt);
|
|
self::assertSame(1, preg_match(
|
|
'/public function VerbraucherdatenEmpfangen\(array \$daten\): void(?<block>.*?)'
|
|
. 'private function aktualisiereVerbraucherEnergie/s',
|
|
$inhalt,
|
|
$treffer
|
|
));
|
|
self::assertStringContainsString(
|
|
"SetTimerInterval('RegelungVerzoegert', 100)",
|
|
$treffer['block']
|
|
);
|
|
self::assertStringNotContainsString('$this->regeln(false);', $treffer['block']);
|
|
}
|
|
}
|