Files
Termix/plugins/homepage/tests/backend/migrations.test.ts
T

177 lines
5.6 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { createTestDb, type TestDb } from "@termix/plugin-sdk/testing";
import { pluginDir } from "./helpers";
// The three tables as core's SQLite bootstrap created them before 2.9.0,
// plus the homepage_items index performance-indexes.ts added.
const LEGACY_DDL = `
CREATE TABLE homepage_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
type_id TEXT NOT NULL,
title TEXT,
config TEXT NOT NULL DEFAULT '{}',
folder_id INTEGER,
sync_id TEXT UNIQUE,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_homepage_items_user_id ON homepage_items (user_id);
CREATE TABLE homepage_layouts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
layout TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE dashboard_service_links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
label TEXT NOT NULL,
url TEXT NOT NULL,
"order" INTEGER NOT NULL DEFAULT 0,
sync_id TEXT UNIQUE,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`;
let db: TestDb | null = null;
afterEach(() => {
db?.close();
db = null;
});
const tableExists = (name: string) =>
!!db!.sqlite
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?")
.get(name);
const indexOn = (table: string) =>
(
db!.sqlite
.prepare(
"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND name NOT LIKE 'sqlite_%'",
)
.all(table) as { name: string }[]
).map((row) => row.name);
describe("adopting the homepage tables", () => {
it("keeps every existing row when the legacy tables are there", async () => {
db = await createTestDb(pluginDir, {
before: (sqlite) => {
sqlite.exec("INSERT INTO users (id) VALUES ('user-1'), ('user-2')");
sqlite.exec(LEGACY_DDL);
sqlite
.prepare(
"INSERT INTO homepage_items (user_id, type_id, title, config, sync_id) VALUES (?, ?, ?, ?, ?)",
)
.run("user-1", "clock", "My Clock", '{"format":"24h"}', "sync-item");
sqlite
.prepare(
"INSERT INTO homepage_layouts (user_id, layout) VALUES (?, ?)",
)
.run("user-1", '{"entries":[],"pan":{"x":0,"y":0},"zoom":1}');
sqlite
.prepare(
"INSERT INTO dashboard_service_links (user_id, label, url, sync_id) VALUES (?, ?, ?, ?)",
)
.run("user-1", "Grafana", "https://grafana.local", "sync-link");
},
});
expect(db.applied).toEqual(["0001_adopt_homepage_tables"]);
expect(tableExists("homepage_items")).toBe(false);
expect(tableExists("homepage_layouts")).toBe(false);
expect(tableExists("dashboard_service_links")).toBe(false);
expect(tableExists("p_homepage_homepage_items")).toBe(true);
expect(tableExists("p_homepage_homepage_layouts")).toBe(true);
expect(tableExists("p_homepage_dashboard_service_links")).toBe(true);
const items = db.sqlite
.prepare(
"SELECT user_id, type_id, title, sync_id FROM p_homepage_homepage_items",
)
.all();
expect(items).toEqual([
{
user_id: "user-1",
type_id: "clock",
title: "My Clock",
sync_id: "sync-item",
},
]);
const layouts = db.sqlite
.prepare("SELECT user_id, layout FROM p_homepage_homepage_layouts")
.all();
expect(layouts).toEqual([
{
user_id: "user-1",
layout: '{"entries":[],"pan":{"x":0,"y":0},"zoom":1}',
},
]);
const links = db.sqlite
.prepare(
"SELECT user_id, label, url, sync_id FROM p_homepage_dashboard_service_links",
)
.all();
expect(links).toEqual([
{
user_id: "user-1",
label: "Grafana",
url: "https://grafana.local",
sync_id: "sync-link",
},
]);
expect(indexOn("p_homepage_homepage_items")).toEqual([
"idx_homepage_items_user_id",
]);
expect(indexOn("p_homepage_dashboard_service_links")).toEqual([
"idx_dashboard_service_links_user_id",
]);
});
it("creates the tables fresh when there are no legacy ones", async () => {
db = await createTestDb(pluginDir);
expect(tableExists("p_homepage_homepage_items")).toBe(true);
expect(tableExists("p_homepage_homepage_layouts")).toBe(true);
expect(tableExists("p_homepage_dashboard_service_links")).toBe(true);
expect(
db.sqlite.prepare("SELECT * FROM p_homepage_homepage_items").all(),
).toEqual([]);
});
it("removes a user's rows when the user is deleted", async () => {
db = await createTestDb(pluginDir, {
before: (sqlite) => {
sqlite.exec("INSERT INTO users (id) VALUES ('user-1'), ('user-2')");
},
});
db.sqlite
.prepare(
"INSERT INTO p_homepage_homepage_items (user_id, type_id) VALUES (?, ?)",
)
.run("user-1", "clock");
db.sqlite
.prepare(
"INSERT INTO p_homepage_homepage_items (user_id, type_id) VALUES (?, ?)",
)
.run("user-2", "clock");
db.sqlite.exec("DELETE FROM users WHERE id = 'user-1'");
const remaining = (
db.sqlite
.prepare("SELECT user_id FROM p_homepage_homepage_items")
.all() as { user_id: string }[]
).map((row) => row.user_id);
expect(remaining).toEqual(["user-2"]);
});
});