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

129 lines
4.1 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { parse, stringify } from "yaml";
import {
parseCustomProxyYaml,
renderCustomProxyYaml,
extractProxyNameFromYaml,
} from "../../lib/custom-proxy";
import { buildSubscriptionConfig } from "../../lib/subscription";
import { encryptText } from "../../lib/crypto";
import type { Hy2NodeRow, Hy2UserRow } from "../../lib/store";
const proxy = {
name: 'HK "one": #香港',
type: "anytls",
server: "hk.example.com",
port: 443,
password: 'test:p"ass#word',
udp: true,
alpn: ["h2", "http/1.1"],
};
for (const [name, source] of Object.entries({
object: stringify(proxy),
sequence: stringify([proxy]),
wrapper: stringify({ proxies: [proxy] }),
flow: JSON.stringify(proxy),
})) {
test(`accepts ${name} and preserves protocol fields and quoted names on output`, () => {
assert.deepEqual(parseCustomProxyYaml(source), proxy);
assert.equal(extractProxyNameFromYaml(source), proxy.name);
assert.deepEqual(parse(renderCustomProxyYaml(source)), [proxy]);
});
}
for (const value of [
"",
"[]",
"[a, b]",
"- name: [bad",
"name: X\nname: Y\ntype: anytls",
"name: 123\ntype: anytls",
"name: X",
"name: X\ntype: anytls\npassword: *missing",
"proxies:\n - name: X\n type: anytls\n - name: Y\n type: anytls",
]) {
test(`rejects malformed or multiple proxies: ${JSON.stringify(value).slice(0, 40)}`, () =>
assert.throws(() => parseCustomProxyYaml(value)));
}
test("mixed subscriptions preserve order, names, group references, credentials and disabled filtering", () => {
const custom = {
id: 2,
slug: "custom",
node_type: "custom",
name: "Display name",
client_name: "Old fallback",
custom_config_yaml: stringify({ proxies: [proxy] }),
enabled: 1,
} as Hy2NodeRow;
const hy2 = {
id: 1,
slug: "hy2",
node_type: "hy2",
name: "Tokyo",
client_name: 'Tokyo "HY2"',
server_host: "jp.example.com",
server_port: 443,
enabled: 1,
custom_config_yaml: "",
bandwidth_up_mbps: 30,
bandwidth_down_mbps: 30,
} as Hy2NodeRow;
const user = {
username: "member",
auth_secret_encrypted: encryptText('test:abc"def'),
} as Hy2UserRow;
const settings = {
template: "mode: rule",
profileName: 'Profile: "1"',
extraProxyGroups: "",
mainProxyGroupMemberSlugs: ["custom", "hy2"],
proxyGroups: [
{
id: "work",
name: 'Work: "1"',
type: "select" as const,
memberSlugs: ["custom", "disabled", "hy2"],
},
],
};
const result = parse(
buildSubscriptionConfig({
user,
nodes: [hy2, custom, { ...custom, slug: "disabled", enabled: 0 }],
settings,
}),
);
assert.deepEqual(result.proxies[0], proxy);
assert.equal(result.proxies[1].password, 'member:test:abc"def');
assert.equal(result.proxies[1].name, hy2.client_name);
assert.deepEqual(
result["proxy-groups"].map((group: { proxies: string[] }) => group.proxies),
[
[proxy.name, hy2.client_name],
[proxy.name, hy2.client_name],
],
);
assert.equal(result["proxy-groups"][0].name, settings.profileName);
assert.equal(result["proxy-groups"][1].name, settings.proxyGroups[0].name);
assert.equal(result.proxies.length, 2);
const hiddenResult = parse(buildSubscriptionConfig({user, nodes: [hy2, custom], settings: {...settings, mainProxyGroupHiddenSlugs: ["hy2"]}}));
assert.deepEqual(hiddenResult["proxy-groups"][0].proxies, [proxy.name]);
assert.deepEqual(hiddenResult["proxy-groups"][1].proxies, [proxy.name, hy2.client_name]);
assert.equal(hiddenResult.proxies.length, 2);
const emptyResult = parse(buildSubscriptionConfig({user, nodes: [hy2, custom], settings: {...settings, mainProxyGroupHiddenSlugs: ["hy2", "custom"]}}));
assert.deepEqual(emptyResult["proxy-groups"][0].proxies, ["DIRECT"]);
// Custom-only subscriptions do not require HY2 credentials.
assert.deepEqual(
parse(
buildSubscriptionConfig({
user: { ...user, auth_secret_encrypted: null },
nodes: [custom],
settings,
}),
).proxies,
[proxy],
);
});