@@ -22,7 +22,16 @@ import {
2222import { intVar , requiredVar } from '@balena/env-parsing' ;
2323import { assertExists } from './lib/common.js' ;
2424import { PineTest } from 'pinejs-client-supertest' ;
25- import type { UploadPart } from '../out/webresource-handler/index.js' ;
25+ import {
26+ type SupportedChecksumAlgorithm ,
27+ supportedChecksumAlgorithms ,
28+ type UploadPart ,
29+ } from '../out/webresource-handler/index.js' ;
30+ import { CrtCrc64Nvme } from '@aws-sdk/crc64-nvme-crt' ;
31+ import { AwsCrc32 } from '@aws-crypto/crc32' ;
32+ import { AwsCrc32c } from '@aws-crypto/crc32c' ;
33+ import type { Checksum } from '@aws-sdk/types' ;
34+ import { setTimeout } from 'timers/promises' ;
2635
2736const pipeline = util . promisify ( pipelineRaw ) ;
2837
@@ -122,6 +131,41 @@ describe('06 webresources tests', function () {
122131 ) ;
123132 } ) ;
124133
134+ supportedChecksumAlgorithms . forEach ( ( checksumAlgorithm ) => {
135+ it ( `accepts a checksum ${ checksumAlgorithm } header ${ resourcePath } ` , async ( ) => {
136+ const { body : organization } = await supertest ( testLocalServer )
137+ . post ( `/${ resourceName } /organization` )
138+ . set ( 'x-pinejs-checksum-algorithm' , checksumAlgorithm )
139+ . set (
140+ 'x-pinejs-checksum' ,
141+ await getChecksum ( checksumAlgorithm , filePath ) ,
142+ )
143+ . field ( 'name' , 'John' )
144+ . attach ( resourcePath , filePath , { filename, contentType } )
145+ . expect ( 201 ) ;
146+
147+ expect ( organization [ resourcePath ] . size ) . to . equals ( fileSize ) ;
148+ expect ( organization [ resourcePath ] . filename ) . to . equals ( filename ) ;
149+ expect ( organization [ resourcePath ] . content_type ) . to . equals (
150+ contentType ,
151+ ) ;
152+ } ) ;
153+ } ) ;
154+
155+ supportedChecksumAlgorithms . forEach ( ( checksumAlgorithm ) => {
156+ it ( `fails if checksum ${ checksumAlgorithm } is wrong` , async ( ) => {
157+ await supertest ( testLocalServer )
158+ . post ( `/${ resourceName } /organization` )
159+ . set ( 'x-pinejs-checksum-algorithm' , checksumAlgorithm )
160+ . set ( 'x-pinejs-checksum' , 'abc' )
161+ . field ( 'name' , 'John' )
162+ . attach ( resourcePath , filePath , { filename, contentType } )
163+ . expect ( 400 ) ;
164+
165+ expect ( await isBucketEventuallyEmpty ( ) ) . to . be . true ;
166+ } ) ;
167+ } ) ;
168+
125169 it ( `does not store ${ resourcePath } if is bigger than PINEJS_WEBRESOURCE_MAXFILESIZE` , async ( ) => {
126170 const { largeStream } = await getLargeFileStream (
127171 intVar ( 'PINEJS_WEBRESOURCE_MAXFILESIZE' ) + 10 * 1024 * 1024 ,
@@ -1405,14 +1449,12 @@ const getKeyFromHref = (href: string): string => {
14051449 return splittedHref [ splittedHref . length - 1 ] ;
14061450} ;
14071451
1408- const delay = ( ms : number ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
1409-
14101452const isBucketEventuallyEmpty = async ( attempts = 5 , retryDelay = 1000 ) => {
14111453 for ( let attempt = 0 ; attempt < attempts ; attempt ++ ) {
14121454 if ( ( await listAllFilesInBucket ( ) ) . length === 0 ) {
14131455 return true ;
14141456 }
1415- await delay ( retryDelay ) ;
1457+ await setTimeout ( retryDelay ) ;
14161458 }
14171459
14181460 return false ;
@@ -1430,7 +1472,7 @@ const isEventuallyDeleted = async (
14301472 if ( ! fileExists ) {
14311473 return true ;
14321474 }
1433- await delay ( retryDelay ) ;
1475+ await setTimeout ( retryDelay ) ;
14341476 }
14351477
14361478 return false ;
@@ -1557,8 +1599,8 @@ const awaitForDeletionTasks = async (
15571599 initialDelay = 500 ,
15581600) => {
15591601 // Even the creation of deletion tasks happens in second plan
1560- // so we give it a initial delay for the tasks to be created
1561- await delay ( initialDelay ) ;
1602+ // so we give it a initial setTimeout for the tasks to be created
1603+ await setTimeout ( initialDelay ) ;
15621604 for ( let i = 0 ; i < attempts ; i ++ ) {
15631605 const { body : pendingDeletions } = await pineTask . get ( {
15641606 resource : 'task' ,
@@ -1575,7 +1617,7 @@ const awaitForDeletionTasks = async (
15751617 return ;
15761618 }
15771619
1578- await delay ( retryDelay ) ;
1620+ await setTimeout ( retryDelay ) ;
15791621 }
15801622
15811623 throw new Error ( 'Failed to await for webresource deletions' ) ;
@@ -1650,3 +1692,23 @@ const uploadParts = async (parts: UploadPart[]) => {
16501692 } ) ) ,
16511693 } ;
16521694} ;
1695+
1696+ const getChecksum = async (
1697+ algorithm : SupportedChecksumAlgorithm ,
1698+ filePath : string ,
1699+ ) : Promise < string > => {
1700+ const fileBuffer = await fs . readFile ( filePath ) ;
1701+ let calculate : Checksum ;
1702+ if ( algorithm === 'CRC32' ) {
1703+ calculate = new AwsCrc32 ( ) ;
1704+ } else if ( algorithm === 'CRC32C' ) {
1705+ calculate = new AwsCrc32c ( ) ;
1706+ } else if ( algorithm === 'CRC64NVME' ) {
1707+ calculate = new CrtCrc64Nvme ( ) ;
1708+ } else {
1709+ throw new Error ( `Unsupported algorithm: ${ algorithm } ` ) ;
1710+ }
1711+
1712+ calculate . update ( fileBuffer ) ;
1713+ return Buffer . from ( await calculate . digest ( ) ) . toString ( 'base64' ) ;
1714+ } ;
0 commit comments