1+ import json
2+ import re
13import pytest
24from enum import Enum
35from unittest import TestCase
1012from osbot_utils .type_safe .primitives .domains .identifiers .safe_str .Safe_Str__Id import Safe_Str__Id
1113from osbot_utils .type_safe .type_safe_core .collections .Type_Safe__Dict import Type_Safe__Dict
1214from osbot_utils .type_safe .type_safe_core .collections .Type_Safe__List import Type_Safe__List
15+ from osbot_utils .utils .Env import not_in_github_action
1316from osbot_utils .utils .Objects import base_classes
1417from osbot_utils .type_safe .primitives .domains .identifiers .Safe_Id import Safe_Id
1518from osbot_utils .type_safe .Type_Safe import Type_Safe
@@ -380,4 +383,81 @@ class An_Class(Type_Safe):
380383 assert An_Class (an_dict = {'A' : 42 }).json () == { 'an_dict' : { 'A' : 42 }} # FIXED
381384 assert An_Class .from_json (An_Class (an_dict = {'A' : 42 }).json ()).obj () == __ (an_dict = __ (A = 42 )) # FIXED
382385 assert An_Class .from_json ({ 'an_dict' : { An_Enum .A : 42 }} ).obj () == __ (an_dict = __ (A = 42 )) #
383- assert An_Class .from_json ({ 'an_dict' : { 'A' : 42 }} ).obj () == __ (an_dict = __ (A = 42 )) #
386+ assert An_Class .from_json ({ 'an_dict' : { 'A' : 42 }} ).obj () == __ (an_dict = __ (A = 42 )) #
387+
388+ def test__regression__nested_dict_enum_keys__obj_vs_json_inconsistency (self ):
389+ """
390+ BUG: .obj() and .json() are inconsistent for nested Dict with Enum keys.
391+ .json() uses enum.value, .obj() uses transformed enum.name
392+ """
393+ class Status (str , Enum ):
394+ ACTIVE = 'active'
395+ INACTIVE = 'inactive'
396+
397+ class Container (Type_Safe ):
398+ nested : Dict [str , Dict [Status , int ]]
399+
400+ container = Container (nested = {'key' : {Status .ACTIVE : 100 }})
401+
402+ # .json() uses enum VALUE
403+ assert container .json () == {'nested' : {'key' : {'active' : 100 }}}
404+
405+ # BUG: .obj() should also use 'active' but uses 'Status_ACTIVE'
406+ #assert container.obj() == __(nested=__(key=__(Status_ACTIVE=100))) # BUG Current behavior
407+ #assert container.obj() != __(nested=__(key=__(active=100))) # BUG Expected behavior
408+ assert container .obj () == __ (nested = __ (key = __ (active = 100 ))) # FIXED
409+
410+ # assert container.json() == {'nested': {'key': {Status.ACTIVE: 100}}} # BUG
411+ # error_message = ("assert {'nested': {'key': {<Status.ACTIVE: 'active'>: 100}}} == {}\n \n "
412+ # "Left contains 1 more item:\n "
413+ # "{'nested': {'key': {<Status.ACTIVE: 'active'>: 100}}}\n \n "
414+ # "Full diff:\n - {}\n + {\n + 'nested': {\n + 'key': "
415+ # "{\n + <Status.ACTIVE: 'active'>: 100,\n + },\n + "
416+ # "},\n + }") # BUG
417+ assert container .json () == {'nested' : {'key' : {Status .ACTIVE : 100 }}} # this works due to auto conversion of enum into it's string value
418+ assert container .json () == {'nested' : {'key' : {'active' : 100 }}} # FIXED: this is what we wanted to happen
419+
420+ if not_in_github_action (): # GH actions has different spacing
421+ error_message = ("assert {'nested': {'key': {'active': 100}}} == {}\n \n "
422+ "Left contains 1 more item:\n "
423+ "{'nested': {'key': {'active': 100}}}\n \n " # FIXED: now we get the 'active' string (instead of the Enum representation)
424+ "Full diff:\n - {}\n + {\n + 'nested': "
425+ "{\n + 'key': {\n + "
426+ "'active': 100,\n + },\n + },\n + }" )
427+ with pytest .raises (AssertionError , match = re .escape (error_message )):
428+ assert container .json () == {} # FIXED this is the error message we should get
429+
430+ error_message_2 = 'assert __(nested=__(key=__(active=100))) == __()\n '
431+ with pytest .raises (AssertionError , match = re .escape (error_message_2 )):
432+ assert container .obj () == __ ()
433+
434+ # couple more edge cases tests
435+ json_str = json .dumps (container .json ())
436+ assert json_str == '{"nested": {"key": {"active": 100}}}'
437+ assert json .loads (json_str ) == {'nested' : {'key' : {'active' : 100 }}}
438+
439+ container2 = Container (nested = {'key' : {Status .ACTIVE : 100 , Status .INACTIVE : 50 }})
440+ assert container2 .json () == {'nested' : {'key' : {'active' : 100 , 'inactive' : 50 }}}
441+ assert container2 .obj () == __ (nested = __ (key = __ (active = 100 , inactive = 50 )))
442+
443+ # Test round-trip consistency
444+ container3 = Container .from_json (container .json ())
445+ assert container3 .json () == container .json ()
446+ assert container3 .obj () == container .obj ()
447+
448+ def test__regression__simple_dict_enum_keys__now__works_correctly (self ):
449+
450+ class Status (str , Enum ):
451+ ACTIVE = 'active'
452+ INACTIVE = 'inactive'
453+
454+ class SimpleContainer (Type_Safe ):
455+ data : Dict [Status , int ]
456+
457+ simple = SimpleContainer (data = {Status .ACTIVE : 100 })
458+
459+ # Single-level Dict works correctly - uses enum value
460+ assert simple .json () == {'data' : {'active' : 100 }} # ✓ Correct
461+
462+ # And it's JSON-serializable
463+ assert json .dumps (simple .json ()) == '{"data": {"active": 100}}' # ✓ Works
0 commit comments