Merge pull request #373 from kfiramar/fix/provider-connections-group-migration

Thanks @kfiramar! 🎉 Critical schema fix — the `group` column was used in all provider_connections queries but missing from the base schema and backfill migration. Databases upgraded from older versions were silently failing on group-related queries. Clean fix with regression test. Merged!
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-03-14 19:47:58 -03:00
committed by GitHub
2 changed files with 62 additions and 0 deletions

View File

@@ -80,6 +80,7 @@ const SCHEMA_SQL = `
consecutive_use_count INTEGER DEFAULT 0,
rate_limit_protection INTEGER DEFAULT 0,
last_used_at TEXT,
"group" TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
@@ -316,6 +317,10 @@ function ensureProviderConnectionsColumns(db: SqliteDatabase) {
db.exec("ALTER TABLE provider_connections ADD COLUMN last_used_at TEXT");
console.log("[DB] Added provider_connections.last_used_at column");
}
if (!columnNames.has("group")) {
db.exec('ALTER TABLE provider_connections ADD COLUMN "group" TEXT');
console.log('[DB] Added provider_connections."group" column');
}
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
console.warn("[DB] Failed to verify provider_connections schema:", message);

View File

@@ -141,6 +141,63 @@ test("provider connection persists rateLimitProtection across reopen", async ()
assert.equal(secondRead.rateLimitProtection, true);
});
test('provider connection migration adds "group" column for existing databases', async () => {
await resetStorage();
const sqlitePath = core.SQLITE_FILE;
core.resetDbInstance();
const Database = (await import("better-sqlite3")).default;
const db = new Database(sqlitePath);
db.exec(`
CREATE TABLE provider_connections (
id TEXT PRIMARY KEY,
provider TEXT NOT NULL,
auth_type TEXT,
name TEXT,
email TEXT,
priority INTEGER DEFAULT 0,
is_active INTEGER DEFAULT 1,
access_token TEXT,
refresh_token TEXT,
expires_at TEXT,
token_expires_at TEXT,
scope TEXT,
project_id TEXT,
test_status TEXT,
error_code TEXT,
last_error TEXT,
last_error_at TEXT,
last_error_type TEXT,
last_error_source TEXT,
backoff_level INTEGER DEFAULT 0,
rate_limited_until TEXT,
health_check_interval INTEGER,
last_health_check_at TEXT,
last_tested TEXT,
api_key TEXT,
id_token TEXT,
provider_specific_data TEXT,
expires_in INTEGER,
display_name TEXT,
global_priority INTEGER,
default_model TEXT,
token_type TEXT,
consecutive_use_count INTEGER DEFAULT 0,
rate_limit_protection INTEGER DEFAULT 0,
last_used_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
`);
db.close();
const reopened = core.getDbInstance();
const columns = reopened.prepare("PRAGMA table_info(provider_connections)").all();
const names = new Set(columns.map((column) => column.name));
assert.equal(names.has("group"), true);
});
test("resolveProxyForConnection applies combo proxy for object/string model entries", async () => {
await resetStorage();