122 lines
3.5 KiB
TypeScript
122 lines
3.5 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);
|
|
// Custom-only subscriptions do not require HY2 credentials.
|
|
assert.deepEqual(
|
|
parse(
|
|
buildSubscriptionConfig({
|
|
user: { ...user, auth_secret_encrypted: null },
|
|
nodes: [custom],
|
|
settings,
|
|
}),
|
|
).proxies,
|
|
[proxy],
|
|
);
|
|
});
|