rolledBack); self::assertSame($cause, $failure->getPrevious()); self::assertStringContainsString('meta kaputt', $failure->getMessage()); self::assertToolError($failure->toError(), 'write_failed'); } public function testRolledBackHintReplacesTheDefaultHintOfWriteFailedOnly(): void { $clean = WriteFailed::rolledBack('x', hint: 'Erst neu lesen.'); $partial = WriteFailed::partial('x'); self::assertSame('Erst neu lesen.', $clean->toError()->get_error_data()['hint']); self::assertNotSame('Erst neu lesen.', $partial->toError()->get_error_data()['hint']); self::assertStringStartsWith('Den Aufruf nicht einfach wiederholen.', $partial->toError()->get_error_data()['hint']); } public function testIncompleteRollbackMapsToPartialWriteWithTheIds(): void { $error = WriteFailed::partial('Felder konnten nicht gespeichert werden.', postId: 5, revisionId: 811)->toError(); self::assertToolError($error, 'partial_write'); self::assertSame(5, $error->get_error_data()['post_id']); self::assertSame(811, $error->get_error_data()['revision_id']); } public function testIncompleteRollbackCanDescribeItsOwnStateHintAndDetails(): void { $error = WriteFailed::partial( 'Menü nicht gespeichert.', state: 'Das Menü ist teilweise geändert.', hint: 'Mit lit/menu-get prüfen.', details: ['menu_id' => 3] )->toError(); self::assertToolError($error, 'partial_write'); self::assertStringContainsString('Das Menü ist teilweise geändert.', $error->get_error_message()); self::assertStringNotContainsString('Entwurf', $error->get_error_message()); self::assertSame('Mit lit/menu-get prüfen.', $error->get_error_data()['hint']); self::assertSame(3, $error->get_error_data()['menu_id']); } public function testRolledBackStateReplacesNothingWasSavedOfWriteFailedOnly(): void { $state = 'Die Änderung wurde nicht gespeichert (Sicherung 88 bleibt erhalten).'; $clean = WriteFailed::rolledBack('x', state: $state)->toError(); $partial = WriteFailed::partial('x')->toError(); self::assertStringContainsString($state, $clean->get_error_message()); self::assertStringNotContainsString('Nichts wurde gespeichert', $clean->get_error_message()); self::assertStringNotContainsString($state, $partial->get_error_message()); } public function testCleanRollbackKeepsItsDetails(): void { $error = WriteFailed::rolledBack('Nicht übernommen.', details: ['backup_id' => 88])->toError(); self::assertToolError($error, 'write_failed'); self::assertSame(88, $error->get_error_data()['backup_id']); self::assertStringContainsString('"backup_id":88', $error->get_error_message()); } public function testTheLogMessageIsTheDetailOrTheCauseOrTheReasonWithTheRollbackOutcome(): void { $cause = new RuntimeException('meta kaputt'); self::assertSame('Detail [rolled back]', WriteFailed::rolledBack('x', $cause, 'Detail')->getMessage()); self::assertSame('meta kaputt [rollback incomplete]', WriteFailed::partial('x', $cause)->getMessage()); self::assertSame('Grund [rolled back]', WriteFailed::rolledBack('Grund')->getMessage()); } public function testTheNamedConstructorsKeepTheIdsForCallersThatLogThem(): void { $clean = WriteFailed::rolledBack('x', postId: 7, revisionId: 811); $partial = WriteFailed::partial('x', postId: 7, revisionId: 811); self::assertTrue($clean->rolledBack); self::assertSame([7, 811], [$clean->postId, $clean->revisionId]); self::assertFalse($partial->rolledBack); self::assertSame([7, 811], [$partial->postId, $partial->revisionId]); self::assertArrayNotHasKey('post_id', $clean->toError()->get_error_data()); } }