feat(auth): implement port 51121 with OS fallback
Add port fallback logic to OAuth callback server: - Try port 51121 (ANTIGRAVITY_CALLBACK_PORT) first - Fallback to OS-assigned port on EADDRINUSE - Add redirectUri property to CallbackServerHandle - Return actual bound port in handle.port Add comprehensive port handling tests (5 new tests): - Should prefer port 51121 - Should return actual bound port - Should fallback when port occupied - Should cleanup and release port on close - Should provide redirect URI with actual port All 16 tests passing (11 existing + 5 new). 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
parent
534142da12
commit
e27bceb97e
@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
import { describe, it, expect, beforeEach, afterEach, mock } from "bun:test"
|
||||||
import { buildAuthURL, exchangeCode } from "./oauth"
|
import { buildAuthURL, exchangeCode, startCallbackServer } from "./oauth"
|
||||||
import { ANTIGRAVITY_CLIENT_ID, GOOGLE_TOKEN_URL } from "./constants"
|
import { ANTIGRAVITY_CLIENT_ID, GOOGLE_TOKEN_URL, ANTIGRAVITY_CALLBACK_PORT } from "./constants"
|
||||||
|
|
||||||
describe("OAuth PKCE Removal", () => {
|
describe("OAuth PKCE Removal", () => {
|
||||||
describe("buildAuthURL", () => {
|
describe("buildAuthURL", () => {
|
||||||
@ -188,4 +188,75 @@ describe("OAuth PKCE Removal", () => {
|
|||||||
expect(result1.state).not.toBe(result2.state)
|
expect(result1.state).not.toBe(result2.state)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("startCallbackServer Port Handling", () => {
|
||||||
|
it("should prefer port 51121", () => {
|
||||||
|
// #given
|
||||||
|
// Port 51121 should be free
|
||||||
|
|
||||||
|
// #when
|
||||||
|
const handle = startCallbackServer()
|
||||||
|
|
||||||
|
// #then
|
||||||
|
// If 51121 is available, should use it
|
||||||
|
// If not available, should use valid fallback
|
||||||
|
expect(handle.port).toBeGreaterThan(0)
|
||||||
|
expect(handle.port).toBeLessThan(65536)
|
||||||
|
handle.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return actual bound port", () => {
|
||||||
|
// #when
|
||||||
|
const handle = startCallbackServer()
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(typeof handle.port).toBe("number")
|
||||||
|
expect(handle.port).toBeGreaterThan(0)
|
||||||
|
handle.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fallback to OS-assigned port if 51121 is occupied (EADDRINUSE)", async () => {
|
||||||
|
// #given - Occupy port 51121 first
|
||||||
|
const blocker = Bun.serve({
|
||||||
|
port: ANTIGRAVITY_CALLBACK_PORT,
|
||||||
|
fetch: () => new Response("blocked")
|
||||||
|
})
|
||||||
|
|
||||||
|
try {
|
||||||
|
// #when
|
||||||
|
const handle = startCallbackServer()
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(handle.port).not.toBe(ANTIGRAVITY_CALLBACK_PORT)
|
||||||
|
expect(handle.port).toBeGreaterThan(0)
|
||||||
|
handle.close()
|
||||||
|
} finally {
|
||||||
|
// Cleanup blocker
|
||||||
|
blocker.stop()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should cleanup server on close", () => {
|
||||||
|
// #given
|
||||||
|
const handle = startCallbackServer()
|
||||||
|
const port = handle.port
|
||||||
|
|
||||||
|
// #when
|
||||||
|
handle.close()
|
||||||
|
|
||||||
|
// #then - port should be released (can bind again)
|
||||||
|
const testServer = Bun.serve({ port, fetch: () => new Response("test") })
|
||||||
|
expect(testServer.port).toBe(port)
|
||||||
|
testServer.stop()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should provide redirect URI with actual port", () => {
|
||||||
|
// #given
|
||||||
|
const handle = startCallbackServer()
|
||||||
|
|
||||||
|
// #then
|
||||||
|
expect(handle.redirectUri).toBe(`http://localhost:${handle.port}/oauth-callback`)
|
||||||
|
handle.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -148,6 +148,7 @@ export async function fetchUserInfo(
|
|||||||
|
|
||||||
export interface CallbackServerHandle {
|
export interface CallbackServerHandle {
|
||||||
port: number
|
port: number
|
||||||
|
redirectUri: string
|
||||||
waitForCallback: () => Promise<CallbackResult>
|
waitForCallback: () => Promise<CallbackResult>
|
||||||
close: () => void
|
close: () => void
|
||||||
}
|
}
|
||||||
@ -171,9 +172,7 @@ export function startCallbackServer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server = Bun.serve({
|
const fetchHandler = (request: Request): Response => {
|
||||||
port: 0,
|
|
||||||
fetch(request: Request): Response {
|
|
||||||
const url = new URL(request.url)
|
const url = new URL(request.url)
|
||||||
|
|
||||||
if (url.pathname === "/oauth-callback") {
|
if (url.pathname === "/oauth-callback") {
|
||||||
@ -204,10 +203,22 @@ export function startCallbackServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Response("Not Found", { status: 404 })
|
return new Response("Not Found", { status: 404 })
|
||||||
},
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server = Bun.serve({
|
||||||
|
port: ANTIGRAVITY_CALLBACK_PORT,
|
||||||
|
fetch: fetchHandler,
|
||||||
})
|
})
|
||||||
|
} catch (error) {
|
||||||
|
server = Bun.serve({
|
||||||
|
port: 0,
|
||||||
|
fetch: fetchHandler,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const actualPort = server.port as number
|
const actualPort = server.port as number
|
||||||
|
const redirectUri = `http://localhost:${actualPort}/oauth-callback`
|
||||||
|
|
||||||
const waitForCallback = (): Promise<CallbackResult> => {
|
const waitForCallback = (): Promise<CallbackResult> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -223,6 +234,7 @@ export function startCallbackServer(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
port: actualPort,
|
port: actualPort,
|
||||||
|
redirectUri,
|
||||||
waitForCallback,
|
waitForCallback,
|
||||||
close: cleanup,
|
close: cleanup,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user