1- import  {  ChecksumAlgorithm  }  from  "@aws-sdk/middleware-flexible-checksums" ; 
1+ import  { 
2+   ChecksumAlgorithm , 
3+   DEFAULT_CHECKSUM_ALGORITHM , 
4+   RequestChecksumCalculation , 
5+   ResponseChecksumValidation , 
6+ }  from  "@aws-sdk/middleware-flexible-checksums" ; 
27import  {  HttpRequest  }  from  "@smithy/protocol-http" ; 
38import  {  BuildMiddleware  }  from  "@smithy/types" ; 
49import  {  Readable  }  from  "stream" ; 
@@ -7,7 +12,7 @@ import { describe, expect, test as it } from "vitest";
712import  {  ChecksumAlgorithm  as  Algo ,  S3  }  from  "../../src/index" ; 
813
914describe ( "Flexible Checksums" ,  ( )  =>  { 
10-   const  testCases  =  [ 
15+   const  testCases :  [ string ,   string   |   undefined ,   string ] [ ]  =  [ 
1116    [ "" ,  ChecksumAlgorithm . CRC32 ,  "AAAAAA==" ] , 
1217    [ "abc" ,  ChecksumAlgorithm . CRC32 ,  "NSRBwg==" ] , 
1318    [ "Hello world" ,  ChecksumAlgorithm . CRC32 ,  "i9aeUg==" ] , 
@@ -23,148 +28,204 @@ describe("Flexible Checksums", () => {
2328    [ "" ,  ChecksumAlgorithm . SHA256 ,  "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" ] , 
2429    [ "abc" ,  ChecksumAlgorithm . SHA256 ,  "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" ] , 
2530    [ "Hello world" ,  ChecksumAlgorithm . SHA256 ,  "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=" ] , 
31+ 
32+     // Choose default checksum algorithm when explicily not provided. 
33+     [ "Hello world" ,  undefined ,  "i9aeUg==" ] , 
2634  ] ; 
2735
2836  describe ( "putObject" ,  ( )  =>  { 
29-     testCases . forEach ( ( [ body ,  checksumAlgorithm ,  checksumValue ] )  =>  { 
30-       const  checksumHeader  =  `x-amz-checksum-${ checksumAlgorithm . toLowerCase ( ) }  ; 
31- 
32-       describe ( `sets ${ checksumHeader } ${ checksumValue } ${ checksumAlgorithm }  ,  ( )  =>  { 
33-         const  getBodyAsReadableStream  =  ( content : string )  =>  { 
34-           const  readableStream  =  new  Readable ( ) ; 
35-           const  separator  =  " " ; 
36-           const  wordsAsChunks  =  content . split ( separator ) ; 
37-           wordsAsChunks . forEach ( ( word ,  index )  =>  { 
38-             readableStream . push ( word ) ; 
39-             if  ( index  !==  wordsAsChunks . length  -  1 )  { 
40-               readableStream . push ( separator ) ; 
41-             } 
42-           } ) ; 
43-           readableStream . push ( null ) ; 
44-           return  readableStream ; 
45-         } ; 
46- 
47-         it ( `when body is sent as a request` ,  async  ( )  =>  { 
48-           const  requestChecksumValidator : BuildMiddleware < any ,  any >  =  ( next )  =>  async  ( args )  =>  { 
49-             // middleware intercept the request and return it early 
50-             const  request  =  args . request  as  HttpRequest ; 
51-             const  {  headers }  =  request ; 
52-             expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . to . equal ( checksumAlgorithm ) ; 
53-             expect ( headers [ checksumHeader ] ) . to . equal ( checksumValue ) ; 
54-             return  {  output : { }  as  any ,  response : { }  as  any  } ; 
55-           } ; 
56- 
57-           const  client  =  new  S3 ( { 
58-             region : "us-west-2" , 
59-             credentials : { 
60-               accessKeyId : "CLIENT_TEST" , 
61-               secretAccessKey : "CLIENT_TEST" , 
62-             } , 
63-           } ) ; 
64-           client . middlewareStack . addRelativeTo ( requestChecksumValidator ,  { 
65-             relation : "after" , 
66-             toMiddleware : "flexibleChecksumsMiddleware" , 
67-           } ) ; 
68- 
69-           return  await  client . putObject ( { 
70-             Bucket : "bucket" , 
71-             Key : "key" , 
72-             Body : body , 
73-             ChecksumAlgorithm : checksumAlgorithm  as  Algo , 
74-           } ) ; 
75-         } ) ; 
76- 
77-         it ( `when body is sent as a stream` ,  async  ( )  =>  { 
78-           const  requestChecksumValidator : BuildMiddleware < any ,  any >  =  ( next )  =>  async  ( args )  =>  { 
79-             // middleware intercept the request and return it early 
80-             const  request  =  args . request  as  HttpRequest ; 
81-             const  {  headers,  body }  =  request ; 
82-             expect ( headers [ "content-length" ] ) . to . be . undefined ; 
83-             expect ( headers [ "content-encoding" ] ) . to . equal ( "aws-chunked" ) ; 
84-             expect ( headers [ "transfer-encoding" ] ) . to . equal ( "chunked" ) ; 
85-             expect ( headers [ "x-amz-content-sha256" ] ) . to . equal ( "STREAMING-UNSIGNED-PAYLOAD-TRAILER" ) ; 
86-             expect ( headers [ "x-amz-trailer" ] ) . to . equal ( checksumHeader ) ; 
87-             body . on ( "data" ,  ( data : any )  =>  { 
88-               const  stringValue  =  data . toString ( ) ; 
89-               if  ( stringValue . startsWith ( checksumHeader ) )  { 
90-                 const  receivedChecksum  =  stringValue . replace ( "\r\n" ,  "" ) . split ( ":" ) [ 1 ] ; 
91-                 expect ( receivedChecksum ) . to . equal ( checksumValue ) ; 
92-               } 
37+     describe . each ( [ undefined ,  RequestChecksumCalculation . WHEN_SUPPORTED ,  RequestChecksumCalculation . WHEN_REQUIRED ] ) ( 
38+       `when requestChecksumCalculation='%s'` , 
39+       ( requestChecksumCalculation )  =>  { 
40+         describe . each ( testCases ) ( 
41+           `for body="%s" and checksumAlgorithm="%s", sets checksum="%s"` , 
42+           ( body ,  checksumAlgorithm ,  checksumValue )  =>  { 
43+             const  checksumHeader  =  `x-amz-checksum-${ ( checksumAlgorithm  ??  DEFAULT_CHECKSUM_ALGORITHM ) . toLowerCase ( ) }  ; 
44+             const  getBodyAsReadableStream  =  ( content : string )  =>  { 
45+               const  readableStream  =  new  Readable ( ) ; 
46+               const  separator  =  " " ; 
47+               const  wordsAsChunks  =  content . split ( separator ) ; 
48+               wordsAsChunks . forEach ( ( word ,  index )  =>  { 
49+                 readableStream . push ( word ) ; 
50+                 if  ( index  !==  wordsAsChunks . length  -  1 )  { 
51+                   readableStream . push ( separator ) ; 
52+                 } 
53+               } ) ; 
54+               readableStream . push ( null ) ; 
55+               return  readableStream ; 
56+             } ; 
57+ 
58+             it ( `when body is sent as a string` ,  async  ( )  =>  { 
59+               const  requestChecksumValidator : BuildMiddleware < any ,  any >  =  ( next )  =>  async  ( args )  =>  { 
60+                 // middleware intercept the request and return it early 
61+                 const  request  =  args . request  as  HttpRequest ; 
62+                 const  {  headers }  =  request ; 
63+ 
64+                 // Headers are not set when checksumAlgorithm is not provided, 
65+                 // and requestChecksumCalculation is explicitly set to WHEN_SUPPORTED. 
66+                 if  ( 
67+                   checksumAlgorithm  ===  undefined  && 
68+                   requestChecksumCalculation  ===  RequestChecksumCalculation . WHEN_REQUIRED 
69+                 )  { 
70+                   expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . toBeUndefined ( ) ; 
71+                   expect ( headers [ checksumHeader ] ) . toBeUndefined ( ) ; 
72+                 }  else  { 
73+                   expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . toEqual ( 
74+                     checksumAlgorithm  ??  DEFAULT_CHECKSUM_ALGORITHM 
75+                   ) ; 
76+                   expect ( headers [ checksumHeader ] ) . toEqual ( checksumValue ) ; 
77+                 } 
78+ 
79+                 return  {  output : { }  as  any ,  response : { }  as  any  } ; 
80+               } ; 
81+ 
82+               const  client  =  new  S3 ( { 
83+                 region : "us-west-2" , 
84+                 credentials : { 
85+                   accessKeyId : "CLIENT_TEST" , 
86+                   secretAccessKey : "CLIENT_TEST" , 
87+                 } , 
88+                 requestChecksumCalculation, 
89+               } ) ; 
90+               client . middlewareStack . addRelativeTo ( requestChecksumValidator ,  { 
91+                 relation : "after" , 
92+                 toMiddleware : "flexibleChecksumsMiddleware" , 
93+               } ) ; 
94+ 
95+               return  await  client . putObject ( { 
96+                 Bucket : "bucket" , 
97+                 Key : "key" , 
98+                 Body : body , 
99+                 ChecksumAlgorithm : checksumAlgorithm  as  Algo , 
100+               } ) ; 
101+             } ) ; 
102+ 
103+             it ( `when body is sent as a stream` ,  async  ( )  =>  { 
104+               const  requestChecksumValidator : BuildMiddleware < any ,  any >  =  ( next )  =>  async  ( args )  =>  { 
105+                 // middleware intercept the request and return it early 
106+                 const  request  =  args . request  as  HttpRequest ; 
107+                 const  {  headers,  body }  =  request ; 
108+                 expect ( headers [ "content-length" ] ) . toBeUndefined ( ) ; 
109+ 
110+                 // Headers are not set when checksumAlgorithm is not provided, 
111+                 // and requestChecksumCalculation is explicitly set to WHEN_SUPPORTED. 
112+                 if  ( 
113+                   checksumAlgorithm  ===  undefined  && 
114+                   requestChecksumCalculation  ===  RequestChecksumCalculation . WHEN_REQUIRED 
115+                 )  { 
116+                   expect ( headers [ "content-encoding" ] ) . toBeUndefined ( ) ; 
117+                   expect ( headers [ "transfer-encoding" ] ) . toBeUndefined ( ) ; 
118+                   expect ( headers [ "x-amz-content-sha256" ] ) . toBeUndefined ( ) ; 
119+                   expect ( headers [ "x-amz-trailer" ] ) . toBeUndefined ( ) ; 
120+                 }  else  { 
121+                   expect ( headers [ "content-encoding" ] ) . toEqual ( "aws-chunked" ) ; 
122+                   expect ( headers [ "transfer-encoding" ] ) . toEqual ( "chunked" ) ; 
123+                   expect ( headers [ "x-amz-content-sha256" ] ) . toEqual ( "STREAMING-UNSIGNED-PAYLOAD-TRAILER" ) ; 
124+                   expect ( headers [ "x-amz-trailer" ] ) . toEqual ( checksumHeader ) ; 
125+                 } 
126+                 body . on ( "data" ,  ( data : any )  =>  { 
127+                   const  stringValue  =  data . toString ( ) ; 
128+                   if  ( stringValue . startsWith ( checksumHeader ) )  { 
129+                     const  receivedChecksum  =  stringValue . replace ( "\r\n" ,  "" ) . split ( ":" ) [ 1 ] ; 
130+                     expect ( receivedChecksum ) . toEqual ( checksumValue ) ; 
131+                   } 
132+                 } ) ; 
133+                 return  {  output : { }  as  any ,  response : { }  as  any  } ; 
134+               } ; 
135+ 
136+               const  client  =  new  S3 ( { 
137+                 region : "us-west-2" , 
138+                 credentials : { 
139+                   accessKeyId : "CLIENT_TEST" , 
140+                   secretAccessKey : "CLIENT_TEST" , 
141+                 } , 
142+                 requestChecksumCalculation, 
143+               } ) ; 
144+               client . middlewareStack . addRelativeTo ( requestChecksumValidator ,  { 
145+                 relation : "after" , 
146+                 toMiddleware : "flexibleChecksumsMiddleware" , 
147+               } ) ; 
148+ 
149+               const  bodyStream  =  getBodyAsReadableStream ( body ) ; 
150+               await  client . putObject ( { 
151+                 Bucket : "bucket" , 
152+                 Key : "key" , 
153+                 Body : bodyStream , 
154+                 ChecksumAlgorithm : checksumAlgorithm  as  Algo , 
155+               } ) ; 
93156            } ) ; 
94-             return  {  output : { }  as  any ,  response : { }  as  any  } ; 
95-           } ; 
96- 
97-           const  client  =  new  S3 ( { 
98-             region : "us-west-2" , 
99-             credentials : { 
100-               accessKeyId : "CLIENT_TEST" , 
101-               secretAccessKey : "CLIENT_TEST" , 
102-             } , 
103-           } ) ; 
104-           client . middlewareStack . addRelativeTo ( requestChecksumValidator ,  { 
105-             relation : "after" , 
106-             toMiddleware : "flexibleChecksumsMiddleware" , 
107-           } ) ; 
108- 
109-           const  bodyStream  =  getBodyAsReadableStream ( body ) ; 
110-           await  client . putObject ( { 
111-             Bucket : "bucket" , 
112-             Key : "key" , 
113-             Body : bodyStream , 
114-             ChecksumAlgorithm : checksumAlgorithm  as  Algo , 
115-           } ) ; 
116-         } ) ; 
117-       } ) ; 
118-     } ) ; 
157+           } 
158+         ) ; 
159+       } 
160+     ) ; 
119161  } ) ; 
120162
121163  describe ( "getObject" ,  async  ( )  =>  { 
122-     testCases . forEach ( ( [ body ,  checksumAlgorithm ,  checksumValue ] )  =>  { 
123-       const  checksumHeader  =  `x-amz-checksum-${ checksumAlgorithm . toLowerCase ( ) }  ; 
124- 
125-       it ( `validates ${ checksumHeader } ${ checksumValue } ${ checksumAlgorithm }  ,  async  ( )  =>  { 
126-         const  responseBody  =  new  Readable ( ) ; 
127-         responseBody . push ( body ) ; 
128-         responseBody . push ( null ) ; 
129-         const  responseChecksumValidator : BuildMiddleware < any ,  any >  =  ( next ,  context )  =>  async  ( args )  =>  { 
130-           const  request  =  args . request  as  HttpRequest ; 
131-           return  { 
132-             output : { 
133-               $metadata : {  attempts : 0 ,  httpStatusCode : 200  } , 
134-               request, 
135-               context, 
136-               Body : responseBody , 
137-             }  as  any , 
138-             response : { 
139-               body : responseBody , 
140-               headers : { 
141-                 [ checksumHeader ] : checksumValue , 
164+     describe . each ( [ undefined ,  ResponseChecksumValidation . WHEN_SUPPORTED ,  ResponseChecksumValidation . WHEN_REQUIRED ] ) ( 
165+       `when responseChecksumValidation='%s'` , 
166+       ( responseChecksumValidation )  =>  { 
167+         it . each ( testCases ) ( 
168+           `for body="%s" and checksumAlgorithm="%s", validates ChecksumMode` , 
169+           async  ( body ,  checksumAlgorithm ,  checksumValue )  =>  { 
170+             const  checksumHeader  =  `x-amz-checksum-${ ( checksumAlgorithm  ??  DEFAULT_CHECKSUM_ALGORITHM ) . toLowerCase ( ) }  ; 
171+ 
172+             const  responseBody  =  new  Readable ( ) ; 
173+             responseBody . push ( body ) ; 
174+             responseBody . push ( null ) ; 
175+             const  responseChecksumValidator : BuildMiddleware < any ,  any >  =  ( next ,  context )  =>  async  ( args )  =>  { 
176+               // ChecksumMode is not set when checksumAlgorithm is not provided, 
177+               // and responseChecksumValidation is explicitly set to WHEN_SUPPORTED. 
178+               if  ( 
179+                 checksumAlgorithm  ===  undefined  && 
180+                 responseChecksumValidation  ===  ResponseChecksumValidation . WHEN_REQUIRED 
181+               )  { 
182+                 expect ( args . input . ChecksumMode ) . toBeUndefined ( ) ; 
183+               }  else  { 
184+                 expect ( args . input . ChecksumMode ) . toEqual ( "ENABLED" ) ; 
185+               } 
186+ 
187+               const  request  =  args . request  as  HttpRequest ; 
188+               return  { 
189+                 output : { 
190+                   $metadata : {  attempts : 0 ,  httpStatusCode : 200  } , 
191+                   request, 
192+                   context, 
193+                   Body : responseBody , 
194+                 }  as  any , 
195+                 response : { 
196+                   body : responseBody , 
197+                   headers : { 
198+                     [ checksumHeader ] : checksumValue , 
199+                   } , 
200+                 }  as  any , 
201+               } ; 
202+             } ; 
203+ 
204+             const  client  =  new  S3 ( { 
205+               region : "us-west-2" , 
206+               credentials : { 
207+                 accessKeyId : "CLIENT_TEST" , 
208+                 secretAccessKey : "CLIENT_TEST" , 
142209              } , 
143-             }  as  any , 
144-           } ; 
145-         } ; 
146- 
147-         const  client  =  new  S3 ( { 
148-           region : "us-west-2" , 
149-           credentials : { 
150-             accessKeyId : "CLIENT_TEST" , 
151-             secretAccessKey : "CLIENT_TEST" , 
152-           } , 
153-         } ) ; 
154-         client . middlewareStack . addRelativeTo ( responseChecksumValidator ,  { 
155-           relation : "after" , 
156-           toMiddleware : "flexibleChecksumsMiddleware" , 
157-         } ) ; 
158- 
159-         const  {  Body }  =  await  client . getObject ( { 
160-           Bucket : "bucket" , 
161-           Key : "key" , 
162-           ChecksumMode : "ENABLED" , 
163-         } ) ; 
164-         ( Body  as  Readable ) . on ( "data" ,  ( chunk )  =>  { 
165-           expect ( chunk . toString ( ) ) . to . equal ( body ) ; 
166-         } ) ; 
167-       } ) ; 
168-     } ) ; 
210+               responseChecksumValidation, 
211+             } ) ; 
212+             client . middlewareStack . addRelativeTo ( responseChecksumValidator ,  { 
213+               relation : "after" , 
214+               toMiddleware : "flexibleChecksumsMiddleware" , 
215+             } ) ; 
216+ 
217+             const  {  Body }  =  await  client . getObject ( { 
218+               Bucket : "bucket" , 
219+               Key : "key" , 
220+               // Do not pass ChecksumMode if algorithm is not explicitly defined. It'll be set by SDK. 
221+               ChecksumMode : checksumAlgorithm  ? "ENABLED"  : undefined , 
222+             } ) ; 
223+             ( Body  as  Readable ) . on ( "data" ,  ( chunk )  =>  { 
224+               expect ( chunk . toString ( ) ) . toEqual ( body ) ; 
225+             } ) ; 
226+           } 
227+         ) ; 
228+       } 
229+     ) ; 
169230  } ) ; 
170231} ) ; 
0 commit comments