@@ -31,8 +31,6 @@ const {
3131 mockFsWriteFile,
3232 mockJoin,
3333 actualPath,
34- mockFileExistsInWorkspace,
35- mockListWorkspaceFiles,
3634 mockUploadWorkspaceFile,
3735} = vi . hoisted ( ( ) => {
3836 // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -62,9 +60,19 @@ const {
6260 return actualPath . join ( ...args )
6361 } ) ,
6462 actualPath,
65- mockFileExistsInWorkspace : vi . fn ( ) . mockResolvedValue ( false ) ,
66- mockListWorkspaceFiles : vi . fn ( ) . mockResolvedValue ( [ ] ) ,
67- mockUploadWorkspaceFile : vi . fn ( ) . mockResolvedValue ( { } ) ,
63+ mockUploadWorkspaceFile : vi
64+ . fn ( )
65+ . mockImplementation (
66+ async ( workspaceId : string , _userId : string , _buffer : Buffer , fileName : string ) => ( {
67+ id : 'wf_test' ,
68+ name : fileName ,
69+ size : 0 ,
70+ type : 'application/octet-stream' ,
71+ url : `/api/files/serve/${ workspaceId } /${ fileName } ` ,
72+ key : `${ workspaceId } /${ fileName } ` ,
73+ context : 'workspace' ,
74+ } )
75+ ) ,
6876 }
6977} )
7078
@@ -110,9 +118,7 @@ vi.mock('@/lib/uploads/contexts/execution', () => ({
110118 uploadExecutionFile : vi . fn ( ) ,
111119} ) )
112120
113- vi . mock ( '@/lib/uploads/contexts/workspace' , ( ) => ( {
114- fileExistsInWorkspace : mockFileExistsInWorkspace ,
115- listWorkspaceFiles : mockListWorkspaceFiles ,
121+ vi . mock ( '@/lib/uploads/contexts/workspace/workspace-file-manager' , ( ) => ( {
116122 uploadWorkspaceFile : mockUploadWorkspaceFile ,
117123} ) )
118124
@@ -190,9 +196,7 @@ describe('File Parse API Route', () => {
190196 mockFsStat . mockResolvedValue ( { isFile : ( ) => true , size : 17 } )
191197 mockFsReadFile . mockResolvedValue ( Buffer . from ( 'test file content' ) )
192198 mockIsSupportedFileType . mockReturnValue ( true )
193- mockFileExistsInWorkspace . mockResolvedValue ( false )
194- mockListWorkspaceFiles . mockResolvedValue ( [ ] )
195- mockUploadWorkspaceFile . mockResolvedValue ( { } )
199+ mockUploadWorkspaceFile . mockClear ( )
196200 mockParseFile . mockResolvedValue ( {
197201 content : 'parsed content' ,
198202 metadata : { pageCount : 1 } ,
@@ -384,34 +388,32 @@ describe('File Parse API Route', () => {
384388 )
385389 } )
386390
387- it ( 'should preserve the full download cap when an external URL reuses a workspace file ' , async ( ) => {
391+ it ( 'should never dedup external URL fetches by path filename — two URLs sharing image.png both download ' , async ( ) => {
388392 inputValidationMockFns . mockValidateUrlWithDNS . mockResolvedValue ( {
389393 isValid : true ,
390394 resolvedIP : '203.0.113.10' ,
391395 } )
392- inputValidationMockFns . mockSecureFetchWithPinnedIP . mockResolvedValue (
393- new Response ( 'file content' , {
394- status : 200 ,
395- headers : { 'content-type' : 'text/plain' } ,
396- } )
397- )
398- mockFileExistsInWorkspace . mockResolvedValueOnce ( false ) . mockResolvedValueOnce ( true )
399- mockListWorkspaceFiles . mockResolvedValueOnce ( [
400- { name : 'file2.txt' , key : 'workspace-file2.txt' } ,
401- ] )
402-
403- mockParseBuffer
404- . mockResolvedValueOnce ( {
405- content : 'a' . repeat ( 4 * 1024 * 1024 ) ,
406- metadata : { pageCount : 1 } ,
407- } )
408- . mockResolvedValueOnce ( {
409- content : 'second file' ,
410- metadata : { pageCount : 1 } ,
411- } )
396+ inputValidationMockFns . mockSecureFetchWithPinnedIP
397+ . mockResolvedValueOnce (
398+ new Response ( 'first image bytes' , {
399+ status : 200 ,
400+ headers : { 'content-type' : 'image/png' } ,
401+ } )
402+ )
403+ . mockResolvedValueOnce (
404+ new Response ( 'second image bytes — different content' , {
405+ status : 200 ,
406+ headers : { 'content-type' : 'image/png' } ,
407+ } )
408+ )
409+ mockIsSupportedFileType . mockReturnValue ( false )
410+ permissionsMockFns . mockGetUserEntityPermissions . mockResolvedValue ( 'write' )
412411
413412 const req = createMockRequest ( 'POST' , {
414- filePath : [ 'https://example.com/file1.txt' , 'https://example.com/file2.txt' ] ,
413+ filePath : [
414+ 'https://files.slack.com/files-pri/T07-FAAA/download/image.png' ,
415+ 'https://files.slack.com/files-pri/T07-FBBB/download/image.png' ,
416+ ] ,
415417 workspaceId : 'workspace-id' ,
416418 } )
417419
@@ -420,9 +422,21 @@ describe('File Parse API Route', () => {
420422
421423 expect ( response . status ) . toBe ( 200 )
422424 expect ( data . results ) . toHaveLength ( 2 )
423- expect ( storageServiceMockFns . mockDownloadFile ) . toHaveBeenCalledWith (
424- expect . objectContaining ( { key : 'workspace-file2.txt' , maxBytes : 100 * 1024 * 1024 } )
425+ expect ( inputValidationMockFns . mockSecureFetchWithPinnedIP ) . toHaveBeenCalledTimes ( 2 )
426+ expect ( inputValidationMockFns . mockSecureFetchWithPinnedIP ) . toHaveBeenNthCalledWith (
427+ 1 ,
428+ 'https://files.slack.com/files-pri/T07-FAAA/download/image.png' ,
429+ '203.0.113.10' ,
430+ expect . any ( Object )
431+ )
432+ expect ( inputValidationMockFns . mockSecureFetchWithPinnedIP ) . toHaveBeenNthCalledWith (
433+ 2 ,
434+ 'https://files.slack.com/files-pri/T07-FBBB/download/image.png' ,
435+ '203.0.113.10' ,
436+ expect . any ( Object )
425437 )
438+ expect ( mockUploadWorkspaceFile ) . toHaveBeenCalledTimes ( 2 )
439+ expect ( storageServiceMockFns . mockDownloadFile ) . not . toHaveBeenCalled ( )
426440 } )
427441
428442 it ( 'should stop multi-file parsing once the combined parsed output is too large' , async ( ) => {
0 commit comments