From c2cb64e9a701cd0548c0fb9acc6821d3f52da030 Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Fri, 1 May 2026 14:57:08 +0000 Subject: [PATCH] test: add Redactor combined and idempotence coverage Co-Authored-By: Claude Sonnet 4.6 --- .../ProjectZomboidRedactorCombinedTest.php | 146 ++++++++++++++++++ .../ProjectZomboidRedactorIdempotenceTest.php | 99 ++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 test/tests/Util/Redactor/ProjectZomboidRedactorCombinedTest.php create mode 100644 test/tests/Util/Redactor/ProjectZomboidRedactorIdempotenceTest.php diff --git a/test/tests/Util/Redactor/ProjectZomboidRedactorCombinedTest.php b/test/tests/Util/Redactor/ProjectZomboidRedactorCombinedTest.php new file mode 100644 index 0000000..873809c --- /dev/null +++ b/test/tests/Util/Redactor/ProjectZomboidRedactorCombinedTest.php @@ -0,0 +1,146 @@ +" added Base.Aerosolbomb at 0,0,0.', + '[16-04-26 12:00:01.000] 76561198000000000 "" added IsoObject (fence_01) at 0,0,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='', text='hello'}.", + '[16-04-26 17:14:35.128][INFO] Combat: "" (0,0,0) hit "Player2" (1006,2005,0) weapon="Tire Iron (Worn)" damage=0.112317.', + '[16-04-26 16:17:49.731][LOG] Safety: "" (0,0,0) restore true.', + '[16-04-26 12:00:02.000] [76561198000000000][ISEnterVehicle][Player2][0,0,0][Van_LectroMax].', + ]); + + $output = (new ProjectZomboidRedactor())->redact($input); + + $this->assertSame($expected, $output, 'With all three toggles on, every Steam ID, player name context, and coord shape must be replaced.'); + } + + public function testSteamIdToggleOffLeavesSteamIdsIntact(): void + { + // All three PII categories present; Steam ID toggle is disabled. + // + // Important nuance: PLAYER_AFTER_STEAMID_REGEX anchors on the redacted placeholder + // 76561198000000000. With redactSteamIds(false) the raw Steam ID survives, so the + // regex does NOT fire for lines in the "after-Steam-ID" shape — those names survive + // too. Names anchored by other contexts (ChatMessage author, Combat:/Safety:) are + // still redacted because those regexes don't depend on the Steam ID pass. + $input = implode("\n", [ + // after-Steam-ID shape: name will NOT be redacted because the Steam ID is raw + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + // ChatMessage author: still redacted (anchor is independent of Steam ID pass) + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='AdminUser', text='hello'}.", + // Combat: name + attacker coords + '[16-04-26 17:14:35.128][INFO] Combat: "Player2" (1005,2005,0) hit "Player1" (1006,2005,0) weapon="Pipe Bomb" damage=1.0.', + ]); + + $expected = implode("\n", [ + // Steam ID intact; "Player1" NOT redacted (anchor regex didn't fire) + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 0,0,0.', + // ChatMessage name redacted; coords were an at-clause → redacted + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='', text='hello'}.", + // Combat: name + attacker coords both redacted + '[16-04-26 17:14:35.128][INFO] Combat: "" (0,0,0) hit "Player1" (1006,2005,0) weapon="Pipe Bomb" damage=1.0.', + ]); + + $output = (new ProjectZomboidRedactor()) + ->redactSteamIds(false) + ->redact($input); + + $this->assertSame( + $expected, + $output, + 'With Steam ID toggle off: raw Steam IDs survive; PLAYER_AFTER_STEAMID_REGEX does not fire (no placeholder to anchor on) so those names also survive; ChatMessage and Combat:/Safety: names are still redacted; coords are still redacted.', + ); + } + + public function testPlayerNameToggleOffLeavesNamesIntact(): void + { + // Steam IDs and coords redact; player names survive verbatim. + $input = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='Player2', text='bye'}.", + '[16-04-26 16:17:49.731][LOG] Safety: "AdminUser" (1050,2050,0) restore true.', + ]); + + $expected = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198000000000 "Player1" added Base.Aerosolbomb at 0,0,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='Player2', text='bye'}.", + '[16-04-26 16:17:49.731][LOG] Safety: "AdminUser" (0,0,0) restore true.', + ]); + + $output = (new ProjectZomboidRedactor()) + ->redactPlayerNames(false) + ->redact($input); + + $this->assertSame($expected, $output, 'With player-name toggle off, all player names must survive; Steam IDs and coords must still be redacted.'); + } + + public function testCoordinatesToggleOffLeavesCoordsIntact(): void + { + // Steam IDs and player names redact; coordinates survive verbatim. + $input = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + '[16-04-26 12:00:01.000] [76561198222222222][ISEnterVehicle][Player2][1020,2020,0][Van_LectroMax].', + '[16-04-26 17:14:35.128][INFO] Combat: "AdminUser" (1005,2005,0) hit "Player1" (1006,2005,0) weapon="Baseball Bat" damage=0.5.', + ]); + + $expected = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198000000000 "" added Base.Aerosolbomb at 1000,2000,0.', + '[16-04-26 12:00:01.000] [76561198000000000][ISEnterVehicle][Player2][1020,2020,0][Van_LectroMax].', + '[16-04-26 17:14:35.128][INFO] Combat: "" (1005,2005,0) hit "Player1" (1006,2005,0) weapon="Baseball Bat" damage=0.5.', + ]); + + $output = (new ProjectZomboidRedactor()) + ->redactCoordinates(false) + ->redact($input); + + $this->assertSame($expected, $output, 'With coordinates toggle off, all coord triplets must survive; Steam IDs and player names must still be redacted.'); + } + + public function testAllTogglesOffReturnsInputByteForByte(): void + { + // Disabling every toggle must produce an output identical to the input — + // the "passthrough" contract: opt-out means truly nothing happens. + $input = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='Player2', text='hello'}.", + '[16-04-26 17:14:35.128][INFO] Combat: "AdminUser" (1005,2005,0) hit "Player1" (1006,2005,0) weapon="Tire Iron (Worn)" damage=0.112317.', + '[16-04-26 12:00:01.000] [76561198333333333][ISEnterVehicle][Player2][1020,2020,0][Van_LectroMax].', + ]); + + $output = (new ProjectZomboidRedactor()) + ->redactSteamIds(false) + ->redactPlayerNames(false) + ->redactCoordinates(false) + ->redact($input); + + $this->assertSame($input, $output, 'With all three toggles disabled, the output must be byte-for-byte identical to the input.'); + } +} diff --git a/test/tests/Util/Redactor/ProjectZomboidRedactorIdempotenceTest.php b/test/tests/Util/Redactor/ProjectZomboidRedactorIdempotenceTest.php new file mode 100644 index 0000000..b84e32a --- /dev/null +++ b/test/tests/Util/Redactor/ProjectZomboidRedactorIdempotenceTest.php @@ -0,0 +1,99 @@ + do not accidentally re-match and produce a doubly- + * nested result like "" → something else. + */ +class ProjectZomboidRedactorIdempotenceTest extends TestCase +{ + public function testIdempotenceSteamIdOnly(): void + { + $input = implode("\n", [ + 'Players: 76561198111111111, 76561198222222222, 76561198333333333 connected.', + '[16-04-26 12:00:00.000] [76561198111111111][ISEnterVehicle][Player1][1000,2000,0][Van_LectroMax].', + ]); + + $redactor = new ProjectZomboidRedactor(); + $redacted = $redactor->redact($input); + $redactedAgain = $redactor->redact($redacted); + + $this->assertSame($redacted, $redactedAgain, 'Applying redact() twice to Steam-ID-only input must produce the same result as applying it once.'); + } + + public function testIdempotencePlayerNamesOnly(): void + { + // Input already has the Steam ID placeholder in place (as the Steam ID pass + // would have written it), so PLAYER_AFTER_STEAMID_REGEX can fire. After the + // first pass the name becomes ""; the second pass must leave "" + // untouched — it is not a valid display name inside double quotes preceded + // by the Steam ID placeholder anchor in a way that would re-match, because + // the replacement written is: 76561198000000000 "", and the regex + // would need an unquoted player name inside quotes after the placeholder. + // "" (with the angle brackets) does satisfy [^"]+ but the second + // pass must still produce an identical result. + $input = implode("\n", [ + '76561198000000000 "Player1" ISLogSystem.writeLog @ 1000,2000,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='AdminUser', text='hi'}.", + '[16-04-26 16:17:49.731][LOG] Safety: "Player2" (1000,2000,0) restore true.', + ]); + + $redactor = (new ProjectZomboidRedactor())->redactSteamIds(false)->redactCoordinates(false); + $redacted = $redactor->redact($input); + $redactedAgain = $redactor->redact($redacted); + + $this->assertSame($redacted, $redactedAgain, 'Applying redact() twice to player-name-only input must produce the same result as applying it once.'); + } + + public function testIdempotenceCoordsOnly(): void + { + $input = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198000000001 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + '[16-04-26 12:00:01.000] [76561198000000001][ISEnterVehicle][Player1][1020,2020,-1][Van_LectroMax].', + '[16-04-26 17:14:35.128][INFO] Combat: "Player1" (1005,2005,0) hit "Player2" (1006,2005,0) weapon="Tire Iron (Worn)" damage=0.112317.', + '[16-04-26 16:17:49.731][LOG] Safety: "Player1" (1000,2000,0) restore true.', + ]); + + $redactor = (new ProjectZomboidRedactor())->redactSteamIds(false)->redactPlayerNames(false); + $redacted = $redactor->redact($input); + $redactedAgain = $redactor->redact($redacted); + + $this->assertSame($redacted, $redactedAgain, 'Applying redact() twice to coords-only input must produce the same result as applying it once; the placeholder 0,0,0 must not be re-matched.'); + } + + public function testIdempotenceAllCategories(): void + { + // Full input: all three PII categories in multiple lexical contexts. + // After the first redact(), every placeholder is in place. The second + // redact() must make no further changes. + $input = implode("\n", [ + '[16-04-26 12:00:00.000] 76561198111111111 "Player1" added Base.Aerosolbomb at 1000,2000,0.', + '[16-04-26 12:00:01.000] 76561198222222222 "Player2" teleported to 1050,2050,0.', + "[16-04-26 17:05:03.280][info] Got message:ChatMessage{chat=Local, author='AdminUser', text='hello'}.", + '[16-04-26 17:14:35.128][INFO] Combat: "Player1" (1005,2005,0) hit "Player2" (1006,2005,0) weapon="Tire Iron (Worn)" damage=0.112317.', + '[16-04-26 16:17:49.731][LOG] Safety: "Player1" (1000,2000,0) restore true.', + '[16-04-26 12:00:02.000] [76561198333333333][ISEnterVehicle][Player2][1020,2020,0][Van_LectroMax].', + ]); + + $redactor = new ProjectZomboidRedactor(); + $redacted = $redactor->redact($input); + $redactedAgain = $redactor->redact($redacted); + + $this->assertSame($redacted, $redactedAgain, 'Applying redact() twice to input with all PII categories must produce the same result as applying it once; no placeholder must re-match on the second pass.'); + } +}