@@ -2,7 +2,7 @@ const { Readable } = require('stream');
22const config = require ( 'config' ) ;
33const S3Adapter = require ( '../index' ) ;
44const optionsFromArguments = require ( '../lib/optionsFromArguments' ) ;
5- const { GetObjectCommand, PutObjectCommand, CreateBucketCommand } = require ( '@aws-sdk/client-s3' ) ;
5+ const { GetObjectCommand, PutObjectCommand, HeadBucketCommand , CreateBucketCommand } = require ( '@aws-sdk/client-s3' ) ;
66const { getMockS3Adapter } = require ( './mocks/s3adapter' ) ;
77
88
@@ -48,6 +48,107 @@ describe('S3Adapter tests', () => {
4848 expect ( s3 . _bucket ) . toBe ( TEST_BUCKET ) ;
4949 } ) ;
5050
51+ describe ( 'bucket operations' , ( ) => {
52+ let s3 , s3ClientMock ;
53+ beforeEach ( ( ) => {
54+ const options = {
55+ bucket : 'bucket-1' ,
56+ bucketPrefix : 'test/' ,
57+ } ;
58+ s3ClientMock = jasmine . createSpyObj ( 'S3Client' , [ 'send' ] ) ;
59+ s3ClientMock . send . and . returnValue ( Promise . resolve ( ) ) ;
60+
61+ s3 = new S3Adapter ( options ) ;
62+ s3 . _s3Client = s3ClientMock ;
63+ } ) ;
64+
65+ it ( 'should return early if _hasBucket is true' , async ( ) => {
66+ s3 . _hasBucket = true ;
67+
68+ await s3 . createBucket ( ) ;
69+
70+ expect ( s3ClientMock . send ) . not . toHaveBeenCalled ( ) ;
71+ } ) ;
72+
73+ it ( 'should set _hasBucket to true if bucket exists' , async ( ) => {
74+ s3ClientMock . send . and . returnValue ( Promise . resolve ( { } ) ) ;
75+
76+ await s3 . createBucket ( ) ;
77+
78+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( HeadBucketCommand ) ) ;
79+ expect ( s3 . _hasBucket ) . toBe ( true ) ;
80+ } ) ;
81+
82+ it ( 'should attempt to create bucket if NotFound error occurs' , async ( ) => {
83+ const notFoundError = { name : 'NotFound' } ;
84+ s3ClientMock . send . and . returnValues (
85+ Promise . reject ( notFoundError ) ,
86+ Promise . resolve ( { } )
87+ ) ;
88+
89+ await s3 . createBucket ( ) ;
90+
91+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
92+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( HeadBucketCommand ) ) ;
93+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( CreateBucketCommand ) ) ;
94+ expect ( s3 . _hasBucket ) . toBe ( true ) ;
95+ } ) ;
96+
97+ it ( 'should handle BucketAlreadyExists error during creation' , async ( ) => {
98+ const notFoundError = { name : 'NotFound' } ;
99+ const bucketExistsError = { name : 'BucketAlreadyExists' } ;
100+ s3ClientMock . send . and . returnValues (
101+ Promise . reject ( notFoundError ) ,
102+ Promise . reject ( bucketExistsError )
103+ ) ;
104+
105+ await s3 . createBucket ( ) ;
106+
107+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
108+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( HeadBucketCommand ) ) ;
109+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( CreateBucketCommand ) ) ;
110+ expect ( s3 . _hasBucket ) . toBe ( true ) ;
111+ } ) ;
112+
113+ it ( 'should handle BucketAlreadyOwnedByYou error during creation' , async ( ) => {
114+ const notFoundError = { name : 'NotFound' } ;
115+ const bucketOwnedError = { name : 'BucketAlreadyOwnedByYou' } ;
116+ s3ClientMock . send . and . returnValues (
117+ Promise . reject ( notFoundError ) ,
118+ Promise . reject ( bucketOwnedError )
119+ ) ;
120+
121+ await s3 . createBucket ( ) ;
122+
123+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
124+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( HeadBucketCommand ) ) ;
125+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( CreateBucketCommand ) ) ;
126+ expect ( s3 . _hasBucket ) . toBe ( true ) ;
127+ } ) ;
128+
129+ it ( 'should throw non-NotFound errors during check' , async ( ) => {
130+ const otherError = { name : 'SomeOtherError' } ;
131+ s3ClientMock . send . and . returnValue ( Promise . reject ( otherError ) ) ;
132+
133+ await expectAsync ( s3 . createBucket ( ) )
134+ . toBeRejectedWith ( otherError ) ;
135+ expect ( s3 . _hasBucket ) . toBe ( false ) ;
136+ } ) ;
137+
138+ it ( 'should throw unexpected errors during creation' , async ( ) => {
139+ const notFoundError = { name : 'NotFound' } ;
140+ const creationError = { name : 'CreationError' } ;
141+ s3ClientMock . send . and . returnValues (
142+ Promise . reject ( notFoundError ) ,
143+ Promise . reject ( creationError )
144+ ) ;
145+
146+ await expectAsync ( s3 . createBucket ( ) )
147+ . toBeRejectedWith ( creationError ) ;
148+ expect ( s3 . _hasBucket ) . toBe ( false ) ;
149+ } ) ;
150+ } )
151+
51152 describe ( 'configured with immutable values' , ( ) => {
52153 describe ( 'not initialized properly' , ( ) => {
53154 it ( 'should fail with two string arguments' , ( ) => {
@@ -252,9 +353,10 @@ describe('S3Adapter tests', () => {
252353 expect ( resp . writeHead ) . toHaveBeenCalled ( ) ;
253354 expect ( resp . write ) . toHaveBeenCalled ( ) ;
254355 expect ( resp . end ) . toHaveBeenCalled ( ) ;
356+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
357+
255358 const commands = s3ClientMock . send . calls . all ( ) ;
256- expect ( commands . length ) . toBe ( 2 ) ;
257- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
359+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
258360 const commandArg = commands [ 1 ] . args [ 0 ] ;
259361 expect ( commandArg ) . toBeInstanceOf ( GetObjectCommand ) ;
260362 expect ( commandArg . input . Range ) . toBe ( 'bytes=0-1' ) ;
@@ -640,8 +742,9 @@ describe('S3Adapter tests', () => {
640742 s3 . _s3Client = s3ClientMock ;
641743
642744 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
643- expect ( s3ClientMock . send . calls . all ( ) . length ) . toBe ( 2 ) ;
644- expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( CreateBucketCommand ) ) ;
745+
746+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
747+ expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( HeadBucketCommand ) ) ;
645748 expect ( s3ClientMock . send ) . toHaveBeenCalledWith ( jasmine . any ( PutObjectCommand ) ) ;
646749 } ) ;
647750
@@ -651,9 +754,9 @@ describe('S3Adapter tests', () => {
651754 const metadata = { foo : 'bar' } ;
652755
653756 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { metadata } ) ;
757+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
654758 const commands = s3ClientMock . send . calls . all ( ) ;
655- expect ( commands . length ) . toBe ( 2 ) ;
656- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
759+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
657760 const commandArg = commands [ 1 ] . args [ 0 ] ;
658761 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
659762 expect ( commandArg . input . Metadata ) . toEqual ( { foo : 'bar' } ) ;
@@ -665,9 +768,9 @@ describe('S3Adapter tests', () => {
665768 const tags = { foo : 'bar' , baz : 'bin' } ;
666769
667770 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { tags } ) ;
771+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
668772 const commands = s3ClientMock . send . calls . all ( ) ;
669- expect ( commands . length ) . toBe ( 2 ) ;
670- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
773+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
671774 const commandArg = commands [ 1 ] . args [ 0 ] ;
672775 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
673776 expect ( commandArg . input . Tagging ) . toBe ( 'foo=bar&baz=bin' ) ;
@@ -679,9 +782,9 @@ describe('S3Adapter tests', () => {
679782 s3 . _s3Client = s3ClientMock ;
680783
681784 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
785+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
682786 const commands = s3ClientMock . send . calls . all ( ) ;
683- expect ( commands . length ) . toBe ( 2 ) ;
684- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
787+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
685788 const commandArg = commands [ 1 ] . args [ 0 ] ;
686789 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
687790 expect ( commandArg . input . ACL ) . toBe ( 'public-read' ) ;
@@ -692,9 +795,9 @@ describe('S3Adapter tests', () => {
692795 s3 . _s3Client = s3ClientMock ;
693796
694797 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
798+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
695799 const commands = s3ClientMock . send . calls . all ( ) ;
696- expect ( commands . length ) . toBe ( 2 ) ;
697- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
800+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
698801 const commandArg = commands [ 1 ] . args [ 0 ] ;
699802 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
700803 expect ( commandArg . input . ACL ) . toBeUndefined ( ) ;
@@ -707,9 +810,9 @@ describe('S3Adapter tests', () => {
707810 s3 . _s3Client = s3ClientMock ;
708811
709812 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
813+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
710814 const commands = s3ClientMock . send . calls . all ( ) ;
711- expect ( commands . length ) . toBe ( 2 ) ;
712- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
815+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
713816 const commandArg = commands [ 1 ] . args [ 0 ] ;
714817 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
715818 expect ( commandArg . input . ACL ) . toBe ( 'private' ) ;
@@ -723,9 +826,9 @@ describe('S3Adapter tests', () => {
723826 s3 . _s3Client = s3ClientMock ;
724827
725828 await s3 . createFile ( 'file.txt' , 'hello world' , 'text/utf8' , { } ) ;
829+ expect ( s3ClientMock . send ) . toHaveBeenCalledTimes ( 2 ) ;
726830 const commands = s3ClientMock . send . calls . all ( ) ;
727- expect ( commands . length ) . toBe ( 2 ) ;
728- expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( CreateBucketCommand ) ;
831+ expect ( commands [ 0 ] . args [ 0 ] ) . toBeInstanceOf ( HeadBucketCommand ) ;
729832 const commandArg = commands [ 1 ] . args [ 0 ] ;
730833 expect ( commandArg ) . toBeInstanceOf ( PutObjectCommand ) ;
731834 expect ( commandArg . input . ACL ) . toBeUndefined ( ) ;
0 commit comments