" 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.'); } }