lease('manager_standard'), self::INSTALLATION_ID, 0, $this->zeit('2026-09-19T12:00:00Z') ); self::assertTrue(true); } public function testPeakLizenzGibtLastspitzenmodusFrei(): void { Lizenzpruefung::pruefeLease( $this->lease('manager_peak'), self::INSTALLATION_ID, 2, $this->zeit('2026-09-19T12:00:00Z') ); self::assertTrue(true); } public function testStandardLizenzSperrtLastspitzenmodus(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Peak Shaving ist nicht lizenziert.'); Lizenzpruefung::pruefeLease( $this->lease('manager_standard'), self::INSTALLATION_ID, 1, $this->zeit('2026-09-19T12:00:00Z') ); } public function testAbgelaufeneOfflineFreigabeWirdAbgelehnt(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Offline-Freigabe ist abgelaufen.'); Lizenzpruefung::pruefeLease( $this->lease('manager_peak'), self::INSTALLATION_ID, 0, $this->zeit('2026-10-03T12:00:00Z') ); } public function testAndereInstallationWirdAbgelehnt(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('anderen Installation'); Lizenzpruefung::pruefeLease( $this->lease('manager_standard'), '223e4567-e89b-42d3-a456-426614174000', 0, $this->zeit('2026-09-19T12:00:00Z') ); } public function testErneuerungWirdAbRefreshAfterFaellig(): void { $lease = $this->lease('manager_standard'); self::assertFalse(Lizenzpruefung::erneuerungFaellig( $lease, $this->zeit('2026-09-20T11:59:59Z') )); self::assertTrue(Lizenzpruefung::erneuerungFaellig( $lease, $this->zeit('2026-09-20T12:00:00Z') )); } public function testLeaseOhneManagerBerechtigungWirdAbgelehnt(): void { $lease = $this->lease('consumer_single'); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Manager-Berechtigung fehlt.'); Lizenzpruefung::pruefeLease( $lease, self::INSTALLATION_ID, 0, $this->zeit('2026-09-19T12:00:00Z') ); } public function testZweiLizenzierteBoilerWerdenFreigegeben(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'sku' => 'ENX-BOILER-MULTI', 'catalog_key' => 'boiler_multi', 'quantity' => 2, 'status' => 'active', ]; self::assertSame(2, Lizenzpruefung::berechtigungsMenge($lease, 'boiler_multi')); Lizenzpruefung::pruefeVerbrauchermengen($lease, ['boiler_multi' => 2]); self::assertTrue(true); } public function testDritterBoilerBeiZweiLizenzenWirdAbgelehnt(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'sku' => 'ENX-BOILER-MULTI', 'catalog_key' => 'boiler_multi', 'quantity' => 2, 'status' => 'active', ]; $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( 'Lizenzkontingent fuer boiler_multi ueberschritten: 3 verwendet, 2 lizenziert.' ); Lizenzpruefung::pruefeVerbrauchermengen($lease, ['boiler_multi' => 3]); } public function testVerbraucherkontingenteWerdenJeTypSummiertUndGetrenntGeprueft(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'] = array_merge($lease['entitlements'], [ [ 'catalog_key' => 'boiler_multi', 'quantity' => 1, 'status' => 'active', ], [ 'catalog_key' => 'boiler_multi', 'quantity' => 1, 'status' => 'active', ], [ 'catalog_key' => 'battery', 'quantity' => 1, 'status' => 'active', ], [ 'catalog_key' => 'battery', 'quantity' => 10, 'status' => 'revoked', ], ]); self::assertSame(2, Lizenzpruefung::berechtigungsMenge($lease, 'boiler_multi')); self::assertSame(1, Lizenzpruefung::berechtigungsMenge($lease, 'battery')); Lizenzpruefung::pruefeVerbrauchermengen($lease, [ 'boiler_multi' => 2, 'battery' => 1, ]); self::assertTrue(true); } public function testSeparateWaermepumpenlizenzBleibtUnveraendert(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => 'heat_pump', 'quantity' => 1, 'status' => 'active', ]; $verwendung = ['heat_pump' => 1]; $pruefmengen = Lizenzpruefung::beruecksichtigeBestandslizenzFuerWaermepumpen( $lease, $verwendung ); self::assertSame($verwendung, $pruefmengen); Lizenzpruefung::pruefeVerbrauchermengen($lease, $pruefmengen); } public function testFreieBestandslizenzDecktEineWaermepumpeAb(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => 'consumer_single', 'quantity' => 1, 'status' => 'active', ]; $pruefmengen = Lizenzpruefung::beruecksichtigeBestandslizenzFuerWaermepumpen( $lease, ['heat_pump' => 1] ); self::assertSame(['heat_pump' => 0, 'consumer_single' => 1], $pruefmengen); Lizenzpruefung::pruefeVerbrauchermengen($lease, $pruefmengen); } public function testBestandslizenzDarfNichtDoppeltVerwendetWerden(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => 'consumer_single', 'quantity' => 1, 'status' => 'active', ]; $pruefmengen = Lizenzpruefung::beruecksichtigeBestandslizenzFuerWaermepumpen( $lease, ['consumer_single' => 1, 'heat_pump' => 1] ); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Lizenzkontingent fuer heat_pump ueberschritten'); Lizenzpruefung::pruefeVerbrauchermengen($lease, $pruefmengen); } public function testPvUndVerbrauchGebenPrognoseFrei(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => Lizenzpruefung::PROGNOSE_PV, 'quantity' => 1, 'status' => 'active', ]; $lease['entitlements'][] = [ 'catalog_key' => Lizenzpruefung::PROGNOSE_VERBRAUCH, 'quantity' => 1, 'status' => 'active', ]; self::assertTrue(Lizenzpruefung::hatPrognoseBerechtigung($lease)); } public function testEinzelnePrognoseBerechtigungReichtNichtAus(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => Lizenzpruefung::PROGNOSE_PV, 'quantity' => 1, 'status' => 'active', ]; self::assertFalse(Lizenzpruefung::hatPrognoseBerechtigung($lease)); } public function testNetzfahrplanGibtPrognoseAlleineFrei(): void { $lease = $this->lease('manager_standard'); $lease['entitlements'][] = [ 'catalog_key' => Lizenzpruefung::NETZFAHRPLAN, 'quantity' => 1, 'status' => 'active', ]; self::assertTrue(Lizenzpruefung::hatPrognoseBerechtigung($lease)); } public function testLizenzcodeWirdWieAufDemServerNormalisiert(): void { self::assertSame( 'ENX23456789ABCDEFGH', Lizenzpruefung::normalisiereCode('enx-2345-6789-abcd-efgh') ); self::assertTrue(Lizenzpruefung::istLizenzcodeGueltig('ENX-2345-6789-ABCD-EFGH')); self::assertFalse(Lizenzpruefung::istLizenzcodeGueltig('ENX-OOOO-OOOO-OOOO-OOOO')); } /** * @return array */ private function lease(string $sku): array { return [ 'status' => 'active', 'development' => true, 'installationId' => self::INSTALLATION_ID, 'orderId' => '323e4567-e89b-42d3-a456-426614174000', 'entitlements' => [[ 'sku' => $sku, 'catalog_key' => $sku, 'quantity' => 1, 'status' => 'active', ]], 'issuedAt' => '2026-09-19T12:00:00.000Z', 'refreshAfter' => '2026-09-20T12:00:00.000Z', 'offlineUntil' => '2026-10-03T12:00:00.000Z', ]; } private function zeit(string $wert): int { return (new DateTimeImmutable($wert))->getTimestamp(); } }