@@ -106,23 +106,18 @@ describe('ConfigurationHandler', () => {
106106 expect ( updatedDesc ) . toMatch ( / " w o r k i n g B r a n c h " : \s * " f e a t u r e \/ 1 2 3 " / ) ;
107107 } ) ;
108108
109- it ( 'preserves stored keys when current has undefined' , async ( ) => {
109+ it ( 'preserves all stored keys (including unknown ones) when current has undefined' , async ( ) => {
110110 const storedJson = JSON . stringify ( {
111111 parentBranch : 'main' ,
112- releaseBranch : 'release/1 ' ,
113- branchType : 'hotfix' ,
112+ unknownKey : 'preserve-me ' ,
113+ branchConfiguration : { name : 'leaf' } ,
114114 } ) ;
115115 mockGetDescription . mockResolvedValue ( descriptionWithConfig ( storedJson ) ) ;
116116 mockUpdateDescription . mockResolvedValue ( undefined ) ;
117117
118118 const execution = minimalExecution ( {
119119 currentConfiguration : {
120- branchType : 'feature' ,
121- releaseBranch : undefined ,
122- workingBranch : 'feature/123' ,
123120 parentBranch : undefined ,
124- hotfixOriginBranch : undefined ,
125- hotfixBranch : undefined ,
126121 branchConfiguration : undefined ,
127122 } ,
128123 } ) ;
@@ -131,8 +126,80 @@ describe('ConfigurationHandler', () => {
131126
132127 expect ( mockUpdateDescription ) . toHaveBeenCalled ( ) ;
133128 const fullDesc = mockUpdateDescription . mock . calls [ 0 ] [ 3 ] ;
134- expect ( fullDesc ) . toContain ( '"parentBranch": "main"' ) ;
135- expect ( fullDesc ) . toContain ( '"releaseBranch": "release/1"' ) ;
129+ const parsed = JSON . parse ( handler . getContent ( fullDesc ) ! . trim ( ) ) ;
130+ expect ( parsed . parentBranch ) . toBe ( 'main' ) ;
131+ expect ( parsed . unknownKey ) . toBe ( 'preserve-me' ) ;
132+ expect ( parsed . branchConfiguration ) . toEqual ( { name : 'leaf' } ) ;
133+ } ) ;
134+
135+ it ( 'always excludes results from the saved payload even if present in stored' , async ( ) => {
136+ const storedJson = JSON . stringify ( {
137+ results : [ { some : 'result' } ] ,
138+ parentBranch : 'main' ,
139+ } ) ;
140+ mockGetDescription . mockResolvedValue ( descriptionWithConfig ( storedJson ) ) ;
141+ mockUpdateDescription . mockResolvedValue ( undefined ) ;
142+
143+ const execution = minimalExecution ( {
144+ currentConfiguration : {
145+ parentBranch : 'develop' ,
146+ } ,
147+ } ) ;
148+
149+ await handler . update ( execution ) ;
150+
151+ const fullDesc = mockUpdateDescription . mock . calls [ 0 ] [ 3 ] ;
152+ const parsed = JSON . parse ( handler . getContent ( fullDesc ) ! . trim ( ) ) ;
153+ expect ( parsed . results ) . toBeUndefined ( ) ;
154+ expect ( parsed . parentBranch ) . toBe ( 'develop' ) ;
155+ } ) ;
156+
157+ it ( 'fails safely when block is mangled (missing end tag)' , async ( ) => {
158+ const mangledDesc = `body\n${ CONFIG_START } \n{"x":1}\nno end tag here` ;
159+ mockGetDescription . mockResolvedValue ( mangledDesc ) ;
160+
161+ const execution = minimalExecution ( ) ;
162+ const result = await handler . update ( execution ) ;
163+
164+ // Should log error and return undefined instead of corrupting or crashing
165+ expect ( result ) . toBeUndefined ( ) ;
166+ const { logError } = require ( '../../../utils/logger' ) ;
167+ expect ( logError ) . toHaveBeenCalledWith ( expect . stringContaining ( 'problem with open-close tags' ) ) ;
168+ } ) ;
169+
170+ it ( 'handles malformed JSON in stored config gracefully' , async ( ) => {
171+ mockGetDescription . mockResolvedValue ( descriptionWithConfig ( 'invalid { json' ) ) ;
172+ mockUpdateDescription . mockResolvedValue ( undefined ) ;
173+
174+ const execution = minimalExecution ( {
175+ currentConfiguration : {
176+ branchType : 'feature' ,
177+ workingBranch : 'feat/new' ,
178+ } ,
179+ } ) ;
180+
181+ await handler . update ( execution ) ;
182+
183+ expect ( mockUpdateDescription ) . toHaveBeenCalled ( ) ;
184+ const fullDesc = mockUpdateDescription . mock . calls [ 0 ] [ 3 ] ;
185+ expect ( fullDesc ) . toContain ( '"branchType": "feature"' ) ;
186+ expect ( fullDesc ) . toContain ( '"workingBranch": "feat/new"' ) ;
187+ } ) ;
188+
189+ it ( 'handles empty stored config block gracefully' , async ( ) => {
190+ mockGetDescription . mockResolvedValue ( descriptionWithConfig ( ' ' ) ) ;
191+ mockUpdateDescription . mockResolvedValue ( undefined ) ;
192+
193+ const execution = minimalExecution ( {
194+ currentConfiguration : {
195+ branchType : 'feature' ,
196+ } ,
197+ } ) ;
198+
199+ await handler . update ( execution ) ;
200+
201+ expect ( mockUpdateDescription ) . toHaveBeenCalled ( ) ;
202+ expect ( mockUpdateDescription . mock . calls [ 0 ] [ 3 ] ) . toContain ( '"branchType": "feature"' ) ;
136203 } ) ;
137204
138205 it ( 'returns undefined on error' , async ( ) => {
@@ -144,4 +211,19 @@ describe('ConfigurationHandler', () => {
144211 expect ( result ) . toBeUndefined ( ) ;
145212 } ) ;
146213 } ) ;
214+
215+ describe ( 'edge cases' , ( ) => {
216+ it ( 'get returns undefined when internalGetter returns empty string' , async ( ) => {
217+ mockGetDescription . mockResolvedValue ( '' ) ;
218+ const execution = minimalExecution ( ) ;
219+ const result = await handler . get ( execution ) ;
220+ expect ( result ) . toBeUndefined ( ) ;
221+ } ) ;
222+
223+ it ( 'get throws informative error on invalid JSON' , async ( ) => {
224+ mockGetDescription . mockResolvedValue ( descriptionWithConfig ( '{ "broken": ' ) ) ;
225+ const execution = minimalExecution ( ) ;
226+ await expect ( handler . get ( execution ) ) . rejects . toThrow ( / U n e x p e c t e d e n d o f J S O N i n p u t | S y n t a x E r r o r / ) ;
227+ } ) ;
228+ } ) ;
147229} ) ;
0 commit comments