diff --git a/test/tests/Util/Redactor/ProjectZomboidRedactorIntegrationTest.php b/test/tests/Util/Redactor/ProjectZomboidRedactorIntegrationTest.php index be54778..6b94d02 100644 --- a/test/tests/Util/Redactor/ProjectZomboidRedactorIntegrationTest.php +++ b/test/tests/Util/Redactor/ProjectZomboidRedactorIntegrationTest.php @@ -69,6 +69,38 @@ class ProjectZomboidRedactorIntegrationTest extends TestCase ]; } + /** + * Yields [fixturePath] for the subset of fixtures where every synthetic + * player name (Player1 / Player2 / AdminUser / PlayerSuspect) appears + * exclusively in a context the redactor recognises: + * + * - chat: ChatMessage{author='...'} envelope + * - cmd, item, map, user: 77-char-Steam-ID followed by "..." quoted name + * + * Fixtures intentionally excluded: + * + * - admin: names appear in free-text positions (no Steam-ID anchor, + * no quotes, no Combat:/Safety: prefix). Names survive in v1. + * - client-action, + * perk: names appear inside [...] brackets, not "..." quotes. + * PLAYER_AFTER_STEAMID_REGEX requires double-quotes. + * - pvp: attacker name redacts but victim name after `hit "..."` + * survives in v1 (Task 3 limitation). + * - burd-journals, + * debug-server: no synthetic player names present. + */ + public static function fixturesWhereAllNamesAreInCoveredContextsProvider(): array + { + $dir = self::$fixturesDir; + return [ + 'chat' => [$dir . '/chat-minimal.txt'], + 'cmd' => [$dir . '/cmd-minimal.txt'], + 'item' => [$dir . '/item-minimal.txt'], + 'map' => [$dir . '/map-minimal.txt'], + 'user' => [$dir . '/user-minimal.txt'], + ]; + } + /** * Yields [fixturePath, logClass] for the fixtures whose log class parses * them. All 11 fixtures are represented. @@ -202,4 +234,39 @@ class ProjectZomboidRedactorIntegrationTest extends TestCase ), ); } + + // --------------------------------------------------------------------------- + // Test 4 — Player-name collapse in fully-covered fixtures + // --------------------------------------------------------------------------- + + /** + * For fixtures where every synthetic player name appears exclusively in a + * context the redactor recognises, no synthetic name should remain after + * redaction. + * + * This addresses observation #3 from the final code review (the integration + * tests previously asserted Steam-ID elimination + structural preservation + * + idempotence, but did not directly verify name collapse). The unit tests + * in ProjectZomboidRedactorPlayerNameTest cover this property exhaustively + * per-context; this integration test re-verifies it end-to-end against the + * fixtures that ride into iblogs. + */ + #[DataProvider('fixturesWhereAllNamesAreInCoveredContextsProvider')] + public function testFixturePlayerNamesCollapseInCoveredContexts(string $fixturePath): void + { + $content = (new PathLogFile($fixturePath))->getContent(); + $redacted = $this->redact($content); + + foreach (['Player1', 'Player2', 'AdminUser', 'PlayerSuspect'] as $name) { + $this->assertStringNotContainsString( + $name, + $redacted, + sprintf( + 'Fixture "%s": synthetic name %s survived redaction. Every name in this fixture should appear only in a covered lexical context.', + basename($fixturePath), + $name, + ), + ); + } + } }