ci: add pull request quality gates
CI / install-and-build (pull_request) Successful in 1m29s
CI / format (pull_request) Successful in 51s
CI / typecheck-source (pull_request) Successful in 41s
CI / typecheck-tests (pull_request) Successful in 39s
CI / test (pull_request) Successful in 51s
CI / lint (pull_request) Successful in 21s
CI / install-and-build (pull_request) Successful in 1m29s
CI / format (pull_request) Successful in 51s
CI / typecheck-source (pull_request) Successful in 41s
CI / typecheck-tests (pull_request) Successful in 39s
CI / test (pull_request) Successful in 51s
CI / lint (pull_request) Successful in 21s
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
This commit was merged in pull request #12.
This commit is contained in:
@@ -3,7 +3,11 @@ import assert from "node:assert/strict";
|
||||
import { createServer } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { ApiInterface } from "../../../src/db/api/ApiInterface.js";
|
||||
import { apiProtocols, isApiUrl, parseApiUrl } from "../../../src/db/api/url.js";
|
||||
import {
|
||||
apiProtocols,
|
||||
isApiUrl,
|
||||
parseApiUrl,
|
||||
} from "../../../src/db/api/url.js";
|
||||
import {
|
||||
NotFoundAbodeError,
|
||||
NotAuthorizedAbodeError,
|
||||
@@ -86,7 +90,7 @@ describe("parseApiUrl", () => {
|
||||
|
||||
it("extra query params become headers", () => {
|
||||
const [, { headers }] = parseApiUrl(
|
||||
"http://example.com?X-Custom-Header=value"
|
||||
"http://example.com?X-Custom-Header=value",
|
||||
);
|
||||
assert.equal(headers["X-Custom-Header"], "value");
|
||||
});
|
||||
@@ -94,7 +98,7 @@ describe("parseApiUrl", () => {
|
||||
it("throws for non-api protocol", () => {
|
||||
assert.throws(
|
||||
() => parseApiUrl("sqlite:///db.sqlite"),
|
||||
/Not an \{abode\+,\}http\{s,\}: protocol/
|
||||
/Not an \{abode\+,\}http\{s,\}: protocol/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -106,18 +110,22 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
|
||||
before(async () => {
|
||||
let nextStatus = 500;
|
||||
respondWith = (s) => { nextStatus = s; };
|
||||
respondWith = (s) => {
|
||||
nextStatus = s;
|
||||
};
|
||||
|
||||
const server = createServer((req, res) => {
|
||||
res.writeHead(nextStatus, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ ok: false, error: "test" }));
|
||||
});
|
||||
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
await new Promise<void>((resolve) =>
|
||||
server.listen(0, "127.0.0.1", resolve),
|
||||
);
|
||||
const { port } = server.address() as AddressInfo;
|
||||
serverUrl = `http://127.0.0.1:${port}`;
|
||||
closeServer = () =>
|
||||
new Promise<void>((resolve, reject) =>
|
||||
server.close((err) => (err ? reject(err) : resolve()))
|
||||
server.close((err) => (err ? reject(err) : resolve())),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -131,7 +139,7 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
(err) => {
|
||||
assert.ok(err instanceof NotFoundAbodeError);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -143,7 +151,7 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
(err) => {
|
||||
assert.ok(err instanceof NotAuthorizedAbodeError);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -155,7 +163,7 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
(err) => {
|
||||
assert.ok(err instanceof ReadonlyAbodeError);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -167,7 +175,7 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
(err) => {
|
||||
assert.ok(err instanceof InvalidAbodeError);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -179,7 +187,7 @@ describe("ApiInterface HTTP error mapping", () => {
|
||||
(err) => {
|
||||
assert.ok(err instanceof ConflictAbodeError);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -92,7 +92,11 @@ describe("api backend: auth over HTTP", async () => {
|
||||
const self = await fetch(`${server.url}/auth/self`, {
|
||||
headers: { Cookie: `abode_session=${cookie}` },
|
||||
});
|
||||
assert.equal(self.status, 401, "session was invalidated server-side, not just the cookie cleared");
|
||||
assert.equal(
|
||||
self.status,
|
||||
401,
|
||||
"session was invalidated server-side, not just the cookie cleared",
|
||||
);
|
||||
});
|
||||
|
||||
it("POST /auth/clear-sessions invalidates outstanding session cookies", async () => {
|
||||
|
||||
@@ -10,10 +10,10 @@ import { runAuthTests } from "../../shared/auth.js";
|
||||
|
||||
async function createExpiredApikey(
|
||||
db: BackendDbInterface,
|
||||
uid: string
|
||||
uid: string,
|
||||
): Promise<`at_${string}`> {
|
||||
const si = db as SqliteInterface;
|
||||
const token = (`at_${"e".repeat(32)}`) as `at_${string}`;
|
||||
const token = `at_${"e".repeat(32)}` as `at_${string}`;
|
||||
const kid = crypto.randomUUID();
|
||||
const { sql } = si._;
|
||||
si._.db.run(sql`
|
||||
|
||||
@@ -46,7 +46,7 @@ describe("SqliteMigrator", () => {
|
||||
assert.equal(applied.length, 3);
|
||||
assert.deepEqual(
|
||||
applied.map((m) => m.id),
|
||||
[1, 2, 3]
|
||||
[1, 2, 3],
|
||||
);
|
||||
db.destroy();
|
||||
});
|
||||
@@ -66,7 +66,7 @@ describe("SqliteMigrator", () => {
|
||||
const migrator = new SqliteMigrator(db);
|
||||
await assert.rejects(
|
||||
() => migrator.migrateTo(9999),
|
||||
/No known migration with id 9999/
|
||||
/No known migration with id 9999/,
|
||||
);
|
||||
db.destroy();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { sql, catSql, joinSql, calcUpdates, unsafeSql } from "../../../src/db/sqlite/sql.js";
|
||||
import {
|
||||
sql,
|
||||
catSql,
|
||||
joinSql,
|
||||
calcUpdates,
|
||||
unsafeSql,
|
||||
} from "../../../src/db/sqlite/sql.js";
|
||||
|
||||
describe("sql template tag", () => {
|
||||
it("produces correct sql and empty vars for plain text", () => {
|
||||
|
||||
@@ -10,13 +10,19 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
|
||||
before(() => {
|
||||
db = makeDb();
|
||||
db.run(unsafeSql("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)"));
|
||||
db.run(
|
||||
unsafeSql(
|
||||
"CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, val TEXT NOT NULL)",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
after(() => db.destroy());
|
||||
|
||||
it("run INSERT returns changes count", () => {
|
||||
const { changes } = db.run(sql`INSERT INTO test(val) VALUES(${{ text: "hello" }})`);
|
||||
const { changes } = db.run(
|
||||
sql`INSERT INTO test(val) VALUES(${{ text: "hello" }})`,
|
||||
);
|
||||
assert.equal(changes, 1);
|
||||
});
|
||||
|
||||
@@ -24,7 +30,9 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
db.run(unsafeSql("DELETE FROM test"));
|
||||
db.run(sql`INSERT INTO test(val) VALUES(${{ text: "a" }})`);
|
||||
db.run(sql`INSERT INTO test(val) VALUES(${{ text: "b" }})`);
|
||||
const rows = db.all<{ val: string }>(unsafeSql("SELECT val FROM test ORDER BY val"));
|
||||
const rows = db.all<{ val: string }>(
|
||||
unsafeSql("SELECT val FROM test ORDER BY val"),
|
||||
);
|
||||
assert.equal(rows.length, 2);
|
||||
assert.equal(rows[0].val, "a");
|
||||
assert.equal(rows[1].val, "b");
|
||||
@@ -38,7 +46,7 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
assert.equal(row.val, "one");
|
||||
|
||||
const none = db.get<{ val: string }>(
|
||||
sql`SELECT val FROM test WHERE val = ${{ text: "none" }}`
|
||||
sql`SELECT val FROM test WHERE val = ${{ text: "none" }}`,
|
||||
);
|
||||
assert.equal(none, null);
|
||||
});
|
||||
@@ -49,7 +57,7 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
db.run(sql`INSERT INTO test(val) VALUES(${{ text: "dup2" }})`);
|
||||
assert.throws(
|
||||
() => db.get<{ val: string }>(unsafeSql("SELECT val FROM test")),
|
||||
/Multiple results/
|
||||
/Multiple results/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -69,7 +77,7 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
db.multi(() => {
|
||||
db.run(sql`INSERT INTO test(val) VALUES(${{ text: "rollback" }})`);
|
||||
throw new Error("abort!");
|
||||
})
|
||||
}),
|
||||
);
|
||||
const rows = db.all<{ val: string }>(unsafeSql("SELECT val FROM test"));
|
||||
assert.equal(rows.length, 0);
|
||||
@@ -82,7 +90,13 @@ function runWrappedDbSuite(name: string, makeDb: () => WrappedDb) {
|
||||
|
||||
it("rethrow propagates non-SQLite errors unchanged", () => {
|
||||
const err = new Error("custom error");
|
||||
assert.throws(() => db.rethrow(() => { throw err; }), (e) => e === err);
|
||||
assert.throws(
|
||||
() =>
|
||||
db.rethrow(() => {
|
||||
throw err;
|
||||
}),
|
||||
(e) => e === err,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -93,9 +107,7 @@ describe("better-sqlite3 WrappedDb", async () => {
|
||||
let bs3Ctor: (new (path: string) => WrappedDb) | null = null;
|
||||
|
||||
try {
|
||||
const mod = await import(
|
||||
"../../../src/db/sqlite/impl/better-sqlite3.js"
|
||||
);
|
||||
const mod = await import("../../../src/db/sqlite/impl/better-sqlite3.js");
|
||||
bs3Ctor = mod.WrappedBetterSqlite3Db;
|
||||
} catch {
|
||||
// better-sqlite3 not available, skip
|
||||
|
||||
Reference in New Issue
Block a user