@@ -89,4 +89,80 @@ describe('getOrGenerateKey', () => {
8989 expect ( k ) . toEqual ( 'test-org-key-2' ) ;
9090 } ) ;
9191 } ) ;
92+
93+ describe ( 'legacy key migration' , ( ) => {
94+ it ( 'migrates key from legacy location to new location' , async ( ) => {
95+ const stored : Record < string , string > = {
96+ LaunchDarkly_AnonymousKeys_org : 'existing-org-key' ,
97+ } ;
98+ ( storage . get as jest . Mock ) . mockImplementation ( ( key : string ) => stored [ key ] ?? null ) ;
99+ ( storage . set as jest . Mock ) . mockImplementation ( ( key : string , value : string ) => {
100+ stored [ key ] = value ;
101+ } ) ;
102+ ( storage . clear as jest . Mock ) . mockImplementation ( ( key : string ) => {
103+ delete stored [ key ] ;
104+ } ) ;
105+
106+ const k = await getOrGenerateKey (
107+ 'LaunchDarkly_ContextKeys_org' ,
108+ mockPlatform ,
109+ 'LaunchDarkly_AnonymousKeys_org' ,
110+ ) ;
111+
112+ expect ( k ) . toEqual ( 'existing-org-key' ) ;
113+ expect ( crypto . randomUUID ) . not . toHaveBeenCalled ( ) ;
114+ expect ( storage . set ) . toHaveBeenCalledWith ( 'LaunchDarkly_ContextKeys_org' , 'existing-org-key' ) ;
115+ expect ( storage . clear ) . toHaveBeenCalledWith ( 'LaunchDarkly_AnonymousKeys_org' ) ;
116+ } ) ;
117+
118+ it ( 'does not clear legacy key when set silently fails' , async ( ) => {
119+ ( storage . get as jest . Mock ) . mockImplementation ( ( key : string ) =>
120+ key === 'LaunchDarkly_AnonymousKeys_org' ? 'existing-org-key' : null ,
121+ ) ;
122+ // set is a no-op, simulating a silent storage failure
123+ ( storage . set as jest . Mock ) . mockResolvedValue ( undefined ) ;
124+
125+ const k = await getOrGenerateKey (
126+ 'LaunchDarkly_ContextKeys_org' ,
127+ mockPlatform ,
128+ 'LaunchDarkly_AnonymousKeys_org' ,
129+ ) ;
130+
131+ expect ( k ) . toEqual ( 'existing-org-key' ) ;
132+ expect ( storage . set ) . toHaveBeenCalledWith ( 'LaunchDarkly_ContextKeys_org' , 'existing-org-key' ) ;
133+ expect ( storage . clear ) . not . toHaveBeenCalled ( ) ;
134+ } ) ;
135+
136+ it ( 'does not check legacy key when new key already exists' , async ( ) => {
137+ ( storage . get as jest . Mock ) . mockImplementation ( ( key : string ) =>
138+ key === 'LaunchDarkly_ContextKeys_org' ? 'new-org-key' : 'legacy-org-key' ,
139+ ) ;
140+
141+ const k = await getOrGenerateKey (
142+ 'LaunchDarkly_ContextKeys_org' ,
143+ mockPlatform ,
144+ 'LaunchDarkly_AnonymousKeys_org' ,
145+ ) ;
146+
147+ expect ( k ) . toEqual ( 'new-org-key' ) ;
148+ expect ( storage . get ) . toHaveBeenCalledTimes ( 1 ) ;
149+ expect ( storage . get ) . toHaveBeenCalledWith ( 'LaunchDarkly_ContextKeys_org' ) ;
150+ expect ( storage . clear ) . not . toHaveBeenCalled ( ) ;
151+ } ) ;
152+
153+ it ( 'generates new key when neither new nor legacy key exists' , async ( ) => {
154+ ( storage . get as jest . Mock ) . mockResolvedValue ( null ) ;
155+
156+ const k = await getOrGenerateKey (
157+ 'LaunchDarkly_ContextKeys_org' ,
158+ mockPlatform ,
159+ 'LaunchDarkly_AnonymousKeys_org' ,
160+ ) ;
161+
162+ expect ( k ) . toEqual ( 'test-org-key-1' ) ;
163+ expect ( crypto . randomUUID ) . toHaveBeenCalled ( ) ;
164+ expect ( storage . set ) . toHaveBeenCalledWith ( 'LaunchDarkly_ContextKeys_org' , 'test-org-key-1' ) ;
165+ expect ( storage . clear ) . not . toHaveBeenCalled ( ) ;
166+ } ) ;
167+ } ) ;
92168} ) ;
0 commit comments