Files
kunthawat 2d1be52177 Emdash source with visual editor image upload fix
Fixes:
1. media.ts: wrap placeholder generation in try-catch
2. toolbar.ts: check r.ok, display error message in popover
2026-05-03 10:44:54 +07:00

181 lines
4.9 KiB
TypeScript

import { unlinkSync } from "node:fs";
import type { Kysely } from "kysely";
import { describe, it, expect, afterEach } from "vitest";
import {
createDatabase,
EmDashDatabaseError,
formatNativeModuleVersionError,
} from "../../src/database/connection.js";
import type { Database } from "../../src/database/types.js";
describe("createDatabase", () => {
let db: Kysely<Database> | undefined;
afterEach(async () => {
if (db) {
await db.destroy();
db = undefined;
}
});
describe("in-memory SQLite", () => {
it("should create in-memory database with :memory: URL", () => {
db = createDatabase({ url: ":memory:" });
expect(db).toBeDefined();
});
it("should allow queries on in-memory database", async () => {
db = createDatabase({ url: ":memory:" });
// Create a simple table
await db.schema
.createTable("test")
.addColumn("id", "text", (col) => col.primaryKey())
.execute();
// Insert a row
await db
.insertInto("test" as any)
.values({ id: "test-1" })
.execute();
// Query it back
const result = await db
.selectFrom("test" as any)
.selectAll()
.execute();
expect(result).toHaveLength(1);
expect(result[0].id).toBe("test-1");
});
});
describe("file-based SQLite", () => {
const testDbPath = "./test-db.sqlite";
afterEach(() => {
try {
unlinkSync(testDbPath);
} catch {
// Ignore if file doesn't exist
}
});
it("should create file-based database with file: URL", () => {
db = createDatabase({ url: `file:${testDbPath}` });
expect(db).toBeDefined();
});
it("should persist data to file", async () => {
// Create database and insert data
db = createDatabase({ url: `file:${testDbPath}` });
await db.schema
.createTable("test")
.addColumn("id", "text", (col) => col.primaryKey())
.execute();
await db
.insertInto("test" as any)
.values({ id: "test-1" })
.execute();
await db.destroy();
db = undefined;
// Reopen database and verify data persists
db = createDatabase({ url: `file:${testDbPath}` });
const result = await db
.selectFrom("test" as any)
.selectAll()
.execute();
expect(result).toHaveLength(1);
expect(result[0].id).toBe("test-1");
});
});
describe("libSQL / Turso", () => {
it("should throw error for libsql URL without auth token", () => {
expect(() => {
createDatabase({ url: "libsql://example.turso.io" });
}).toThrow(EmDashDatabaseError);
expect(() => {
createDatabase({ url: "libsql://example.turso.io" });
}).toThrow("Auth token required");
});
it("should throw not implemented error for libsql URL with token", () => {
expect(() => {
createDatabase({
url: "libsql://example.turso.io",
authToken: "test-token",
});
}).toThrow("LibSQL not yet implemented");
});
});
describe("error handling", () => {
it("should throw EmDashDatabaseError for invalid URL scheme", () => {
expect(() => {
createDatabase({ url: "invalid://test" });
}).toThrow(EmDashDatabaseError);
expect(() => {
createDatabase({ url: "invalid://test" });
}).toThrow("Unsupported database URL scheme");
});
it("should throw EmDashDatabaseError for malformed file path", () => {
expect(() => {
createDatabase({ url: "file:/nonexistent/path/to/db.sqlite" });
}).toThrow(EmDashDatabaseError);
});
it("should wrap underlying errors in EmDashDatabaseError", () => {
try {
createDatabase({ url: "file:/root/cannot-write-here.db" });
} catch (error) {
expect(error).toBeInstanceOf(EmDashDatabaseError);
expect(error).toHaveProperty("cause");
}
});
});
describe("formatNativeModuleVersionError", () => {
it("returns an actionable message for NODE_MODULE_VERSION mismatch", () => {
const err = new Error(
"The module '/path/better_sqlite3.node' was compiled against a different Node.js version using NODE_MODULE_VERSION 115. This version of Node.js requires NODE_MODULE_VERSION 127.",
);
const message = formatNativeModuleVersionError(err);
expect(message).not.toBeNull();
expect(message).toContain("better-sqlite3");
expect(message).toMatch(/rebuild/i);
});
it("returns null for unrelated errors", () => {
expect(formatNativeModuleVersionError(new Error("disk full"))).toBeNull();
expect(formatNativeModuleVersionError("some string")).toBeNull();
});
});
describe("connection lifecycle", () => {
it("should allow closing connection with destroy()", async () => {
db = createDatabase({ url: ":memory:" });
await expect(db.destroy()).resolves.not.toThrow();
db = undefined;
});
it("should return functional Kysely instance", () => {
db = createDatabase({ url: ":memory:" });
// Check for Kysely methods
expect(db.selectFrom).toBeInstanceOf(Function);
expect(db.insertInto).toBeInstanceOf(Function);
expect(db.updateTable).toBeInstanceOf(Function);
expect(db.deleteFrom).toBeInstanceOf(Function);
expect(db.schema).toBeDefined();
});
});
});