mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 10:21:55 +00:00
523 lines
14 KiB
TypeScript
523 lines
14 KiB
TypeScript
import * as PM from "./propertiesMatcher.ts";
|
|
import { expect } from "https://deno.land/x/expect@v0.2.9/mod.ts";
|
|
import { matches } from "../dependencies.ts";
|
|
import { configSpec as bitcoinPropertiesConfig } from "./test/output.ts";
|
|
|
|
const randWithSeed = (seed = 1) => {
|
|
return function random() {
|
|
const x = Math.sin(seed++) * 10000;
|
|
return x - Math.floor(x);
|
|
};
|
|
};
|
|
const bitcoinProperties = bitcoinPropertiesConfig.build();
|
|
type BitcoinProperties = typeof bitcoinProperties;
|
|
const anyValue: unknown = "";
|
|
const _testBoolean: boolean = anyValue as PM.GuardAll<
|
|
BitcoinProperties["rpc"]["spec"]["enable"]
|
|
>;
|
|
// @ts-expect-error Boolean can't be a string
|
|
const _testBooleanBad: string = anyValue as PM.GuardAll<
|
|
BitcoinProperties["rpc"]["spec"]["enable"]
|
|
>;
|
|
const _testString: string = anyValue as PM.GuardAll<
|
|
BitcoinProperties["rpc"]["spec"]["username"]
|
|
>;
|
|
// @ts-expect-error string can't be a boolean
|
|
const _testStringBad: boolean = anyValue as PM.GuardAll<
|
|
BitcoinProperties["rpc"]["spec"]["username"]
|
|
>;
|
|
const _testNumber: number = anyValue as PM.GuardAll<
|
|
BitcoinProperties["advanced"]["spec"]["dbcache"]
|
|
>;
|
|
// @ts-expect-error Number can't be string
|
|
const _testNumberBad: string = anyValue as PM.GuardAll<
|
|
BitcoinProperties["advanced"]["spec"]["dbcache"]
|
|
>;
|
|
const _testObject: {
|
|
enable: boolean;
|
|
avoidpartialspends: boolean;
|
|
discardfee: number;
|
|
} = anyValue as PM.GuardAll<BitcoinProperties["wallet"]>;
|
|
// @ts-expect-error Boolean can't be object
|
|
const _testObjectBad: boolean = anyValue as PM.GuardAll<
|
|
BitcoinProperties["wallet"]
|
|
>;
|
|
const _testObjectNested: { test: { a: boolean } } = anyValue as PM.GuardAll<{
|
|
readonly type: "object";
|
|
readonly spec: {
|
|
readonly test: {
|
|
readonly type: "object";
|
|
readonly spec: {
|
|
readonly a: {
|
|
readonly type: "boolean";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}>;
|
|
const _testList: readonly string[] = anyValue as PM.GuardAll<{
|
|
type: "list";
|
|
subtype: "string";
|
|
default: [];
|
|
}>;
|
|
// @ts-expect-error number[] can't be string[]
|
|
const _testListBad: readonly number[] = anyValue as PM.GuardAll<{
|
|
type: "list";
|
|
subtype: "string";
|
|
default: [];
|
|
}>;
|
|
const _testPointer: string | null = anyValue as PM.GuardAll<{
|
|
type: "pointer";
|
|
}>;
|
|
const testUnionValue = anyValue as PM.GuardAll<{
|
|
type: "union";
|
|
tag: {
|
|
id: "mode";
|
|
name: "Pruning Mode";
|
|
warning: null;
|
|
description:
|
|
'- Disabled: Disable pruning\n- Automatic: Limit blockchain size on disk to a certain number of megabytes\n- Manual: Prune blockchain with the "pruneblockchain" RPC\n';
|
|
"variant-names": {
|
|
disabled: "Disabled";
|
|
automatic: "Automatic";
|
|
manual: "Manual";
|
|
};
|
|
};
|
|
variants: {
|
|
disabled: Record<string, never>;
|
|
automatic: {
|
|
size: {
|
|
type: "number";
|
|
nullable: false;
|
|
name: "Max Chain Size";
|
|
description: "Limit of blockchain size on disk.";
|
|
warning: "Increasing this value will require re-syncing your node.";
|
|
default: 550;
|
|
range: "[550,1000000)";
|
|
integral: true;
|
|
units: "MiB";
|
|
placeholder: null;
|
|
};
|
|
};
|
|
manual: {
|
|
size: {
|
|
type: "number";
|
|
nullable: false;
|
|
name: "Failsafe Chain Size";
|
|
description: "Prune blockchain if size expands beyond this.";
|
|
default: 65536;
|
|
range: "[550,1000000)";
|
|
integral: true;
|
|
units: "MiB";
|
|
};
|
|
};
|
|
};
|
|
default: "disabled";
|
|
}>;
|
|
const _testUnion:
|
|
| { mode: "disabled" }
|
|
| { mode: "automatic"; size: number }
|
|
| {
|
|
mode: "manual";
|
|
size: number;
|
|
} = testUnionValue;
|
|
//@ts-expect-error Bad mode name
|
|
const _testUnionBadUnion:
|
|
| { mode: "disabled" }
|
|
| { mode: "bad"; size: number }
|
|
| {
|
|
mode: "manual";
|
|
size: number;
|
|
} = testUnionValue;
|
|
const _testAll: PM.TypeFromProps<BitcoinProperties> = anyValue as {
|
|
rpc: {
|
|
enable: boolean;
|
|
username: string;
|
|
password: string;
|
|
advanced: {
|
|
auth: string[];
|
|
serialversion: "non-segwit" | "segwit";
|
|
servertimeout: number;
|
|
threads: number;
|
|
workqueue: number;
|
|
};
|
|
};
|
|
|
|
"zmq-enabled": boolean;
|
|
txindex: boolean;
|
|
wallet: {
|
|
enable: boolean;
|
|
avoidpartialspends: boolean;
|
|
discardfee: number;
|
|
};
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: boolean;
|
|
persistmempool: boolean;
|
|
maxmempool: number;
|
|
mempoolexpiry: number;
|
|
};
|
|
peers: {
|
|
listen: boolean;
|
|
onlyconnect: boolean;
|
|
onlyonion: boolean;
|
|
addnode: readonly { hostname: string; port: number }[];
|
|
};
|
|
dbcache: number;
|
|
pruning:
|
|
| { mode: "disabled" }
|
|
| { mode: "automatic"; size: number }
|
|
| {
|
|
mode: "manual";
|
|
size: number;
|
|
};
|
|
blockfilters: {
|
|
blockfilterindex: boolean;
|
|
peerblockfilters: boolean;
|
|
};
|
|
bloomfilters: {
|
|
peerbloomfilters: boolean;
|
|
};
|
|
};
|
|
};
|
|
|
|
const { test } = Deno;
|
|
|
|
{
|
|
test("matchNumberWithRange (1,4)", () => {
|
|
const checker = PM.matchNumberWithRange("(1,4)");
|
|
expect(checker.test(0)).toBe(false);
|
|
expect(checker.test(1)).toBe(false);
|
|
expect(checker.test(2)).toBe(true);
|
|
expect(checker.test(3)).toBe(true);
|
|
expect(checker.test(4)).toBe(false);
|
|
expect(checker.test(5)).toBe(false);
|
|
});
|
|
test("matchNumberWithRange [1,4]", () => {
|
|
const checker = PM.matchNumberWithRange("[1,4]");
|
|
expect(checker.test(0)).toBe(false);
|
|
expect(checker.test(1)).toBe(true);
|
|
expect(checker.test(2)).toBe(true);
|
|
expect(checker.test(3)).toBe(true);
|
|
expect(checker.test(4)).toBe(true);
|
|
expect(checker.test(5)).toBe(false);
|
|
});
|
|
test("matchNumberWithRange [1,*)", () => {
|
|
const checker = PM.matchNumberWithRange("[1,*)");
|
|
expect(checker.test(0)).toBe(false);
|
|
expect(checker.test(1)).toBe(true);
|
|
expect(checker.test(2)).toBe(true);
|
|
expect(checker.test(3)).toBe(true);
|
|
expect(checker.test(4)).toBe(true);
|
|
expect(checker.test(5)).toBe(true);
|
|
});
|
|
test("matchNumberWithRange (*,4]", () => {
|
|
const checker = PM.matchNumberWithRange("(*,4]");
|
|
expect(checker.test(0)).toBe(true);
|
|
expect(checker.test(1)).toBe(true);
|
|
expect(checker.test(2)).toBe(true);
|
|
expect(checker.test(3)).toBe(true);
|
|
expect(checker.test(4)).toBe(true);
|
|
expect(checker.test(5)).toBe(false);
|
|
});
|
|
}
|
|
|
|
{
|
|
test("Generate 1", () => {
|
|
const random = randWithSeed(1);
|
|
const options = { random };
|
|
const generated = PM.generateDefault(
|
|
{ charset: "a-z,B-X,2-5", len: 100 },
|
|
options,
|
|
);
|
|
expect(generated.length).toBe(100);
|
|
expect(generated).toBe(
|
|
"WwwgjGRkvDaGQSLeKTtlOmdDbXoCBkOn3dxUvkKkrlOFd4FbKuvIosvfPTQhbWCTQakqnwpoHmPnbgyK5CGtSQyGhxEGLjS3oKko",
|
|
);
|
|
});
|
|
test("Generate Tests", () => {
|
|
const random = randWithSeed(2);
|
|
const options = { random };
|
|
expect(PM.generateDefault({ charset: "0-1", len: 100 }, options)).toBe(
|
|
"0000110010000000000011110000010010000011101111001000000000000000100001101000010000001000010000010110",
|
|
);
|
|
expect(PM.generateDefault({ charset: "a-z", len: 100 }, options)).toBe(
|
|
"qipnycbqmqdtflrhnckgrhftrqnvxbhyyfehpvficljseasxwdyleacmjqemmpnuotkwzlsqdumuaaksxykchljgdoslrfubhepr",
|
|
);
|
|
expect(PM.generateDefault({ charset: "a,b,c,d,f,g", len: 100 }, options))
|
|
.toBe(
|
|
"bagbafcgaaddcabdfadccaadfbddffdcfccfbafbddbbfcdggfcgaffdbcgcagcfbdbfaagbfgfccdbfdfbdagcfdcabbdffaffc",
|
|
);
|
|
});
|
|
}
|
|
|
|
{
|
|
test("Specs Union", () => {
|
|
const checker = PM.guardAll(bitcoinProperties.advanced.spec.pruning);
|
|
console.log("Checker = ", matches.Parser.parserAsString(checker.parser));
|
|
checker.unsafeCast({ mode: "automatic", size: 1234 });
|
|
});
|
|
|
|
test("A default that is invalid according to the tests", () => {
|
|
const checker = PM.typeFromProps(
|
|
{
|
|
pubkey_whitelist: {
|
|
name: "Pubkey Whitelist (hex)",
|
|
description:
|
|
"A list of pubkeys that are permitted to publish through your relay. A minimum, you need to enter your own Nostr hex (not npub) pubkey. Go to https://damus.io/key/ to convert from npub to hex.",
|
|
type: "list",
|
|
range: "[1,*)",
|
|
subtype: "string",
|
|
spec: {
|
|
masked: false,
|
|
placeholder: "hex (not npub) pubkey",
|
|
pattern: "[0-9a-fA-F]{3}",
|
|
"pattern-description":
|
|
"Must be a valid 64-digit hexadecimal value (ie a Nostr hex pubkey, not an npub). Go to https://damus.io/key/ to convert npub to hex.",
|
|
},
|
|
default: [] as string[], // [] as string []
|
|
warning: null,
|
|
},
|
|
} as const,
|
|
);
|
|
|
|
checker.unsafeCast({
|
|
pubkey_whitelist: ["aaa"],
|
|
});
|
|
});
|
|
|
|
test("Full spec", () => {
|
|
const checker = PM.typeFromProps(bitcoinProperties);
|
|
|
|
checker.unsafeCast({
|
|
rpc: {
|
|
enable: true,
|
|
username: "asdf",
|
|
password: "asdf",
|
|
advanced: {
|
|
auth: ["test:34$aa"],
|
|
serialversion: "non-segwit",
|
|
servertimeout: 12,
|
|
threads: 12,
|
|
workqueue: 12,
|
|
},
|
|
},
|
|
"zmq-enabled": false,
|
|
txindex: false,
|
|
wallet: {
|
|
enable: true,
|
|
avoidpartialspends: false,
|
|
discardfee: 0,
|
|
},
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: false,
|
|
persistmempool: false,
|
|
maxmempool: 3012,
|
|
mempoolexpiry: 321,
|
|
},
|
|
peers: {
|
|
listen: false,
|
|
onlyconnect: false,
|
|
onlyonion: false,
|
|
addnode: [{ hostname: "google.com", port: 231 }],
|
|
},
|
|
dbcache: 123,
|
|
pruning: { mode: "automatic", size: 1234 },
|
|
blockfilters: {
|
|
blockfilterindex: false,
|
|
peerblockfilters: false,
|
|
},
|
|
bloomfilters: {
|
|
peerbloomfilters: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(() =>
|
|
checker.unsafeCast({
|
|
rpc: {
|
|
enable: true,
|
|
username: "asdf",
|
|
password: "asdf",
|
|
advanced: {
|
|
auth: ["test:34$aa"],
|
|
serialversion: "non-segwit",
|
|
servertimeout: 12,
|
|
threads: 12,
|
|
workqueue: 12,
|
|
},
|
|
},
|
|
"zmq-enabled": false,
|
|
txindex: false,
|
|
wallet: {
|
|
enable: true,
|
|
avoidpartialspends: false,
|
|
discardfee: 0,
|
|
},
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: false,
|
|
persistmempool: false,
|
|
maxmempool: 3012,
|
|
mempoolexpiry: 321,
|
|
},
|
|
peers: {
|
|
listen: false,
|
|
onlyconnect: false,
|
|
onlyonion: false,
|
|
addnode: [{ hostname: "google", port: 231 }],
|
|
},
|
|
dbcache: 123,
|
|
pruning: { mode: "automatic", size: 1234 },
|
|
blockfilters: {
|
|
blockfilterindex: false,
|
|
peerblockfilters: false,
|
|
},
|
|
bloomfilters: {
|
|
peerbloomfilters: false,
|
|
},
|
|
},
|
|
})
|
|
).toThrow();
|
|
|
|
expect(() =>
|
|
checker.unsafeCast({
|
|
rpc: {
|
|
enable: true,
|
|
username: "asdf",
|
|
password: "asdf",
|
|
advanced: {
|
|
auth: ["test34$aa"],
|
|
serialversion: "non-segwit",
|
|
servertimeout: 12,
|
|
threads: 12,
|
|
workqueue: 12,
|
|
},
|
|
},
|
|
"zmq-enabled": false,
|
|
txindex: false,
|
|
wallet: {
|
|
enable: true,
|
|
avoidpartialspends: false,
|
|
discardfee: 0,
|
|
},
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: false,
|
|
persistmempool: false,
|
|
maxmempool: 3012,
|
|
mempoolexpiry: 321,
|
|
},
|
|
peers: {
|
|
listen: false,
|
|
onlyconnect: false,
|
|
onlyonion: false,
|
|
addnode: [{ hostname: "google.com", port: 231 }],
|
|
},
|
|
dbcache: 123,
|
|
pruning: { mode: "automatic", size: 1234 },
|
|
blockfilters: {
|
|
blockfilterindex: false,
|
|
peerblockfilters: false,
|
|
},
|
|
bloomfilters: {
|
|
peerbloomfilters: false,
|
|
},
|
|
},
|
|
})
|
|
).toThrow();
|
|
|
|
expect(() =>
|
|
checker.unsafeCast({
|
|
rpc: {
|
|
enable: true,
|
|
username: "asdf",
|
|
password: "asdf",
|
|
advanced: {
|
|
auth: ["test:34$aa"],
|
|
serialversion: "non-segwit",
|
|
servertimeout: 12,
|
|
threads: 12,
|
|
workqueue: 12,
|
|
},
|
|
},
|
|
"zmq-enabled": false,
|
|
txindex: false,
|
|
wallet: {
|
|
enable: true,
|
|
avoidpartialspends: false,
|
|
discardfee: 0,
|
|
},
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: false,
|
|
persistmempool: false,
|
|
maxmempool: 3012,
|
|
mempoolexpiry: 321,
|
|
},
|
|
peers: {
|
|
listen: false,
|
|
onlyconnect: false,
|
|
onlyonion: false,
|
|
addnode: [{ hostname: "google.com", port: 231 }],
|
|
},
|
|
dbcache: 123,
|
|
pruning: { mode: "automatic", size: "1234" },
|
|
blockfilters: {
|
|
blockfilterindex: false,
|
|
peerblockfilters: false,
|
|
},
|
|
bloomfilters: {
|
|
peerbloomfilters: false,
|
|
},
|
|
},
|
|
})
|
|
).toThrow();
|
|
checker.unsafeCast({
|
|
rpc: {
|
|
enable: true,
|
|
username: "asdf",
|
|
password: "asdf",
|
|
advanced: {
|
|
auth: ["test:34$aa"],
|
|
serialversion: "non-segwit",
|
|
servertimeout: 12,
|
|
threads: 12,
|
|
workqueue: 12,
|
|
},
|
|
},
|
|
"zmq-enabled": false,
|
|
txindex: false,
|
|
wallet: {
|
|
enable: true,
|
|
avoidpartialspends: false,
|
|
discardfee: 0,
|
|
},
|
|
advanced: {
|
|
mempool: {
|
|
mempoolfullrbf: false,
|
|
persistmempool: false,
|
|
maxmempool: 3012,
|
|
mempoolexpiry: 321,
|
|
},
|
|
peers: {
|
|
listen: false,
|
|
onlyconnect: false,
|
|
onlyonion: false,
|
|
addnode: [{ hostname: "google.com", port: 231 }],
|
|
},
|
|
dbcache: 123,
|
|
pruning: { mode: "automatic", size: 1234 },
|
|
blockfilters: {
|
|
blockfilterindex: false,
|
|
peerblockfilters: false,
|
|
},
|
|
bloomfilters: {
|
|
peerbloomfilters: false,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
}
|