@@ -277,3 +277,149 @@ def test_erase_json_dict_with_complex_masking_rules(data_masker):
277
277
"age" : "**" ,
278
278
"address" : {"zip" : "xxx" , "street" : "123 Main St" , "details" : {"name" : "Home" , "type" : "Primary" }},
279
279
}
280
+
281
+
282
+ def test_no_matches_for_masking_rule (data_masker ):
283
+ # GIVEN a dictionary without the expected field
284
+ data = {"name" : "Ana" }
285
+ masking_rules = {"$.missing_field" : {"dynamic_mask" : True }}
286
+
287
+ # WHEN applying the masking rule
288
+ with pytest .warns (UserWarning , match = r"No matches found for path: \$\.missing_field" ):
289
+ result = data_masker ._apply_masking_rules (data , masking_rules )
290
+
291
+ # THEN the original data remains unchanged
292
+ assert result == data
293
+
294
+
295
+ def test_warning_during_masking_value (data_masker ):
296
+ # GIVEN data and a masking rule
297
+ data = {"value" : "test" }
298
+
299
+ # Mock provider that raises an error
300
+ class MockProvider :
301
+ def erase (self , value , ** kwargs ):
302
+ raise ValueError ("Mock error" )
303
+
304
+ data_masker .provider = MockProvider ()
305
+
306
+ # WHEN erase is called
307
+ with pytest .warns (UserWarning , match = "Error masking value for path value: Mock error" ):
308
+ masked_data = data_masker .erase (data , masking_rules = {"value" : {"rule" : "value" }})
309
+
310
+ # THEN the original data should remain unchanged
311
+ assert masked_data ["value" ] == "test"
312
+
313
+
314
+ def test_mask_nested_field_with_non_dict_value (data_masker ):
315
+ # GIVEN nested data where a middle path component is not a dictionary
316
+ data = {"user" : {"contact" : "not_a_dict" , "details" : {"ssn" : "123-45-6789" }}} # This will stop the traversal
317
+
318
+ # WHEN attempting to mask a field through a path containing a non-dict value
319
+ data_masker ._mask_nested_field (data , "user.contact.details.ssn" , lambda x : "MASKED" )
320
+
321
+ # THEN the data should remain unchanged since traversal stopped at non-dict value
322
+ assert data == {"user" : {"contact" : "not_a_dict" , "details" : {"ssn" : "123-45-6789" }}}
323
+
324
+
325
+ def test_mask_nested_field_success (data_masker ):
326
+ # GIVEN nested data with a field to mask
327
+ data = {"user" : {"contact" : {"details" : {"address" : {"street" : "123 Main St" , "zip" : "12345" }}}}}
328
+
329
+ # WHEN masking a nested field with a masking rule
330
+ data_masker ._mask_nested_field (data , "user.contact.details.address.zip" , {"custom_mask" : "xxx" })
331
+
332
+ # THEN the nested field should be masked while other data remains unchanged
333
+ assert data == {"user" : {"contact" : {"details" : {"address" : {"street" : "123 Main St" , "zip" : "xxx" }}}}}
334
+
335
+
336
+ ## teste aqui
337
+ def test_erase_dictionary_with_masking_rules (data_masker ):
338
+ # GIVEN a dictionary with nested sensitive data
339
+ data = {"user" : {"name" : "John Doe" , "ssn" : "123-45-6789" , "address" : {"street" : "123 Main St" , "zip" : "12345" }}}
340
+
341
+ # AND masking rules for specific fields
342
+ masking_rules = {"user.ssn" : {"custom_mask" : "XXX-XX-XXXX" }, "user.address.zip" : {"custom_mask" : "00000" }}
343
+
344
+ # WHEN erase is called with masking rules
345
+ result = data_masker .erase (data , masking_rules = masking_rules )
346
+
347
+ # THEN only the specified fields should be masked
348
+ assert result == {
349
+ "user" : {
350
+ "name" : "John Doe" , # unchanged
351
+ "ssn" : "XXX-XX-XXXX" , # masked
352
+ "address" : {"street" : "123 Main St" , "zip" : "00000" }, # unchanged # masked
353
+ },
354
+ }
355
+
356
+
357
+ def test_erase_dictionary_with_global_mask (data_masker ):
358
+ # GIVEN a dictionary with sensitive data
359
+ data = {"user" : {"name" : "John Doe" , "ssn" : "123-45-6789" }}
360
+
361
+ # WHEN erase is called with a custom mask for all fields
362
+ result = data_masker .erase (data , custom_mask = "REDACTED" )
363
+
364
+ # THEN all fields should use the custom mask
365
+ assert result == {"user" : {"name" : "REDACTED" , "ssn" : "REDACTED" }}
366
+
367
+
368
+ def test_erase_empty_dictionary (data_masker ):
369
+ # GIVEN an empty dictionary
370
+ data = {}
371
+
372
+ # WHEN erase is called
373
+ result = data_masker .erase (data , custom_mask = "MASKED" )
374
+
375
+ # THEN an empty dictionary should be returned
376
+ assert result == {}
377
+
378
+
379
+ def test_erase_different_iterables_with_masking (data_masker ):
380
+ # GIVEN different types of iterables
381
+ list_data = ["name" , "phone" , "email" ]
382
+ tuple_data = ("name" , "phone" , "email" )
383
+ set_data = {"name" , "phone" , "email" }
384
+
385
+ # WHEN erase is called with a custom mask
386
+ masked_list = data_masker .erase (list_data , custom_mask = "XXX" )
387
+ masked_tuple = data_masker .erase (tuple_data , custom_mask = "XXX" )
388
+ masked_set = data_masker .erase (set_data , custom_mask = "XXX" )
389
+
390
+ # THEN the masked data should maintain its original type
391
+ assert isinstance (masked_list , list )
392
+ assert isinstance (masked_tuple , tuple )
393
+ assert isinstance (masked_set , set )
394
+
395
+ # AND all values should be masked
396
+ expected_values = {"XXX" }
397
+ assert set (masked_list ) == expected_values
398
+ assert set (masked_tuple ) == expected_values
399
+ assert masked_set == expected_values
400
+
401
+
402
+ def test_erase_handles_invalid_regex_pattern (data_masker ):
403
+ # GIVEN a string and an invalid regex pattern
404
+ data = "test123"
405
+
406
+ # WHEN masking with invalid regex
407
+ result = data_masker .erase (
408
+ data ,
409
+ regex_pattern = "[" ,
410
+ mask_format = "X" , # Invalid regex pattern that will raise re.error
411
+ )
412
+
413
+ # THEN original data should be returned
414
+ assert result == "test123"
415
+
416
+
417
+ def test_erase_handles_empty_string_with_dynamic_mask (data_masker ):
418
+ # GIVEN an empty string
419
+ data = ""
420
+
421
+ # WHEN erase is called with dynamic_mask
422
+ result = data_masker .erase (data , dynamic_mask = True )
423
+
424
+ # THEN empty string should be returned
425
+ assert result == ""
0 commit comments