11import { Command } from 'commander'
2- import { Hono } from 'hono'
32import { describe , it , expect , vi , beforeEach , afterEach } from 'vitest'
3+ import { execFile } from 'node:child_process'
4+ import { mkdirSync , mkdtempSync , writeFileSync } from 'node:fs'
5+ import { tmpdir } from 'node:os'
6+ import { join } from 'node:path'
47
58// Mock dependencies
6- vi . mock ( 'node:fs' , ( ) => ( {
7- existsSync : vi . fn ( ) ,
8- realpathSync : vi . fn ( ) ,
9- } ) )
10-
11- vi . mock ( 'node:path' , ( ) => ( {
12- extname : vi . fn ( ) ,
13- resolve : vi . fn ( ) ,
14- } ) )
15-
16- vi . mock ( 'node:url' , ( ) => ( {
17- pathToFileURL : vi . fn ( ) ,
18- } ) )
19-
20- vi . mock ( 'esbuild' , ( ) => ( {
21- build : vi . fn ( ) ,
22- } ) )
23-
249vi . mock ( '@hono/node-server' , ( ) => ( {
2510 serve : vi . fn ( ) ,
2611} ) )
@@ -44,6 +29,7 @@ import { serveCommand } from './index.js'
4429
4530describe ( 'serveCommand' , ( ) => {
4631 let program : Command
32+ let mockEsbuild : any
4733 let mockModules : any
4834 let mockServe : any
4935 let mockShowRoutes : any
@@ -53,15 +39,6 @@ describe('serveCommand', () => {
5339 program = new Command ( )
5440 serveCommand ( program )
5541
56- // Get mocked modules
57- mockModules = {
58- existsSync : vi . mocked ( ( await import ( 'node:fs' ) ) . existsSync ) ,
59- realpathSync : vi . mocked ( ( await import ( 'node:fs' ) ) . realpathSync ) ,
60- extname : vi . mocked ( ( await import ( 'node:path' ) ) . extname ) ,
61- resolve : vi . mocked ( ( await import ( 'node:path' ) ) . resolve ) ,
62- pathToFileURL : vi . mocked ( ( await import ( 'node:url' ) ) . pathToFileURL ) ,
63- }
64-
6542 mockServe = vi . mocked ( ( await import ( '@hono/node-server' ) ) . serve )
6643 mockShowRoutes = vi . mocked ( ( await import ( 'hono/dev' ) ) . showRoutes )
6744
@@ -81,11 +58,6 @@ describe('serveCommand', () => {
8158 } )
8259
8360 it ( 'should start server with default port' , async ( ) => {
84- mockModules . existsSync . mockReturnValue ( false )
85- mockModules . resolve . mockImplementation ( ( cwd : string , path : string ) => {
86- return `${ cwd } /${ path } `
87- } )
88-
8961 await program . parseAsync ( [ 'node' , 'test' , 'serve' ] )
9062
9163 // Verify serve was called with default port 7070
@@ -99,11 +71,6 @@ describe('serveCommand', () => {
9971 } )
10072
10173 it ( 'should start server with custom port' , async ( ) => {
102- mockModules . existsSync . mockReturnValue ( false )
103- mockModules . resolve . mockImplementation ( ( cwd : string , path : string ) => {
104- return `${ cwd } /${ path } `
105- } )
106-
10774 await program . parseAsync ( [ 'node' , 'test' , 'serve' , '-p' , '8080' ] )
10875
10976 // Verify serve was called with custom port
@@ -117,25 +84,39 @@ describe('serveCommand', () => {
11784 } )
11885
11986 it ( 'should serve app that responds correctly to requests' , async ( ) => {
120- const mockApp = new Hono ( )
121- mockApp . get ( '/' , ( c ) => c . text ( 'Hello World' ) )
122- mockApp . get ( '/api' , ( c ) => c . json ( { message : 'API response' } ) )
123-
124- const expectedPath = 'app.js'
125- const absolutePath = `${ process . cwd ( ) } /${ expectedPath } `
126-
127- mockModules . existsSync . mockReturnValue ( true )
128- mockModules . realpathSync . mockReturnValue ( absolutePath )
129- mockModules . extname . mockReturnValue ( '.js' )
130- mockModules . resolve . mockImplementation ( ( cwd : string , path : string ) => {
131- return `${ cwd } /${ path } `
132- } )
133- mockModules . pathToFileURL . mockReturnValue ( new URL ( `file://${ absolutePath } ` ) )
87+ const appDir = mkdtempSync ( join ( tmpdir ( ) , 'hono-cli-serve-test' ) )
88+ mkdirSync ( appDir , { recursive : true } )
89+ const appFile = join ( appDir , 'app.ts' )
90+ writeFileSync (
91+ appFile ,
92+ `
93+ import { Hono } from 'hono'
94+
95+ const app = new Hono()
96+ app.get('/', (c) => c.text('Hello World'))
97+ app.get('/api', (c) => c.json({ message: 'API response' }))
13498
135- // Mock the import of JS file
136- vi . doMock ( absolutePath , ( ) => ( { default : mockApp } ) )
99+ export default app
100+ `
101+ )
102+ writeFileSync (
103+ join ( appDir , 'package.json' ) ,
104+ JSON . stringify ( {
105+ type : 'module' ,
106+ dependencies : {
107+ hono : 'latest' ,
108+ } ,
109+ } )
110+ )
111+ process . chdir ( appDir )
112+ await new Promise < void > ( ( resolve ) => {
113+ const child = execFile ( 'npm' , [ 'install' ] )
114+ child . on ( 'exit' , ( ) => {
115+ resolve ( )
116+ } )
117+ } )
137118
138- await program . parseAsync ( [ 'node' , 'test' , 'serve' , 'app.js' ] )
119+ await program . parseAsync ( [ 'node' , 'test' , 'serve' , appFile ] )
139120
140121 // Test the captured fetch function
141122 const rootRequest = new Request ( 'http://localhost:7070/' )
@@ -149,11 +130,6 @@ describe('serveCommand', () => {
149130 } )
150131
151132 it ( 'should return 404 for non-existent routes when no app file exists' , async ( ) => {
152- mockModules . existsSync . mockReturnValue ( false )
153- mockModules . resolve . mockImplementation ( ( cwd : string , path : string ) => {
154- return `${ cwd } /${ path } `
155- } )
156-
157133 await program . parseAsync ( [ 'node' , 'test' , 'serve' ] )
158134
159135 // Test 404 behavior with default empty app
@@ -181,11 +157,6 @@ describe('serveCommand', () => {
181157 } )
182158
183159 it ( 'should handle typical use case: basicAuth + proxy to ramen-api.dev' , async ( ) => {
184- mockModules . existsSync . mockReturnValue ( false )
185- mockModules . resolve . mockImplementation ( ( cwd : string , path : string ) => {
186- return `${ cwd } /${ path } `
187- } )
188-
189160 // Mock basicAuth middleware
190161 const mockBasicAuth = vi . fn ( ) . mockImplementation ( ( ) => {
191162 return async ( c : any , next : any ) => {
0 commit comments