Files
hy2-panel/tests/unit/store-custom.test.ts
T

104 lines
4.3 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { test } from "node:test";
import Database from "better-sqlite3";
// Start with the real deployed schema rather than the new migration output.
test("existing v4 boots and custom nodes stay outside HY2 polling/auth while retaining credentials", async () => {
const dir = mkdtempSync(path.join(tmpdir(), "hy2-custom-store-"));
process.env.DATABASE_PATH = path.join(dir, "remote-v4.db");
process.env.ADMIN_USERNAME = "test-admin";
process.env.ADMIN_PASSWORD = "test-admin-local-only";
process.env.POLLER_ENABLED = "false";
const legacy = new Database(process.env.DATABASE_PATH);
legacy.exec(readFileSync("tests/fixtures/remote-v4-schema.sql", "utf8"));
legacy.exec(`INSERT INTO hy2_nodes (id,slug,name,auth_token,traffic_stats_url,traffic_stats_secret,created_at,updated_at,server_host)
VALUES (91,'existing-hy2','Existing HY2','test-auth','http://stats.example:9999','test-stats','2026-01-01','2026-01-01','hy2.example');`);
const yaml =
'# original comment\n- name: "Remote Custom"\n type: anytls\n password: "test-pass"\n server: custom.example\n port: 443\n';
legacy
.prepare(
`INSERT INTO hy2_nodes (id,slug,name,auth_token,traffic_stats_url,traffic_stats_secret,created_at,updated_at,node_type,custom_config_yaml,client_name)
VALUES (92,'existing-custom','Display','test-original-auth','','test-original-stats','2026-01-01','2026-01-01','custom',?,'Old alias')`,
)
.run(yaml);
legacy.exec(
`INSERT INTO app_settings VALUES ('main_proxy_group_member_slugs_json','["existing-custom","existing-hy2"]','2026-01-01')`,
);
const before = legacy.prepare("SELECT * FROM hy2_nodes ORDER BY id").all();
legacy.close();
const { ensureDb } = await import("../../lib/db");
const store = await import("../../lib/store");
const { verifyNodeToken } = await import("../../lib/hy2");
const db = await ensureDb();
try {
assert.deepEqual(
db.prepare("SELECT * FROM hy2_nodes ORDER BY id").all(),
before,
);
assert.deepEqual(
(await store.getSubscriptionSettings()).mainProxyGroupMemberSlugs,
["existing-custom", "existing-hy2"],
);
assert.deepEqual(
(await store.listEnabledNodesForPolling()).map((node) => node.id),
[91],
);
assert.equal((await store.listSubscriptionNodes()).length, 2);
assert.equal(
(await verifyNodeToken("existing-custom", "test-original-auth")).reason,
"node_not_hy2",
);
assert.equal((await verifyNodeToken("existing-hy2", "test-auth")).ok, true);
const custom = (await store.getNodeBySlug("existing-custom"))!;
assert.equal(store.getNodeProxyName(custom), "Remote Custom");
const input = {
nodeType: "custom" as const,
slug: "existing-custom",
name: "Display",
authToken: "",
hy2Listen: "",
serverHost: "",
serverPort: 443,
clientName: "",
bandwidthUpMbps: 30,
bandwidthDownMbps: 30,
skipCertVerify: false,
sni: null,
customConfigYaml: yaml.replace("Remote Custom", "Updated Custom"),
trafficStatsUrl: "",
trafficStatsListen: ":9999",
trafficStatsSecret: "",
enabled: true,
pollIntervalSeconds: 15,
};
await store.updateNode({ ...input, id: custom.id });
const saved = (await store.getNodeBySlug("existing-custom"))!;
assert.equal(saved.id, 92);
assert.equal(saved.custom_config_yaml, input.customConfigYaml);
assert.equal(saved.auth_token, "test-original-auth");
assert.equal(saved.traffic_stats_secret, "test-original-stats");
assert.equal(store.getNodeProxyName(saved), "Updated Custom");
await assert.rejects(() =>
store.updateNode({
...input,
id: 92,
customConfigYaml: "name: [invalid",
}),
);
assert.deepEqual(await store.getNodeBySlug("existing-custom"), saved);
await store.updateNode({ ...input, id: 92, enabled: false });
assert.deepEqual(
(await store.listSubscriptionNodes()).map((node) => node.id),
[91],
);
} finally {
db.close();
global.__hy2PanelDb = undefined;
global.__hy2PanelDbInit = undefined;
rmSync(dir, { recursive: true, force: true });
}
});