@@ -303,4 +303,159 @@ repository:
303303 } )
304304 } ) // loadConfigs
305305
306+ describe ( 'loadYaml' , ( ) => {
307+ let settings ;
308+
309+ beforeEach ( ( ) => {
310+ Settings . fileCache = { } ;
311+ stubContext = {
312+ octokit : {
313+ repos : {
314+ getContent : jest . fn ( )
315+ } ,
316+ request : jest . fn ( ) ,
317+ paginate : jest . fn ( )
318+ } ,
319+ log : {
320+ debug : jest . fn ( ) ,
321+ info : jest . fn ( ) ,
322+ error : jest . fn ( )
323+ } ,
324+ payload : {
325+ installation : {
326+ id : 123
327+ }
328+ }
329+ } ;
330+ settings = createSettings ( { } ) ;
331+ } ) ;
332+
333+ it ( 'should return parsed YAML content when file is fetched successfully' , async ( ) => {
334+ // Given
335+ const filePath = 'path/to/file.yml' ;
336+ const content = Buffer . from ( 'key: value' ) . toString ( 'base64' ) ;
337+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockResolvedValue ( {
338+ data : { content } ,
339+ headers : { etag : 'etag123' }
340+ } ) ;
341+
342+ // When
343+ const result = await settings . loadYaml ( filePath ) ;
344+
345+ // Then
346+ expect ( result ) . toEqual ( { key : 'value' } ) ;
347+ expect ( Settings . fileCache [ `${ mockRepo . owner } /${ filePath } ` ] ) . toEqual ( {
348+ etag : 'etag123' ,
349+ data : { content }
350+ } ) ;
351+ } ) ;
352+
353+ it ( 'should return cached content when file has not changed (304 response)' , async ( ) => {
354+ // Given
355+ const filePath = 'path/to/file.yml' ;
356+ const content = Buffer . from ( 'key: value' ) . toString ( 'base64' ) ;
357+ Settings . fileCache [ `${ mockRepo . owner } /${ filePath } ` ] = { etag : 'etag123' , data : { content } } ;
358+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockRejectedValue ( { status : 304 } ) ;
359+
360+ // When
361+ const result = await settings . loadYaml ( filePath ) ;
362+
363+ // Then
364+ expect ( result ) . toEqual ( { key : 'value' } ) ;
365+ expect ( settings . github . repos . getContent ) . toHaveBeenCalledWith (
366+ expect . objectContaining ( { headers : { 'If-None-Match' : 'etag123' } } )
367+ ) ;
368+ } ) ;
369+
370+ it ( 'should not return cached content when the cache is for another org' , async ( ) => {
371+ // Given
372+ const filePath = 'path/to/file.yml' ;
373+ const content = Buffer . from ( 'key: value' ) . toString ( 'base64' ) ;
374+ const wrongContent = Buffer . from ( 'wrong: content' ) . toString ( 'base64' ) ;
375+ Settings . fileCache [ 'another-org/path/to/file.yml' ] = { etag : 'etag123' , data : { wrongContent } } ;
376+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockResolvedValue ( {
377+ data : { content } ,
378+ headers : { etag : 'etag123' }
379+ } ) ;
380+
381+ // When
382+ const result = await settings . loadYaml ( filePath ) ;
383+
384+ // Then
385+ expect ( result ) . toEqual ( { key : 'value' } ) ;
386+ } )
387+
388+ it ( 'should return null when the file path is a folder' , async ( ) => {
389+ // Given
390+ const filePath = 'path/to/folder' ;
391+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockResolvedValue ( {
392+ data : [ ]
393+ } ) ;
394+
395+ // When
396+ const result = await settings . loadYaml ( filePath ) ;
397+
398+ // Then
399+ expect ( result ) . toBeNull ( ) ;
400+ } ) ;
401+
402+ it ( 'should return null when the file is a symlink or submodule' , async ( ) => {
403+ // Given
404+ const filePath = 'path/to/symlink' ;
405+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockResolvedValue ( {
406+ data : { content : null }
407+ } ) ;
408+
409+ // When
410+ const result = await settings . loadYaml ( filePath ) ;
411+
412+ // Then
413+ expect ( result ) . toBeUndefined ( ) ;
414+ } ) ;
415+
416+ it ( 'should handle 404 errors gracefully and return null' , async ( ) => {
417+ // Given
418+ const filePath = 'path/to/nonexistent.yml' ;
419+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockRejectedValue ( { status : 404 } ) ;
420+
421+ // When
422+ const result = await settings . loadYaml ( filePath ) ;
423+
424+ // Then
425+ expect ( result ) . toBeNull ( ) ;
426+ } ) ;
427+
428+ it ( 'should throw an error for non-404 exceptions when not in nop mode' , async ( ) => {
429+ // Given
430+ const filePath = 'path/to/error.yml' ;
431+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockRejectedValue ( new Error ( 'Unexpected error' ) ) ;
432+
433+ // When / Then
434+ await expect ( settings . loadYaml ( filePath ) ) . rejects . toThrow ( 'Unexpected error' ) ;
435+ } ) ;
436+
437+ it ( 'should log and append NopCommand for non-404 exceptions in nop mode' , async ( ) => {
438+ // Given
439+ const filePath = 'path/to/error.yml' ;
440+ settings . nop = true ;
441+ jest . spyOn ( settings . github . repos , 'getContent' ) . mockRejectedValue ( new Error ( 'Unexpected error' ) ) ;
442+ jest . spyOn ( settings , 'appendToResults' ) ;
443+
444+ // When
445+ const result = await settings . loadYaml ( filePath ) ;
446+
447+ // Then
448+ expect ( result ) . toBeUndefined ( ) ;
449+ expect ( settings . appendToResults ) . toHaveBeenCalledWith (
450+ expect . arrayContaining ( [
451+ expect . objectContaining ( {
452+ type : 'ERROR' ,
453+ action : expect . objectContaining ( {
454+ msg : expect . stringContaining ( 'Unexpected error' )
455+ } )
456+ } )
457+ ] )
458+ ) ;
459+ } ) ;
460+ } ) ;
306461} ) // Settings Tests
0 commit comments