History

Merge remote-tracking branch 'origin/codex/batch14-local-db-migrate-connections' into codex/effect-lint-integration

Rhys Sullivan committed 6 months ago

Commit 6a9df082 parents d8abc3d 78035f22 files · +33 −8Browse files

Unified · Split

2 files
  1. apps/local/src/server/db-upgrade.ts +2 −0
  2. apps/local/src/server/migrate-connections.test.ts +31 −8
apps/local/src/server/db-upgrade.ts+2−0
@@ -21,6 +21,7 @@ import * as fs from "node:fs"; · 20 unmodified lines
2121 export const isPreScopeSchema = (dbPath: string): boolean => {
2222 if (!fs.existsSync(dbPath)) return false;
2323 const db = new Database(dbPath, { readonly: true });
24+ // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: local SQLite schema probe must close the DB handle
2425 try {
2526 const tableExists = db
2627 .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='source'")
@@ -73,6 +74,7 @@ export interface LegacySecret { · 46 unmodified lines
7374 export const readLegacySecrets = (dbPath: string): readonly LegacySecret[] => {
7475 if (!fs.existsSync(dbPath)) return [];
7576 const db = new Database(dbPath, { readonly: true });
77+ // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: local SQLite legacy-row read must close the DB handle
7678 try {
7779 const tableExists = db
7880 .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='secret'")
apps/local/src/server/migrate-connections.test.ts+31−8
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "@effect/vitest"; · 1 unmodified line
22 import { Database } from "bun:sqlite";
33 import { migrate } from "drizzle-orm/bun-sqlite/migrator";
44 import { drizzle } from "drizzle-orm/bun-sqlite";
5+import { Schema } from "effect";
56 import { mkdtempSync, rmSync } from "node:fs";
67 import { tmpdir } from "node:os";
78 import { join } from "node:path";
@@ -9,15 +10,26 @@ import { join } from "node:path"; · 1 unmodified line
910 import { migrateLegacyConnections } from "./migrate-connections";
1011
1112 let workDir: string;
13+let databases: Array<Database>;
1214
1315 beforeEach(() => {
1416 workDir = mkdtempSync(join(tmpdir(), "executor-migrate-connections-"));
17+ databases = [];
1518 });
1619
1720 afterEach(() => {
21+ for (const db of databases) {
22+ db.close();
23+ }
1824 rmSync(workDir, { recursive: true, force: true });
1925 });
2026
27+const openDatabase = (): Database => {
28+ const db = new Database(join(workDir, "data.db"));
29+ databases.push(db);
30+ return db;
31+};
32+
2133 const columnNames = (db: Database, table: string): ReadonlyArray<string> =>
2234 (
2335 db.prepare(`PRAGMA table_info('${table}')`).all() as ReadonlyArray<{
@@ -25,9 +37,24 @@ const columnNames = (db: Database, table: string): ReadonlyArray<string> => · 1 unmodified line
2537 }>
2638 ).map((column) => column.name);
2739
40+const MigratedMcpConfig = Schema.Struct({
41+ auth: Schema.optional(Schema.Unknown),
42+});
43+const decodeMigratedMcpConfig = Schema.decodeUnknownSync(
44+ Schema.fromJsonString(MigratedMcpConfig),
45+);
46+
47+const MigratedOpenApiOAuth2 = Schema.Struct({
48+ kind: Schema.Literal("oauth2"),
49+ connectionId: Schema.String,
50+});
51+const decodeMigratedOpenApiOAuth2 = Schema.decodeUnknownSync(
52+ Schema.fromJsonString(MigratedOpenApiOAuth2),
53+);
54+
2855 describe("migrateLegacyConnections", () => {
2956 it("backfills legacy MCP OAuth rows after connection.kind has been dropped", async () => {
30− const db = new Database(join(workDir, "data.db"));
57+ const db = openDatabase();
3158 migrate(drizzle(db), {
3259 migrationsFolder: join(import.meta.dirname, "../../drizzle"),
3360 });
@@ -98,7 +125,7 @@ describe("migrateLegacyConnections", () => { · 64 unmodified lines
98125 readonly auth_kind: string;
99126 readonly auth_connection_id: string;
100127 };
101− expect(JSON.parse(source.config).auth).toBeUndefined();
128+ expect(decodeMigratedMcpConfig(source.config).auth).toBeUndefined();
102129 expect(source.auth_kind).toBe("oauth2");
103130 expect(source.auth_connection_id).toBe("mcp-oauth2-remote-mcp");
104131
@@ -117,12 +144,10 @@ describe("migrateLegacyConnections", () => { · 12 unmodified lines
117144 owned_by_connection_id: "mcp-oauth2-remote-mcp",
118145 },
119146 ]);
120−
121− db.close();
122147 });
123148
124149 it("backfills legacy OpenAPI OAuth from oauth2 column after invocation_config has been dropped", async () => {
125− const db = new Database(join(workDir, "data.db"));
150+ const db = openDatabase();
126151 migrate(drizzle(db), {
127152 migrationsFolder: join(import.meta.dirname, "../../drizzle"),
128153 });
@@ -196,10 +221,8 @@ describe("migrateLegacyConnections", () => { · 67 unmodified lines
196221 const source = db
197222 .prepare("SELECT oauth2 FROM openapi_source WHERE scope_id = ? AND id = ?")
198223 .get("scope-1", "legacy-openapi") as { readonly oauth2: string };
199− const oauth2 = JSON.parse(source.oauth2);
224+ const oauth2 = decodeMigratedOpenApiOAuth2(source.oauth2);
200225 expect(oauth2.kind).toBe("oauth2");
201226 expect(oauth2.connectionId).toBe(connection?.id);
202−
203− db.close();
204227 });
205228 });