@@ -82,6 +82,19 @@ jest.mock('@aws-sdk/client-s3', () => {
82
82
} ) )
83
83
} ;
84
84
} ) ;
85
+ jest . mock ( '@aws-sdk/client-sts' , ( ) => {
86
+ const original = jest . requireActual ( '@aws-sdk/client-sts' ) ;
87
+ return {
88
+ ...original ,
89
+ GetCallerIdentityCommand : jest . fn ( ) . mockImplementation ( ( params ) => ( {
90
+ ...params ,
91
+ type : 'GetCallerIdentityCommand'
92
+ } ) ) ,
93
+ STSClient : jest . fn ( ) . mockImplementation ( ( ) => ( {
94
+ send : jest . fn ( ) . mockResolvedValue ( { Account : '123456789012' } )
95
+ } ) )
96
+ } ;
97
+ } ) ;
85
98
86
99
afterAll ( ( ) => {
87
100
jest . clearAllMocks ( ) ;
@@ -598,4 +611,153 @@ describe('Function Existence Check', () => {
598
611
} ) ) ;
599
612
} ) ;
600
613
} ) ;
614
+
615
+ test ( 'Config changed with dry run logs message' , ( ) => {
616
+ const configChanged = true ;
617
+ const dryRun = true ;
618
+
619
+ if ( configChanged ) {
620
+ if ( dryRun ) {
621
+ core . info ( '[DRY RUN] Configuration updates are not simulated in dry run mode' ) ;
622
+ return ;
623
+ }
624
+ }
625
+
626
+ expect ( core . info ) . toHaveBeenCalledWith ( '[DRY RUN] Configuration updates are not simulated in dry run mode' ) ;
627
+ } ) ;
628
+
629
+ test ( 'Config changed without dry run calls updateFunctionConfiguration' , async ( ) => {
630
+ const mockClient = { send : jest . fn ( ) } ;
631
+ const configChanged = true ;
632
+ const dryRun = false ;
633
+
634
+ if ( configChanged ) {
635
+ if ( dryRun ) {
636
+ core . info ( '[DRY RUN] Configuration updates are not simulated in dry run mode' ) ;
637
+ return ;
638
+ }
639
+
640
+ await index . updateFunctionConfiguration ( mockClient , {
641
+ functionName : 'test-function' ,
642
+ role : 'test-role' ,
643
+ handler : 'index.handler' ,
644
+ functionDescription : 'test' ,
645
+ parsedMemorySize : 256 ,
646
+ timeout : 30 ,
647
+ runtime : 'nodejs20.x' ,
648
+ kmsKeyArn : 'test-kms' ,
649
+ ephemeralStorage : 512 ,
650
+ vpcConfig : '{}' ,
651
+ parsedEnvironment : { } ,
652
+ deadLetterConfig : '{}' ,
653
+ tracingConfig : '{}' ,
654
+ layers : '[]' ,
655
+ fileSystemConfigs : '[]' ,
656
+ imageConfig : '{}' ,
657
+ snapStart : '{}' ,
658
+ loggingConfig : '{}' ,
659
+ parsedVpcConfig : { } ,
660
+ parsedDeadLetterConfig : { } ,
661
+ parsedTracingConfig : { } ,
662
+ parsedLayers : [ ] ,
663
+ parsedFileSystemConfigs : [ ] ,
664
+ parsedImageConfig : { } ,
665
+ parsedSnapStart : { } ,
666
+ parsedLoggingConfig : { }
667
+ } ) ;
668
+ }
669
+
670
+ expect ( mockClient . send ) . toHaveBeenCalled ( ) ;
671
+ } ) ;
672
+
673
+ test ( 'No config changes logs no changes message' , ( ) => {
674
+ const configChanged = false ;
675
+
676
+ if ( configChanged ) {
677
+ // Should not execute
678
+ } else {
679
+ core . info ( 'No configuration changes detected' ) ;
680
+ }
681
+
682
+ expect ( core . info ) . toHaveBeenCalledWith ( 'No configuration changes detected' ) ;
683
+ } ) ;
684
+
685
+ test ( 'generateS3Key creates key with timestamp' , ( ) => {
686
+ const result = index . generateS3Key ( 'test-function' ) ;
687
+ expect ( result ) . toMatch ( / ^ l a m b d a - d e p l o y m e n t s \/ t e s t - f u n c t i o n \/ \d { 4 } - \d { 2 } - \d { 2 } - \d { 2 } - \d { 2 } - \d { 2 } - \d { 3 } \. z i p $ / ) ;
688
+ } ) ;
689
+
690
+ test ( 'generateS3Key includes commit hash when GITHUB_SHA exists' , ( ) => {
691
+ process . env . GITHUB_SHA = 'abcdef1234567890' ;
692
+ const result = index . generateS3Key ( 'test-function' ) ;
693
+ expect ( result ) . toContain ( '-abcdef1' ) ;
694
+ delete process . env . GITHUB_SHA ;
695
+ } ) ;
696
+
697
+ test ( 'validateBucketName validates bucket names' , ( ) => {
698
+ expect ( index . validateBucketName ( 'valid-bucket-name' ) ) . toBe ( true ) ;
699
+ expect ( index . validateBucketName ( 'ab' ) ) . toBe ( false ) ;
700
+ expect ( index . validateBucketName ( 'INVALID' ) ) . toBe ( false ) ;
701
+ expect ( index . validateBucketName ( '192.168.1.1' ) ) . toBe ( false ) ;
702
+ expect ( index . validateBucketName ( 'bucket..name' ) ) . toBe ( false ) ;
703
+ } ) ;
704
+
705
+ test ( 'isEmptyValue checks empty values' , ( ) => {
706
+ expect ( index . isEmptyValue ( null ) ) . toBe ( true ) ;
707
+ expect ( index . isEmptyValue ( '' ) ) . toBe ( true ) ;
708
+ expect ( index . isEmptyValue ( [ ] ) ) . toBe ( true ) ;
709
+ expect ( index . isEmptyValue ( { } ) ) . toBe ( true ) ;
710
+ expect ( index . isEmptyValue ( 'value' ) ) . toBe ( false ) ;
711
+ expect ( index . isEmptyValue ( { SubnetIds : [ ] } ) ) . toBe ( false ) ;
712
+ } ) ;
713
+
714
+ test ( 'cleanNullKeys removes null values' , ( ) => {
715
+ expect ( index . cleanNullKeys ( null ) ) . toBeUndefined ( ) ;
716
+ expect ( index . cleanNullKeys ( '' ) ) . toBeUndefined ( ) ;
717
+ expect ( index . cleanNullKeys ( { key : 'value' , empty : null } ) ) . toEqual ( { key : 'value' } ) ;
718
+ expect ( index . cleanNullKeys ( [ ] ) ) . toBeUndefined ( ) ;
719
+ expect ( index . cleanNullKeys ( [ 'value' , null ] ) ) . toEqual ( [ 'value' ] ) ;
720
+ } ) ;
721
+
722
+ test ( 'deepEqual compares objects deeply' , ( ) => {
723
+ expect ( index . deepEqual ( { a : 1 } , { a : 1 } ) ) . toBe ( true ) ;
724
+ expect ( index . deepEqual ( { a : 1 } , { a : 2 } ) ) . toBe ( false ) ;
725
+ expect ( index . deepEqual ( [ 1 , 2 ] , [ 1 , 2 ] ) ) . toBe ( true ) ;
726
+ expect ( index . deepEqual ( [ 1 , 2 ] , [ 1 , 3 ] ) ) . toBe ( false ) ;
727
+ expect ( index . deepEqual ( null , null ) ) . toBe ( true ) ;
728
+ expect ( index . deepEqual ( 'test' , 'test' ) ) . toBe ( true ) ;
729
+ } ) ;
730
+
731
+ test ( 'hasConfigurationChanged detects changes' , async ( ) => {
732
+ const current = { Role : 'old-role' , Handler : 'old.handler' } ;
733
+ const updated = { Role : 'new-role' , Handler : 'old.handler' } ;
734
+ const result = await index . hasConfigurationChanged ( current , updated ) ;
735
+ expect ( result ) . toBe ( true ) ;
736
+ expect ( core . info ) . toHaveBeenCalledWith ( expect . stringContaining ( 'Configuration difference detected' ) ) ;
737
+ } ) ;
738
+
739
+ test ( 'hasConfigurationChanged returns true for empty current config' , async ( ) => {
740
+ const result = await index . hasConfigurationChanged ( { } , { Role : 'test' } ) ;
741
+ expect ( result ) . toBe ( true ) ;
742
+ } ) ;
743
+
744
+ test ( 'getAwsAccountId retrieves account ID' , async ( ) => {
745
+ const mockSend = jest . fn ( ) . mockResolvedValue ( { Account : '123456789012' } ) ;
746
+ const { STSClient } = require ( '@aws-sdk/client-sts' ) ;
747
+ STSClient . mockImplementation ( ( ) => ( { send : mockSend } ) ) ;
748
+
749
+ const result = await index . getAwsAccountId ( 'us-east-1' ) ;
750
+ expect ( result ) . toBe ( '123456789012' ) ;
751
+ expect ( core . info ) . toHaveBeenCalledWith ( 'Successfully retrieved AWS account ID: 123456789012' ) ;
752
+ } ) ;
753
+
754
+ test ( 'getAwsAccountId handles errors' , async ( ) => {
755
+ const mockSend = jest . fn ( ) . mockRejectedValue ( new Error ( 'STS error' ) ) ;
756
+ const { STSClient } = require ( '@aws-sdk/client-sts' ) ;
757
+ STSClient . mockImplementation ( ( ) => ( { send : mockSend } ) ) ;
758
+
759
+ const result = await index . getAwsAccountId ( 'us-east-1' ) ;
760
+ expect ( result ) . toBeNull ( ) ;
761
+ expect ( core . warning ) . toHaveBeenCalledWith ( 'Failed to retrieve AWS account ID: STS error' ) ;
762
+ } ) ;
601
763
} ) ;
0 commit comments