@@ -674,6 +674,63 @@ describe('config/resolver', () => {
674674 expect ( config . selfSigned ) . to . equal ( true ) ;
675675 } ) ;
676676
677+ it ( 'preserves non-instance-bound source fields on hostname mismatch' , ( ) => {
678+ const instanceSource = new MockSource ( 'dw-json' , {
679+ hostname : 'prod.demandware.net' ,
680+ username : 'admin' ,
681+ password : 'prod-pass' ,
682+ shortCode : 'abcdef' ,
683+ } ) ;
684+ const globalSource = new MockSource ( 'password-store' , {
685+ clientId : 'my-client-id' ,
686+ clientSecret : 'my-client-secret' ,
687+ shortCode : 'abcdef' ,
688+ } ) ;
689+ const resolver = new ConfigResolver ( [ instanceSource , globalSource ] ) ;
690+
691+ const { config, warnings, sources} = resolver . resolve (
692+ { hostname : 'staging.demandware.net' } ,
693+ { hostnameProtection : true } ,
694+ ) ;
695+
696+ expect ( config . hostname ) . to . equal ( 'staging.demandware.net' ) ;
697+ // Instance-bound fields should be dropped
698+ expect ( config . username ) . to . be . undefined ;
699+ expect ( config . password ) . to . be . undefined ;
700+ // Non-instance-bound fields should survive
701+ expect ( config . clientId ) . to . equal ( 'my-client-id' ) ;
702+ expect ( config . clientSecret ) . to . equal ( 'my-client-secret' ) ;
703+ // Fields from non-instance-bound source that were previously shadowed
704+ // by the instance-bound source should now be available
705+ expect ( config . shortCode ) . to . equal ( 'abcdef' ) ;
706+ expect ( warnings ) . to . have . length ( 1 ) ;
707+ expect ( warnings [ 0 ] . code ) . to . equal ( 'HOSTNAME_MISMATCH' ) ;
708+
709+ // Source info should reflect the drop
710+ const dwJsonInfo = sources . find ( ( s ) => s . name === 'dw-json' ) ;
711+ expect ( dwJsonInfo ) . to . exist ;
712+ expect ( dwJsonInfo ! . fields ) . to . deep . equal ( [ ] ) ;
713+ expect ( dwJsonInfo ! . fieldsIgnored ) . to . include ( 'hostname' ) ;
714+ expect ( dwJsonInfo ! . fieldsIgnored ) . to . include ( 'username' ) ;
715+ expect ( dwJsonInfo ! . fieldsIgnored ) . to . include ( 'password' ) ;
716+ } ) ;
717+
718+ it ( 'drops fields from plugin source that also provides hostname on mismatch' , ( ) => {
719+ const pluginSource = new MockSource ( 'custom-plugin' , {
720+ hostname : 'prod.demandware.net' ,
721+ clientId : 'plugin-client-id' ,
722+ clientSecret : 'plugin-client-secret' ,
723+ } ) ;
724+ const resolver = new ConfigResolver ( [ pluginSource ] ) ;
725+
726+ const { config} = resolver . resolve ( { hostname : 'staging.demandware.net' } , { hostnameProtection : true } ) ;
727+
728+ expect ( config . hostname ) . to . equal ( 'staging.demandware.net' ) ;
729+ // Plugin provided hostname, so it's instance-bound — all its fields dropped
730+ expect ( config . clientId ) . to . be . undefined ;
731+ expect ( config . clientSecret ) . to . be . undefined ;
732+ } ) ;
733+
677734 it ( 'discards TLS options on hostname mismatch protection' , ( ) => {
678735 const source = new MockSource ( 'test' , {
679736 hostname : 'prod.demandware.net' ,
0 commit comments