Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/commands/serve/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ describe('serveCommand', () => {
)
})

it('should start server with custom port 0 by allocating a ephemeral port', async () => {
mockModules.existsSync.mockReturnValue(false)
mockModules.resolve.mockImplementation((cwd: string, path: string) => {
return `${cwd}/${path}`
})

await program.parseAsync(['node', 'test', 'serve', '-p', '0'])

expect(mockServe).toHaveBeenCalledWith(
Copy link
Copy Markdown
Contributor Author

@ysknsid25 ysknsid25 Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I implement mockserve to be more like serve??🤔
For example,

  • When mock receives 0, mock returns 12345.
  • When mock receives others port number, mock returns itself.

expect.objectContaining({
fetch: expect.any(Function),
port: 0,
}),
expect.any(Function)
)
})

it('should serve app that responds correctly to requests', async () => {
const mockApp = new Hono()
mockApp.get('/', (c) => c.text('Hello World'))
Expand Down Expand Up @@ -256,4 +273,34 @@ describe('serveCommand', () => {
expect(unknownResponse.status).toBe(404)
expect(await unknownResponse.text()).toBe('API endpoint not found')
})

describe('port validation', () => {
it.each(['-1', '65536', '2048GB', 'invalid'])(
'should warn and use default port when port is %s',
async (port) => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})

mockModules.existsSync.mockReturnValue(false)
mockModules.resolve.mockImplementation((cwd: string, path: string) => {
return `${cwd}/${path}`
})

await program.parseAsync(['node', 'test', 'serve', '-p', port])

expect(consoleWarnSpy).toHaveBeenCalledWith(
'Port must be a number between 0 and 65535. Using default port 7070.\n'
)

expect(mockServe).toHaveBeenCalledWith(
expect.objectContaining({
fetch: expect.any(Function),
port: 7070,
}),
expect.any(Function)
)

consoleWarnSpy.mockRestore()
}
)
})
})
13 changes: 10 additions & 3 deletions src/commands/serve/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ export function serveCommand(program: Command) {
.command('serve')
.description('Start server')
.argument('[entry]', 'entry file')
.option('-p, --port <port>', 'port number')
.option('-p, --port <port>', 'port number', (value) => {
const parsed = parseInt(value, 10) // 2048GB will be 2048. So we need to check the original value which consists of only digits.
if (!/^\d+$/.test(value) || parsed < 0 || parsed > 65535) {
console.warn('Port must be a number between 0 and 65535. Using default port 7070.\n')
return 7070
}
return parsed
})
.option('--show-routes', 'show registered routes')
.option(
'--use <middleware>',
Expand All @@ -33,7 +40,7 @@ export function serveCommand(program: Command) {
.action(
async (
entry: string | undefined,
options: { port?: string; showRoutes?: boolean; use?: string[] }
options: { port?: number; showRoutes?: boolean; use?: string[] }
) => {
let app: Hono

Expand Down Expand Up @@ -94,7 +101,7 @@ export function serveCommand(program: Command) {
serve(
{
fetch: baseApp.fetch,
port: options.port ? Number.parseInt(options.port) : 7070,
port: options.port ?? 7070,
},
(info) => {
console.log(`Listening on http://localhost:${info.port}`)
Expand Down