@@ -669,9 +669,11 @@ function workbench(f: Awaited<ReturnType<typeof fixture>>, bytes: Buffer) {
669669
670670async function privateUploadTransport (
671671 f : Awaited < ReturnType < typeof fixture > > ,
672- uploadProvenance : ( ) => WorkspaceFileSecretProvenance
672+ uploadProvenance : ( ) => WorkspaceFileSecretProvenance ,
673+ signal ?: AbortSignal
673674) {
674675 const context = {
676+ signal,
675677 userId : f . editorId ,
676678 workspaceId : f . workspaceId ,
677679 toolCallId : generateId ( ) ,
@@ -885,6 +887,45 @@ describe.skipIf(!redisUrl)('private Project upload transport', () => {
885887 expect ( current . metadata . projectFileSecretProvenance ) . toMatchObject ( { pending : true } )
886888 }
887889 )
890+
891+ for ( const signalOwner of [ 'request' , 'invocation' ] as const ) {
892+ check (
893+ `private Project upload never registers a file when the ${ signalOwner } is cancelled while its provenance resolves` ,
894+ async ( ) => {
895+ const f = await fixture ( )
896+ const bytes = Buffer . from ( 'cancelled upload' )
897+ const controller = new AbortController ( )
898+ /** Resolves cleanly after the abort, so only a recheck before completion can stop it. */
899+ const cancelling = ( ) => {
900+ controller . abort ( new Error ( 'cancelled during upload provenance' ) )
901+ return { status : 'exact' as const , entries : [ ] }
902+ }
903+ const transport = await privateUploadTransport (
904+ f ,
905+ cancelling ,
906+ signalOwner === 'invocation' ? controller . signal : undefined
907+ )
908+ const { session } = await createPrivateUpload ( transport , f . projectId , bytes )
909+ await put ( session , bytes )
910+ await expect (
911+ transport (
912+ `http://localhost:3000/api/v2/projects/${ f . projectId } /files/uploads/${ session . id } /complete` ,
913+ {
914+ method : 'POST' ,
915+ headers : { 'upload-token' : session . uploadToken } ,
916+ signal : signalOwner === 'request' ? controller . signal : undefined ,
917+ }
918+ )
919+ ) . rejects . toThrow ( 'cancelled during upload provenance' )
920+ const current = await sessions . verifyUploadSessionToken ( session . uploadToken )
921+ expect ( current . completedFileId ) . toBeNull ( )
922+ expect (
923+ await db . select ( ) . from ( workspaceFiles ) . where ( eq ( workspaceFiles . projectId , f . projectId ) )
924+ ) . toEqual ( [ ] )
925+ expect ( await usage ( f . organizationId ) ) . toBe ( 0 )
926+ }
927+ )
928+ }
888929} )
889930
890931afterAll ( async ( ) => {
0 commit comments