@@ -19,6 +19,83 @@ component extends="testbox.system.BaseSpec" {
1919 } );
2020 } );
2121 } );
22+
23+ story ( " I want to verify LogBox survives appender constructor triggering logger access during configure" , function (){
24+ given ( " A custom appender whose onRegistration() calls getRootLogger()" , function (){
25+ then ( " it should construct without KeyNotFoundException" , function (){
26+ var config = {
27+ appenders : {
28+ sentinelAppender : {
29+ class : " tests.specs.logging.appenders.SentinelTestAppender" ,
30+ properties : {},
31+ levelMin : 0 ,
32+ levelMax : 4
33+ }
34+ },
35+ root : { levelMax : " INFO" , appenders : " sentinelAppender" }
36+ };
37+
38+ // This constructor calls configure() which triggers registerAppender,
39+ // which chains onRegistration() which calls getRootLogger().
40+ // Without the sentinel fix, this throws KeyNotFoundException.
41+ var logBox = new coldbox .system .logging .LogBox ( config );
42+
43+ // Verify getRootLogger returns a real Logger after construction
44+ var root = logBox .getRootLogger ();
45+ expect ( root ).toBeComponent ();
46+ expect ( root .getCategory () ).toBe ( " ROOT" );
47+
48+ // Verify the sentinel appender captured a non-null root logger
49+ var appender = logBox .getAppenderRegistry ()[ " sentinelAppender" ];
50+ expect ( appender .getCapturedRootLogger () ).toBeComponent ();
51+ } );
52+ } );
53+ } );
54+
55+ story ( " I want to re-configure LogBox and verify getRootLogger never returns empty" , function (){
56+ given ( " An already-constructed LogBox" , function (){
57+ then ( " calling configure() again should not break getRootLogger()" , function (){
58+ var config = {
59+ appenders : {
60+ consoleAppender : {
61+ class : " ConsoleAppender" ,
62+ properties : {},
63+ levelMin : 0 ,
64+ levelMax : 4
65+ }
66+ },
67+ root : { levelMax : " INFO" , appenders : " consoleAppender" }
68+ };
69+
70+ var logBox = new coldbox .system .logging .LogBox ( config );
71+ expect ( logBox .getRootLogger () ).toBeComponent ();
72+
73+ // Re-configure: this resets registries and re-runs the appender loop.
74+ // The sentinel root must be in place during the entire re-configure.
75+ logBox .configure ( config );
76+
77+ expect ( logBox .getRootLogger () ).toBeComponent ();
78+ expect ( logBox .getRootLogger ().getCategory () ).toBe ( " ROOT" );
79+ } );
80+ } );
81+ } );
82+
83+ story ( " I want getRootLogger() to have a defensive fallback when registry is empty" , function (){
84+ given ( " A LogBox with the ROOT key manually removed from its loggerRegistry" , function (){
85+ then ( " it should return a transient Logger instead of throwing" , function (){
86+ var config = new coldbox .system .logging .config .LogBoxConfig ();
87+ var logBox = new coldbox .system .logging .LogBox ( config );
88+
89+ // Simulate a corrupt/empty registry state (extreme edge case)
90+ structDelete ( logBox .getLoggerRegistry (), " ROOT" );
91+
92+ // Should return a Logger, not throw KeyNotFoundException
93+ var root = logBox .getRootLogger ();
94+ expect ( root ).toBeComponent ();
95+ expect ( root .getCategory () ).toBe ( " ROOT" );
96+ } );
97+ } );
98+ } );
2299 } );
23100 }
24101
0 commit comments