1- import { describe , it , expect , vi , beforeEach } from 'vitest'
1+ import { describe , it , expect , vi , beforeEach , type MockedFunction } from 'vitest'
22import { createFileRoutes } from '../../src/routes/files'
33import { Hono } from 'hono'
44import type { ReadStream } from 'fs'
@@ -13,20 +13,6 @@ interface FileUploadResult {
1313 mimeType : string
1414}
1515
16- const getFile = vi . fn ( )
17- const getRawFileContent = vi . fn ( )
18- const getFileRange = vi . fn ( )
19- const uploadFile = vi . fn ( )
20- const createFileOrFolder = vi . fn ( )
21- const deleteFileOrFolder = vi . fn ( )
22- const renameOrMoveFile = vi . fn ( )
23- const applyFilePatches = vi . fn ( )
24-
25- const createDirectoryArchive = vi . fn ( )
26- const getArchiveSize = vi . fn ( )
27- const getArchiveStream = vi . fn ( )
28- const deleteArchive = vi . fn ( )
29-
3016vi . mock ( '../../src/utils/logger' , ( ) => ( {
3117 logger : {
3218 info : vi . fn ( ) ,
@@ -43,23 +29,37 @@ vi.mock('@opencode-manager/shared/config/env', () => ({
4329} ) )
4430
4531vi . mock ( '../../src/services/files' , ( ) => ( {
46- getFile,
47- getRawFileContent,
48- getFileRange,
49- uploadFile,
50- createFileOrFolder,
51- deleteFileOrFolder,
52- renameOrMoveFile,
53- applyFilePatches,
32+ getFile : vi . fn ( ) ,
33+ getRawFileContent : vi . fn ( ) ,
34+ getFileRange : vi . fn ( ) ,
35+ uploadFile : vi . fn ( ) ,
36+ createFileOrFolder : vi . fn ( ) ,
37+ deleteFileOrFolder : vi . fn ( ) ,
38+ renameOrMoveFile : vi . fn ( ) ,
39+ applyFilePatches : vi . fn ( ) ,
5440} ) )
5541
5642vi . mock ( '../../src/services/archive' , ( ) => ( {
57- createDirectoryArchive,
58- getArchiveSize,
59- getArchiveStream,
60- deleteArchive,
43+ createDirectoryArchive : vi . fn ( ) ,
44+ getArchiveSize : vi . fn ( ) ,
45+ getArchiveStream : vi . fn ( ) ,
46+ deleteArchive : vi . fn ( ) ,
6147} ) )
6248
49+ const getFile = fileService . getFile as MockedFunction < typeof fileService . getFile >
50+ const getRawFileContent = fileService . getRawFileContent as MockedFunction < typeof fileService . getRawFileContent >
51+ const getFileRange = fileService . getFileRange as MockedFunction < typeof fileService . getFileRange >
52+ const uploadFile = fileService . uploadFile as MockedFunction < typeof fileService . uploadFile >
53+ const createFileOrFolder = fileService . createFileOrFolder as MockedFunction < typeof fileService . createFileOrFolder >
54+ const deleteFileOrFolder = fileService . deleteFileOrFolder as MockedFunction < typeof fileService . deleteFileOrFolder >
55+ const renameOrMoveFile = fileService . renameOrMoveFile as MockedFunction < typeof fileService . renameOrMoveFile >
56+ const applyFilePatches = fileService . applyFilePatches as MockedFunction < typeof fileService . applyFilePatches >
57+
58+ const createDirectoryArchive = archiveService . createDirectoryArchive as MockedFunction < typeof archiveService . createDirectoryArchive >
59+ const getArchiveSize = archiveService . getArchiveSize as MockedFunction < typeof archiveService . getArchiveSize >
60+ const getArchiveStream = archiveService . getArchiveStream as MockedFunction < typeof archiveService . getArchiveStream >
61+ const deleteArchive = archiveService . deleteArchive as MockedFunction < typeof archiveService . deleteArchive >
62+
6363describe ( 'File Routes' , ( ) => {
6464 let app : Hono
6565 let filesApp : Hono
@@ -391,7 +391,9 @@ describe('File Routes', () => {
391391 } )
392392
393393 it ( 'should return error when deletion fails' , async ( ) => {
394- deleteFileOrFolder . mockRejectedValue ( new Error ( 'File not found' ) )
394+ const error = new Error ( 'File not found' ) as any
395+ error . statusCode = 404
396+ deleteFileOrFolder . mockRejectedValue ( error )
395397
396398 const response = await app . request ( '/api/files/test-repo/nonexistent.ts' , {
397399 method : 'DELETE' ,
@@ -436,7 +438,9 @@ describe('File Routes', () => {
436438 } )
437439
438440 it ( 'should return error when patch application fails' , async ( ) => {
439- applyFilePatches . mockRejectedValue ( new Error ( 'Invalid patch' ) )
441+ const error = new Error ( 'Invalid patch' ) as any
442+ error . statusCode = 404
443+ applyFilePatches . mockRejectedValue ( error )
440444
441445 const response = await app . request ( '/api/files/test-repo/test.ts' , {
442446 method : 'PATCH' ,
@@ -472,7 +476,9 @@ describe('File Routes', () => {
472476 } )
473477
474478 it ( 'should return error when rename fails' , async ( ) => {
475- renameOrMoveFile . mockRejectedValue ( new Error ( 'File not found' ) )
479+ const error = new Error ( 'File not found' ) as any
480+ error . statusCode = 404
481+ renameOrMoveFile . mockRejectedValue ( error )
476482
477483 const response = await app . request ( '/api/files/test-repo/nonexistent.ts' , {
478484 method : 'PATCH' ,
@@ -499,17 +505,19 @@ describe('File Routes', () => {
499505 }
500506
501507 it ( 'should reject path with ../ segments via service error' , async ( ) => {
502- getFile . mockRejectedValue ( { message : 'Path traversal detected' , statusCode : 403 } )
508+ const error = { message : 'Path traversal detected' , statusCode : 403 }
509+ getFile . mockRejectedValue ( error )
503510
504- const response = await app . request ( '/api/files/../.. /../etc/passwd' )
511+ const response = await app . request ( '/api/files/test-repo /../etc/passwd' )
505512 const body = await response . json ( ) as { error : string }
506513
507514 expect ( response . status ) . toBe ( 403 )
508515 expect ( body . error ) . toContain ( 'Path traversal detected' )
509516 } )
510517
511518 it ( 'should reject encoded path traversal attempts' , async ( ) => {
512- getFile . mockRejectedValue ( { message : 'Path traversal detected' , statusCode : 403 } )
519+ const error = { message : 'Path traversal detected' , statusCode : 403 }
520+ getFile . mockRejectedValue ( error )
513521
514522 const response = await app . request ( '/api/files/test-repo/..%2Fetc' )
515523 const body = await response . json ( ) as { error : string }
0 commit comments