| @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from "@effect/vitest"; · 1 unmodified line |
| 2 | import { Database } from "bun:sqlite"; | 2 | import { Database } from "bun:sqlite"; |
| 3 | import { migrate } from "drizzle-orm/bun-sqlite/migrator"; | 3 | import { migrate } from "drizzle-orm/bun-sqlite/migrator"; |
| 4 | import { drizzle } from "drizzle-orm/bun-sqlite"; | 4 | import { drizzle } from "drizzle-orm/bun-sqlite"; |
| | 5 | import { Schema } from "effect"; |
| 5 | import { mkdtempSync, rmSync } from "node:fs"; | 6 | import { mkdtempSync, rmSync } from "node:fs"; |
| 6 | import { tmpdir } from "node:os"; | 7 | import { tmpdir } from "node:os"; |
| 7 | import { join } from "node:path"; | 8 | import { join } from "node:path"; |
| @@ -9,15 +10,26 @@ import { join } from "node:path"; · 1 unmodified line |
| 9 | import { migrateLegacyConnections } from "./migrate-connections"; | 10 | import { migrateLegacyConnections } from "./migrate-connections"; |
| 10 | | 11 | |
| 11 | let workDir: string; | 12 | let workDir: string; |
| | 13 | let databases: Array<Database>; |
| 12 | | 14 | |
| 13 | beforeEach(() => { | 15 | beforeEach(() => { |
| 14 | workDir = mkdtempSync(join(tmpdir(), "executor-migrate-connections-")); | 16 | workDir = mkdtempSync(join(tmpdir(), "executor-migrate-connections-")); |
| | 17 | databases = []; |
| 15 | }); | 18 | }); |
| 16 | | 19 | |
| 17 | afterEach(() => { | 20 | afterEach(() => { |
| | 21 | for (const db of databases) { |
| | 22 | db.close(); |
| | 23 | } |
| 18 | rmSync(workDir, { recursive: true, force: true }); | 24 | rmSync(workDir, { recursive: true, force: true }); |
| 19 | }); | 25 | }); |
| 20 | | 26 | |
| | 27 | const openDatabase = (): Database => { |
| | 28 | const db = new Database(join(workDir, "data.db")); |
| | 29 | databases.push(db); |
| | 30 | return db; |
| | 31 | }; |
| | 32 | |
| 21 | const columnNames = (db: Database, table: string): ReadonlyArray<string> => | 33 | const columnNames = (db: Database, table: string): ReadonlyArray<string> => |
| 22 | ( | 34 | ( |
| 23 | db.prepare(`PRAGMA table_info('${table}')`).all() as ReadonlyArray<{ | 35 | db.prepare(`PRAGMA table_info('${table}')`).all() as ReadonlyArray<{ |
| @@ -25,9 +37,24 @@ const columnNames = (db: Database, table: string): ReadonlyArray<string> => · 1 unmodified line |
| 25 | }> | 37 | }> |
| 26 | ).map((column) => column.name); | 38 | ).map((column) => column.name); |
| 27 | | 39 | |
| | 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 | |
| 28 | describe("migrateLegacyConnections", () => { | 55 | describe("migrateLegacyConnections", () => { |
| 29 | it("backfills legacy MCP OAuth rows after connection.kind has been dropped", async () => { | 56 | 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(); |
| 31 | migrate(drizzle(db), { | 58 | migrate(drizzle(db), { |
| 32 | migrationsFolder: join(import.meta.dirname, "../../drizzle"), | 59 | migrationsFolder: join(import.meta.dirname, "../../drizzle"), |
| 33 | }); | 60 | }); |
| @@ -98,7 +125,7 @@ describe("migrateLegacyConnections", () => { · 64 unmodified lines |
| 98 | readonly auth_kind: string; | 125 | readonly auth_kind: string; |
| 99 | readonly auth_connection_id: string; | 126 | readonly auth_connection_id: string; |
| 100 | }; | 127 | }; |
| 101 | expect(JSON.parse(source.config).auth).toBeUndefined(); | 128 | expect(decodeMigratedMcpConfig(source.config).auth).toBeUndefined(); |
| 102 | expect(source.auth_kind).toBe("oauth2"); | 129 | expect(source.auth_kind).toBe("oauth2"); |
| 103 | expect(source.auth_connection_id).toBe("mcp-oauth2-remote-mcp"); | 130 | expect(source.auth_connection_id).toBe("mcp-oauth2-remote-mcp"); |
| 104 | | 131 | |
| @@ -117,12 +144,10 @@ describe("migrateLegacyConnections", () => { · 12 unmodified lines |
| 117 | owned_by_connection_id: "mcp-oauth2-remote-mcp", | 144 | owned_by_connection_id: "mcp-oauth2-remote-mcp", |
| 118 | }, | 145 | }, |
| 119 | ]); | 146 | ]); |
| 120 | | | |
| 121 | db.close(); | | |
| 122 | }); | 147 | }); |
| 123 | | 148 | |
| 124 | it("backfills legacy OpenAPI OAuth from oauth2 column after invocation_config has been dropped", async () => { | 149 | 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(); |
| 126 | migrate(drizzle(db), { | 151 | migrate(drizzle(db), { |
| 127 | migrationsFolder: join(import.meta.dirname, "../../drizzle"), | 152 | migrationsFolder: join(import.meta.dirname, "../../drizzle"), |
| 128 | }); | 153 | }); |
| @@ -196,10 +221,8 @@ describe("migrateLegacyConnections", () => { · 67 unmodified lines |
| 196 | const source = db | 221 | const source = db |
| 197 | .prepare("SELECT oauth2 FROM openapi_source WHERE scope_id = ? AND id = ?") | 222 | .prepare("SELECT oauth2 FROM openapi_source WHERE scope_id = ? AND id = ?") |
| 198 | .get("scope-1", "legacy-openapi") as { readonly oauth2: string }; | 223 | .get("scope-1", "legacy-openapi") as { readonly oauth2: string }; |
| 199 | const oauth2 = JSON.parse(source.oauth2); | 224 | const oauth2 = decodeMigratedOpenApiOAuth2(source.oauth2); |
| 200 | expect(oauth2.kind).toBe("oauth2"); | 225 | expect(oauth2.kind).toBe("oauth2"); |
| 201 | expect(oauth2.connectionId).toBe(connection?.id); | 226 | expect(oauth2.connectionId).toBe(connection?.id); |
| 202 | | | |
| 203 | db.close(); | | |
| 204 | }); | 227 | }); |
| 205 | }); | 228 | }); |