@@ -8,24 +8,27 @@ const {
88 mockCheckRateLimit,
99 mockValidateWorkspaceAccess,
1010 mockGetWorkspaceFile,
11- mockFetchServableWorkspaceFileBuffer ,
11+ mockDownloadWorkspaceFileStream ,
1212} = vi . hoisted ( ( ) => ( {
1313 mockCheckRateLimit : vi . fn ( ) ,
1414 mockValidateWorkspaceAccess : vi . fn ( ) ,
1515 mockGetWorkspaceFile : vi . fn ( ) ,
16- mockFetchServableWorkspaceFileBuffer : vi . fn ( ) ,
16+ mockDownloadWorkspaceFileStream : vi . fn ( ) ,
1717} ) )
1818
1919vi . mock ( '@/app/api/v1/middleware' , ( ) => ( {
2020 checkRateLimit : mockCheckRateLimit ,
2121 createRateLimitResponse : ( ) => new Response ( 'rate limited' , { status : 429 } ) ,
22+ requireRateLimitPrincipal : ( rateLimit : { principal : unknown } ) => rateLimit . principal ,
2223 validateWorkspaceAccess : mockValidateWorkspaceAccess ,
2324 v1ValidationErrorResponse : ( e : { issues : unknown [ ] } ) =>
2425 NextResponse . json ( { error : 'Validation error' , details : e . issues } , { status : 400 } ) ,
2526} ) )
2627vi . mock ( '@/lib/uploads/contexts/workspace' , ( ) => ( {
2728 getWorkspaceFile : mockGetWorkspaceFile ,
28- fetchServableWorkspaceFileBuffer : mockFetchServableWorkspaceFileBuffer ,
29+ } ) )
30+ vi . mock ( '@/lib/workspace-files/application/download-workspace-file' , ( ) => ( {
31+ downloadWorkspaceFileStream : { execute : mockDownloadWorkspaceFileStream } ,
2932} ) )
3033vi . mock ( '@/lib/workspace-files/orchestration' , ( ) => ( {
3134 performDeleteWorkspaceFileItems : vi . fn ( ) ,
@@ -37,14 +40,19 @@ vi.mock('@sim/audit', () => ({
3740} ) )
3841vi . mock ( '@/lib/posthog/server' , ( ) => ( { captureServerEvent : vi . fn ( ) } ) )
3942
40- import { DocCompileUserError } from '@/lib/copilot/tools/server/files/doc-compile-error '
43+ import { OrchestrationError } from '@/lib/core/orchestration/types '
4144import { GET } from '@/app/api/v1/files/[fileId]/route'
4245
4346const WORKSPACE_ID = 'ws-1'
4447const FILE_ID = 'file-1'
4548const context = { params : Promise . resolve ( { fileId : FILE_ID } ) }
4649
4750const DOCX_MIME = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
51+ const PRINCIPAL = {
52+ kind : 'personal_api_key' as const ,
53+ userId : 'user-1' ,
54+ keyId : 'key-1' ,
55+ }
4856
4957function request ( ) {
5058 return createMockRequest (
@@ -71,16 +79,31 @@ function generatedDocument(name = 'report.docx') {
7179 }
7280}
7381
82+ function renderedDownload ( buffer : Buffer ) {
83+ return {
84+ file : generatedDocument ( ) ,
85+ stream : new ReadableStream < Uint8Array > ( {
86+ start ( controller ) {
87+ controller . enqueue ( buffer )
88+ controller . close ( )
89+ } ,
90+ } ) ,
91+ contentLength : buffer . length ,
92+ contentType : DOCX_MIME ,
93+ }
94+ }
95+
7496describe ( 'v1 file download' , ( ) => {
7597 beforeEach ( ( ) => {
7698 vi . clearAllMocks ( )
77- mockCheckRateLimit . mockResolvedValue ( { allowed : true , userId : 'user-1' } )
99+ mockCheckRateLimit . mockResolvedValue ( {
100+ allowed : true ,
101+ userId : 'user-1' ,
102+ principal : PRINCIPAL ,
103+ } )
78104 mockValidateWorkspaceAccess . mockResolvedValue ( null )
79105 mockGetWorkspaceFile . mockResolvedValue ( generatedDocument ( ) )
80- mockFetchServableWorkspaceFileBuffer . mockResolvedValue ( {
81- buffer : Buffer . from ( 'PKrendered' ) ,
82- contentType : DOCX_MIME ,
83- } )
106+ mockDownloadWorkspaceFileStream . mockResolvedValue ( renderedDownload ( Buffer . from ( 'PKrendered' ) ) )
84107 } )
85108
86109 it ( 'serves the rendered bytes and the rendered content type' , async ( ) => {
@@ -104,19 +127,16 @@ describe('v1 file download', () => {
104127
105128 it ( 'reports Content-Length from the rendered bytes, not the declared source size' , async ( ) => {
106129 const rendered = Buffer . alloc ( 50_000 )
107- mockFetchServableWorkspaceFileBuffer . mockResolvedValue ( {
108- buffer : rendered ,
109- contentType : DOCX_MIME ,
110- } )
130+ mockDownloadWorkspaceFileStream . mockResolvedValue ( renderedDownload ( rendered ) )
111131
112132 const response = await GET ( request ( ) , context )
113133
114134 expect ( response . headers . get ( 'Content-Length' ) ) . toBe ( String ( rendered . length ) )
115135 } )
116136
117137 it ( 'returns a retryable 409 while the artifact is still compiling' , async ( ) => {
118- mockFetchServableWorkspaceFileBuffer . mockRejectedValue (
119- new DocCompileUserError ( 'Document is still being generated' )
138+ mockDownloadWorkspaceFileStream . mockRejectedValue (
139+ new OrchestrationError ( 'conflict' , 'Document is still being generated' )
120140 )
121141
122142 const response = await GET ( request ( ) , context )
@@ -127,11 +147,17 @@ describe('v1 file download', () => {
127147 } )
128148
129149 it ( '404s a file that does not exist' , async ( ) => {
130- mockGetWorkspaceFile . mockResolvedValue ( null )
150+ mockDownloadWorkspaceFileStream . mockRejectedValue (
151+ new OrchestrationError ( 'not_found' , 'File not found' )
152+ )
131153
132154 const response = await GET ( request ( ) , context )
133155
134156 expect ( response . status ) . toBe ( 404 )
135- expect ( mockFetchServableWorkspaceFileBuffer ) . not . toHaveBeenCalled ( )
157+ expect ( mockDownloadWorkspaceFileStream ) . toHaveBeenCalledWith ( {
158+ principal : PRINCIPAL ,
159+ input : { fileId : FILE_ID , assertedWorkspaceId : WORKSPACE_ID } ,
160+ request : expect . anything ( ) ,
161+ } )
136162 } )
137163} )
0 commit comments