@@ -134,53 +134,126 @@ describe('CompleteMultipartUpload final-object checksum', () =>
134134 } ) ;
135135 } ) ;
136136
137- it ( 'should return CRC64NVME/FULL_OBJECT on CompleteMPU response when CreateMPU sent no checksum headers' , async ( ) => {
138- const key = `complete-default-${ Date . now ( ) } ` ;
139-
140- const create = await s3 . send (
141- new CreateMultipartUploadCommand ( {
142- Bucket : bucket ,
143- Key : key ,
144- } ) ,
145- ) ;
146-
147- const uploadPart = await s3 . send (
148- new UploadPartCommand ( {
149- Bucket : bucket ,
150- Key : key ,
151- UploadId : create . UploadId ,
152- PartNumber : 1 ,
153- Body : partBody ,
154- } ) ,
155- ) ;
156-
157- const complete = await s3 . send (
158- new CompleteMultipartUploadCommand ( {
159- Bucket : bucket ,
160- Key : key ,
161- UploadId : create . UploadId ,
162- MultipartUpload : {
163- Parts : [ { PartNumber : 1 , ETag : uploadPart . ETag } ] ,
164- } ,
165- } ) ,
166- ) ;
167-
168- assert (
169- complete . ChecksumCRC64NVME ,
170- `expected ChecksumCRC64NVME for default MPU, got: ${ JSON . stringify ( complete ) } ` ,
171- ) ;
172- assert . strictEqual ( complete . ChecksumType , 'FULL_OBJECT' ) ;
173-
174- // Default MPU is FULL_OBJECT — checksum is persisted, so
175- // HeadObject must return the same value.
176- const head = await s3 . send (
177- new HeadObjectCommand ( {
178- Bucket : bucket ,
179- Key : key ,
180- ChecksumMode : 'ENABLED' ,
181- } ) ,
182- ) ;
183- assert . strictEqual ( head . ChecksumCRC64NVME , complete . ChecksumCRC64NVME ) ;
184- assert . strictEqual ( head . ChecksumType , 'FULL_OBJECT' ) ;
137+ it (
138+ 'should return CRC64NVME/FULL_OBJECT on CompleteMPU response ' + 'when CreateMPU sent no checksum headers' ,
139+ async ( ) => {
140+ const key = `complete-default-${ Date . now ( ) } ` ;
141+
142+ const create = await s3 . send (
143+ new CreateMultipartUploadCommand ( {
144+ Bucket : bucket ,
145+ Key : key ,
146+ } ) ,
147+ ) ;
148+
149+ const uploadPart = await s3 . send (
150+ new UploadPartCommand ( {
151+ Bucket : bucket ,
152+ Key : key ,
153+ UploadId : create . UploadId ,
154+ PartNumber : 1 ,
155+ Body : partBody ,
156+ } ) ,
157+ ) ;
158+
159+ const complete = await s3 . send (
160+ new CompleteMultipartUploadCommand ( {
161+ Bucket : bucket ,
162+ Key : key ,
163+ UploadId : create . UploadId ,
164+ MultipartUpload : {
165+ Parts : [ { PartNumber : 1 , ETag : uploadPart . ETag } ] ,
166+ } ,
167+ } ) ,
168+ ) ;
169+
170+ assert (
171+ complete . ChecksumCRC64NVME ,
172+ `expected ChecksumCRC64NVME for default MPU, got: ${ JSON . stringify ( complete ) } ` ,
173+ ) ;
174+ assert . strictEqual ( complete . ChecksumType , 'FULL_OBJECT' ) ;
175+
176+ // Default MPU is FULL_OBJECT — checksum is persisted, so
177+ // HeadObject must return the same value.
178+ const head = await s3 . send (
179+ new HeadObjectCommand ( {
180+ Bucket : bucket ,
181+ Key : key ,
182+ ChecksumMode : 'ENABLED' ,
183+ } ) ,
184+ ) ;
185+ assert . strictEqual ( head . ChecksumCRC64NVME , complete . ChecksumCRC64NVME ) ;
186+ assert . strictEqual ( head . ChecksumType , 'FULL_OBJECT' ) ;
187+ } ,
188+ ) ;
189+
190+ // AWS S3 rejects any per-part
191+ // Checksum<X> field on a default MPU (one created without an
192+ // explicit ChecksumAlgorithm) with InvalidPart — even when the
193+ // field matches the implicit CRC64NVME algorithm and value.
194+ describe ( 'default MPU rejects per-part Checksum fields' , ( ) => {
195+ async function setupDefaultMpu ( ) {
196+ const key = `complete-default-rejects-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } ` ;
197+ const create = await s3 . send ( new CreateMultipartUploadCommand ( { Bucket : bucket , Key : key } ) ) ;
198+ const uploadPart = await s3 . send (
199+ new UploadPartCommand ( {
200+ Bucket : bucket ,
201+ Key : key ,
202+ UploadId : create . UploadId ,
203+ PartNumber : 1 ,
204+ Body : partBody ,
205+ } ) ,
206+ ) ;
207+ return { key, uploadId : create . UploadId , eTag : uploadPart . ETag } ;
208+ }
209+
210+ async function assertInvalidPart ( promise ) {
211+ let caught ;
212+ try {
213+ await promise ;
214+ } catch ( err ) {
215+ caught = err ;
216+ }
217+ assert ( caught , 'expected CompleteMPU to reject' ) ;
218+ assert . strictEqual (
219+ caught . name ,
220+ 'InvalidPart' ,
221+ `expected InvalidPart, got ${ caught . name } : ${ caught . message } ` ,
222+ ) ;
223+ }
224+
225+ it ( 'should return InvalidPart when Part includes matching ChecksumCRC64NVME (correct value)' , async ( ) => {
226+ const { key, uploadId, eTag } = await setupDefaultMpu ( ) ;
227+ const crc64 = await algorithms . crc64nvme . digest ( partBody ) ;
228+ await assertInvalidPart (
229+ s3 . send (
230+ new CompleteMultipartUploadCommand ( {
231+ Bucket : bucket ,
232+ Key : key ,
233+ UploadId : uploadId ,
234+ MultipartUpload : {
235+ Parts : [ { PartNumber : 1 , ETag : eTag , ChecksumCRC64NVME : crc64 } ] ,
236+ } ,
237+ } ) ,
238+ ) ,
239+ ) ;
240+ } ) ;
241+
242+ it ( 'should return InvalidPart when Part includes a non-matching algorithm field' , async ( ) => {
243+ const { key, uploadId, eTag } = await setupDefaultMpu ( ) ;
244+ const crc32 = await algorithms . crc32 . digest ( partBody ) ;
245+ await assertInvalidPart (
246+ s3 . send (
247+ new CompleteMultipartUploadCommand ( {
248+ Bucket : bucket ,
249+ Key : key ,
250+ UploadId : uploadId ,
251+ MultipartUpload : {
252+ Parts : [ { PartNumber : 1 , ETag : eTag , ChecksumCRC32 : crc32 } ] ,
253+ } ,
254+ } ) ,
255+ ) ,
256+ ) ;
257+ } ) ;
185258 } ) ;
186259 } ) ) ;
0 commit comments