Remove CLI from electron app (#17803)

Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
This commit is contained in:
Brendan Allan
2026-04-09 13:18:46 +08:00
committed by GitHub
parent 9c1c061b84
commit ee23043d64
38 changed files with 284 additions and 521 deletions

View File

@@ -19,7 +19,7 @@ afterEach(async () => {
describe("project.initGit endpoint", () => {
test("initializes git and reloads immediately", async () => {
await using tmp = await tmpdir()
const app = Server.Default()
const app = Server.Default().app
const seen: { directory?: string; payload: { type: string } }[] = []
const fn = (evt: { directory?: string; payload: { type: string } }) => {
seen.push(evt)
@@ -76,7 +76,7 @@ describe("project.initGit endpoint", () => {
test("does not reload when the project is already git", async () => {
await using tmp = await tmpdir({ git: true })
const app = Server.Default()
const app = Server.Default().app
const seen: { directory?: string; payload: { type: string } }[] = []
const fn = (evt: { directory?: string; payload: { type: string } }) => {
seen.push(evt)

View File

@@ -42,7 +42,7 @@ describe("session action routes", () => {
fn: async () => {
const session = await Session.create({})
const cancel = spyOn(SessionPrompt, "cancel").mockResolvedValue()
const app = Server.Default()
const app = Server.Default().app
const res = await app.request(`/session/${session.id}/abort`, {
method: "POST",
@@ -66,7 +66,7 @@ describe("session action routes", () => {
const msg = await user(session.id, "hello")
const busy = spyOn(SessionPrompt, "assertNotBusy").mockRejectedValue(new Session.BusyError(session.id))
const remove = spyOn(Session, "removeMessage").mockResolvedValue(msg.id)
const app = Server.Default()
const app = Server.Default().app
const res = await app.request(`/session/${session.id}/message/${msg.id}`, {
method: "DELETE",

View File

@@ -60,7 +60,7 @@ describe("session messages endpoint", () => {
fn: async () => {
const session = await Session.create({})
const ids = await fill(session.id, 5)
const app = Server.Default()
const app = Server.Default().app
const a = await app.request(`/session/${session.id}/message?limit=2`)
expect(a.status).toBe(200)
@@ -89,7 +89,7 @@ describe("session messages endpoint", () => {
fn: async () => {
const session = await Session.create({})
const ids = await fill(session.id, 3)
const app = Server.Default()
const app = Server.Default().app
const res = await app.request(`/session/${session.id}/message`)
expect(res.status).toBe(200)
@@ -109,7 +109,7 @@ describe("session messages endpoint", () => {
directory: tmp.path,
fn: async () => {
const session = await Session.create({})
const app = Server.Default()
const app = Server.Default().app
const bad = await app.request(`/session/${session.id}/message?limit=2&before=bad`)
expect(bad.status).toBe(400)
@@ -131,7 +131,7 @@ describe("session messages endpoint", () => {
fn: async () => {
const session = await Session.create({})
await fill(session.id, 520)
const app = Server.Default()
const app = Server.Default().app
const res = await app.request(`/session/${session.id}/message?limit=510`)
expect(res.status).toBe(200)

View File

@@ -21,7 +21,7 @@ describe("tui.selectSession endpoint", () => {
const session = await Session.create({})
// #when
const app = Server.Default()
const app = Server.Default().app
const response = await app.request("/tui/select-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -47,7 +47,7 @@ describe("tui.selectSession endpoint", () => {
const nonExistentSessionID = "ses_nonexistent123"
// #when
const app = Server.Default()
const app = Server.Default().app
const response = await app.request("/tui/select-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -69,7 +69,7 @@ describe("tui.selectSession endpoint", () => {
const invalidSessionID = "invalid_session_id"
// #when
const app = Server.Default()
const app = Server.Default().app
const response = await app.request("/tui/select-session", {
method: "POST",
headers: { "Content-Type": "application/json" },

View File

@@ -1,6 +1,6 @@
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
import { Database } from "bun:sqlite"
import { drizzle } from "drizzle-orm/bun-sqlite"
import { drizzle, SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
import path from "path"
import fs from "fs/promises"
@@ -89,18 +89,21 @@ function createTestDb() {
name: entry.name,
}))
.sort((a, b) => a.timestamp - b.timestamp)
migrate(drizzle({ client: sqlite }), migrations)
return sqlite
const db = drizzle({ client: sqlite })
migrate(db, migrations)
return [sqlite, db] as const
}
describe("JSON to SQLite migration", () => {
let storageDir: string
let sqlite: Database
let db: SQLiteBunDatabase
beforeEach(async () => {
storageDir = await setupStorageDir()
sqlite = createTestDb()
;[sqlite, db] = createTestDb()
})
afterEach(async () => {
@@ -118,11 +121,10 @@ describe("JSON to SQLite migration", () => {
sandboxes: ["/test/sandbox"],
})
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.projects).toBe(1)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1)
expect(projects[0].id).toBe(ProjectID.make("proj_test123abc"))
@@ -143,11 +145,10 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.projects).toBe(1)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1)
expect(projects[0].id).toBe(ProjectID.make("proj_filename")) // Uses filename, not JSON id
@@ -164,11 +165,10 @@ describe("JSON to SQLite migration", () => {
commands: { start: "npm run dev" },
})
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.projects).toBe(1)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1)
expect(projects[0].id).toBe(ProjectID.make("proj_with_commands"))
@@ -185,11 +185,10 @@ describe("JSON to SQLite migration", () => {
sandboxes: [],
})
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.projects).toBe(1)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1)
expect(projects[0].id).toBe(ProjectID.make("proj_no_commands"))
@@ -216,9 +215,8 @@ describe("JSON to SQLite migration", () => {
share: { url: "https://example.com/share" },
})
await JsonMigration.run(sqlite)
await JsonMigration.run(db)
const db = drizzle({ client: sqlite })
const sessions = db.select().from(SessionTable).all()
expect(sessions.length).toBe(1)
expect(sessions[0].id).toBe(SessionID.make("ses_test456def"))
@@ -247,12 +245,11 @@ describe("JSON to SQLite migration", () => {
JSON.stringify({ ...fixtures.part }),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.messages).toBe(1)
expect(stats?.parts).toBe(1)
const db = drizzle({ client: sqlite })
const messages = db.select().from(MessageTable).all()
expect(messages.length).toBe(1)
expect(messages[0].id).toBe(MessageID.make("msg_test789ghi"))
@@ -287,12 +284,11 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.messages).toBe(1)
expect(stats?.parts).toBe(1)
const db = drizzle({ client: sqlite })
const messages = db.select().from(MessageTable).all()
expect(messages.length).toBe(1)
expect(messages[0].id).toBe(MessageID.make("msg_test789ghi"))
@@ -329,11 +325,10 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.messages).toBe(1)
const db = drizzle({ client: sqlite })
const messages = db.select().from(MessageTable).all()
expect(messages.length).toBe(1)
expect(messages[0].id).toBe(MessageID.make("msg_from_filename")) // Uses filename, not JSON id
@@ -367,11 +362,10 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.parts).toBe(1)
const db = drizzle({ client: sqlite })
const parts = db.select().from(PartTable).all()
expect(parts.length).toBe(1)
expect(parts[0].id).toBe(PartID.make("prt_from_filename")) // Uses filename, not JSON id
@@ -392,7 +386,7 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.sessions).toBe(0)
})
@@ -420,11 +414,10 @@ describe("JSON to SQLite migration", () => {
time: { created: 1700000000000, updated: 1700000001000 },
})
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.sessions).toBe(1)
const db = drizzle({ client: sqlite })
const sessions = db.select().from(SessionTable).all()
expect(sessions.length).toBe(1)
expect(sessions[0].id).toBe(SessionID.make("ses_migrated"))
@@ -452,11 +445,10 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.sessions).toBe(1)
const db = drizzle({ client: sqlite })
const sessions = db.select().from(SessionTable).all()
expect(sessions.length).toBe(1)
expect(sessions[0].id).toBe(SessionID.make("ses_from_filename")) // Uses filename, not JSON id
@@ -471,10 +463,9 @@ describe("JSON to SQLite migration", () => {
sandboxes: [],
})
await JsonMigration.run(sqlite)
await JsonMigration.run(sqlite)
await JsonMigration.run(db)
await JsonMigration.run(db)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1) // Still only 1 due to onConflictDoNothing
})
@@ -507,11 +498,10 @@ describe("JSON to SQLite migration", () => {
]),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.todos).toBe(2)
const db = drizzle({ client: sqlite })
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
expect(todos.length).toBe(2)
expect(todos[0].content).toBe("First todo")
@@ -540,9 +530,8 @@ describe("JSON to SQLite migration", () => {
]),
)
await JsonMigration.run(sqlite)
await JsonMigration.run(db)
const db = drizzle({ client: sqlite })
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
expect(todos.length).toBe(3)
@@ -570,11 +559,10 @@ describe("JSON to SQLite migration", () => {
]
await Bun.write(path.join(storageDir, "permission", "proj_test123abc.json"), JSON.stringify(permissionData))
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.permissions).toBe(1)
const db = drizzle({ client: sqlite })
const permissions = db.select().from(PermissionTable).all()
expect(permissions.length).toBe(1)
expect(permissions[0].project_id).toBe("proj_test123abc")
@@ -600,11 +588,10 @@ describe("JSON to SQLite migration", () => {
}),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats?.shares).toBe(1)
const db = drizzle({ client: sqlite })
const shares = db.select().from(SessionShareTable).all()
expect(shares.length).toBe(1)
expect(shares[0].session_id).toBe("ses_test456def")
@@ -616,7 +603,7 @@ describe("JSON to SQLite migration", () => {
test("returns empty stats when storage directory does not exist", async () => {
await fs.rm(storageDir, { recursive: true, force: true })
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats.projects).toBe(0)
expect(stats.sessions).toBe(0)
@@ -637,12 +624,11 @@ describe("JSON to SQLite migration", () => {
})
await Bun.write(path.join(storageDir, "project", "broken.json"), "{ invalid json")
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats.projects).toBe(1)
expect(stats.errors.some((x) => x.includes("failed to read") && x.includes("broken.json"))).toBe(true)
const db = drizzle({ client: sqlite })
const projects = db.select().from(ProjectTable).all()
expect(projects.length).toBe(1)
expect(projects[0].id).toBe(ProjectID.make("proj_test123abc"))
@@ -666,10 +652,9 @@ describe("JSON to SQLite migration", () => {
]),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats.todos).toBe(2)
const db = drizzle({ client: sqlite })
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
expect(todos.length).toBe(2)
expect(todos[0].content).toBe("keep-0")
@@ -714,13 +699,12 @@ describe("JSON to SQLite migration", () => {
JSON.stringify({ id: "share_missing", secret: "secret", url: "https://missing.example.com" }),
)
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
expect(stats.todos).toBe(1)
expect(stats.permissions).toBe(1)
expect(stats.shares).toBe(1)
const db = drizzle({ client: sqlite })
expect(db.select().from(TodoTable).all().length).toBe(1)
expect(db.select().from(PermissionTable).all().length).toBe(1)
expect(db.select().from(SessionShareTable).all().length).toBe(1)
@@ -823,7 +807,7 @@ describe("JSON to SQLite migration", () => {
)
await Bun.write(path.join(storageDir, "session_share", "ses_broken.json"), "{ nope")
const stats = await JsonMigration.run(sqlite)
const stats = await JsonMigration.run(db)
// Projects: proj_test123abc (valid), proj_missing_id (now derives id from filename)
// Sessions: ses_test456def (valid), ses_missing_project (now uses dir path),
@@ -837,7 +821,6 @@ describe("JSON to SQLite migration", () => {
expect(stats.shares).toBe(1)
expect(stats.errors.length).toBeGreaterThanOrEqual(6)
const db = drizzle({ client: sqlite })
expect(db.select().from(ProjectTable).all().length).toBe(2)
expect(db.select().from(SessionTable).all().length).toBe(3)
expect(db.select().from(MessageTable).all().length).toBe(1)