91 lines
3.5 KiB
TypeScript
91 lines
3.5 KiB
TypeScript
import path from "path";
|
|
import { readFileSync } from "node:fs";
|
|
import BetterSqlite3 from "better-sqlite3";
|
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
|
import { expect, test } from "vitest";
|
|
|
|
import { DB_CASING } from "@homarr/core/infrastructure/db/constants";
|
|
|
|
import type { Database } from "..";
|
|
import { seedDataAsync } from "../migrations/seed";
|
|
import * as sqliteSchema from "../schema/sqlite";
|
|
import { expectBundledCustomWidgetsSeeded } from "./custom-widget-seed-assertions";
|
|
|
|
test("SQLite migrations seed the five disabled bundled custom widgets", async () => {
|
|
const connection = new BetterSqlite3(":memory:");
|
|
const database = drizzle(connection, { schema: sqliteSchema, casing: DB_CASING });
|
|
|
|
migrate(database, { migrationsFolder: path.join(__dirname, "..", "migrations", "sqlite") });
|
|
await seedDataAsync(database as unknown as Database);
|
|
|
|
await expectBundledCustomWidgetsSeeded(database as unknown as Database);
|
|
|
|
connection.close();
|
|
});
|
|
|
|
test("Custom JSX v2 migration preserves populated v1 definitions and secrets", () => {
|
|
const connection = new BetterSqlite3(":memory:");
|
|
connection.exec(`
|
|
CREATE TABLE user (id text PRIMARY KEY NOT NULL);
|
|
CREATE TABLE custom_widget_definition (
|
|
id text PRIMARY KEY NOT NULL,
|
|
name text NOT NULL,
|
|
description text,
|
|
icon_url text,
|
|
url text NOT NULL,
|
|
auth_type text DEFAULT 'none' NOT NULL,
|
|
header_name text,
|
|
method text DEFAULT 'GET' NOT NULL,
|
|
request_body text,
|
|
display_type text DEFAULT 'singleValue' NOT NULL,
|
|
display_config text DEFAULT '{}' NOT NULL,
|
|
enabled integer DEFAULT true NOT NULL,
|
|
created_at integer NOT NULL,
|
|
updated_at integer NOT NULL,
|
|
creator_id text REFERENCES user(id) ON DELETE SET NULL
|
|
);
|
|
CREATE TABLE custom_widget_secret (
|
|
kind text NOT NULL,
|
|
value text NOT NULL,
|
|
updated_at integer NOT NULL,
|
|
definition_id text NOT NULL REFERENCES custom_widget_definition(id) ON DELETE CASCADE,
|
|
PRIMARY KEY(definition_id, kind)
|
|
);
|
|
INSERT INTO user (id) VALUES ('owner');
|
|
INSERT INTO custom_widget_definition (
|
|
id, name, url, auth_type, method, display_type, display_config, enabled, created_at, updated_at, creator_id
|
|
) VALUES ('legacy-weather', 'Weather', 'https://example.test/weather', 'bearer', 'GET', 'singleValue',
|
|
'{"type":"singleValue","jsonPath":"$.temperature"}', true, 1700000000, 1700000001, 'owner');
|
|
INSERT INTO custom_widget_secret (kind, value, updated_at, definition_id)
|
|
VALUES ('apiKey', 'encrypted.value', 1700000001, 'legacy-weather');
|
|
`);
|
|
|
|
const migration = readFileSync(
|
|
path.join(__dirname, "..", "migrations", "sqlite", "0042_custom_widget_v2_reset.sql"),
|
|
"utf8",
|
|
);
|
|
for (const statement of migration
|
|
.split("--> statement-breakpoint")
|
|
.map((value) => value.trim())
|
|
.filter(Boolean)) {
|
|
connection.exec(statement);
|
|
}
|
|
|
|
expect(connection.prepare("SELECT * FROM legacy_custom_widget_definition").get()).toMatchObject({
|
|
id: "legacy-weather",
|
|
name: "Weather",
|
|
enabled: 1,
|
|
creator_id: "owner",
|
|
});
|
|
expect(connection.prepare("SELECT * FROM legacy_custom_widget_secret").get()).toMatchObject({
|
|
definition_id: "legacy-weather",
|
|
kind: "apiKey",
|
|
value: "encrypted.value",
|
|
});
|
|
expect(connection.prepare("SELECT count(*) AS count FROM custom_widget_definition").get()).toEqual({ count: 0 });
|
|
expect(connection.prepare("SELECT count(*) AS count FROM custom_widget_secret").get()).toEqual({ count: 0 });
|
|
|
|
connection.close();
|
|
});
|