mirror of https://github.com/waku-org/js-waku.git
Merge pull request #1711 from waku-org/adklempner/validate-content-topics
feat: add function to validate autoshard content topic
This commit is contained in:
commit
5e9c981e60
|
@ -7,6 +7,8 @@
|
|||
"ahadns",
|
||||
"Alives",
|
||||
"asym",
|
||||
"autoshard",
|
||||
"autosharding",
|
||||
"backoff",
|
||||
"backoffs",
|
||||
"bitauth",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"reporterEnabled": "spec, allure-mocha",
|
||||
"allureMochaReporter": {
|
||||
"outputDir": "allure-results"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
const config = {
|
||||
extension: ['ts'],
|
||||
spec: 'src/**/*.spec.ts',
|
||||
require: ['ts-node/register', 'isomorphic-fetch'],
|
||||
loader: 'ts-node/esm',
|
||||
'node-option': [
|
||||
'experimental-specifier-resolution=node',
|
||||
'loader=ts-node/esm'
|
||||
],
|
||||
exit: true
|
||||
};
|
||||
|
||||
if (process.env.CI) {
|
||||
console.log("Running tests in parallel");
|
||||
config.parallel = true;
|
||||
config.jobs = 6;
|
||||
console.log("Activating allure reporting");
|
||||
config.reporter = 'mocha-multi-reporters';
|
||||
config.reporterOptions = {
|
||||
configFile: '.mocha.reporters.json'
|
||||
};
|
||||
} else {
|
||||
console.log("Running tests serially. To enable parallel execution update mocha config");
|
||||
}
|
||||
|
||||
module.exports = config;
|
|
@ -59,7 +59,9 @@
|
|||
"check:spelling": "cspell \"{README.md,src/**/*.ts}\"",
|
||||
"check:tsc": "tsc -p tsconfig.dev.json",
|
||||
"prepublish": "npm run build",
|
||||
"reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build"
|
||||
"reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build",
|
||||
"test": "run-s test:*",
|
||||
"test:node": "TS_NODE_PROJECT=./tsconfig.dev.json mocha"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import { expect } from "chai";
|
||||
|
||||
import { ensureValidContentTopic } from "./sharding";
|
||||
|
||||
const testInvalidCases = (
|
||||
contentTopics: string[],
|
||||
expectedError: string
|
||||
): void => {
|
||||
for (const invalidTopic of contentTopics) {
|
||||
expect(() => ensureValidContentTopic(invalidTopic)).to.throw(expectedError);
|
||||
}
|
||||
};
|
||||
|
||||
describe("ensureValidContentTopic", () => {
|
||||
it("does not throw on valid cases", () => {
|
||||
const validTopics = [
|
||||
"/0/myapp/1/mytopic/cbor",
|
||||
"/myapp/1/mytopic/cbor",
|
||||
"/myapp/v1.1/mytopic/cbor"
|
||||
];
|
||||
for (const validTopic of validTopics) {
|
||||
expect(() => ensureValidContentTopic(validTopic)).to.not.throw;
|
||||
}
|
||||
});
|
||||
it("throws on empty content topic", () => {
|
||||
testInvalidCases(["", " ", " "], "Content topic format is invalid");
|
||||
});
|
||||
|
||||
it("throws on content topic with too few or too many fields", () => {
|
||||
testInvalidCases(
|
||||
[
|
||||
"myContentTopic",
|
||||
"myapp1mytopiccbor/",
|
||||
" /myapp/1/mytopic",
|
||||
"/myapp/1/mytopic",
|
||||
"/0/myapp/1/mytopic/cbor/extra"
|
||||
],
|
||||
"Content topic format is invalid"
|
||||
);
|
||||
});
|
||||
|
||||
it("throws on content topic with non-number generation field", () => {
|
||||
testInvalidCases(
|
||||
[
|
||||
"/a/myapp/1/mytopic/cbor",
|
||||
"/ /myapp/1/mytopic/cbor",
|
||||
"/_/myapp/1/mytopic/cbor",
|
||||
"/$/myapp/1/mytopic/cbor"
|
||||
],
|
||||
"Invalid generation field in content topic"
|
||||
);
|
||||
});
|
||||
|
||||
// Note that this test case should be removed once Waku supports other generations
|
||||
it("throws on content topic with generation field greater than 0", () => {
|
||||
testInvalidCases(
|
||||
[
|
||||
"/1/myapp/1/mytopic/cbor",
|
||||
"/2/myapp/1/mytopic/cbor",
|
||||
"/3/myapp/1/mytopic/cbor",
|
||||
"/1000/myapp/1/mytopic/cbor"
|
||||
],
|
||||
"Generation greater than 0 is not supported"
|
||||
);
|
||||
});
|
||||
|
||||
it("throws on content topic with empty application field", () => {
|
||||
testInvalidCases(
|
||||
["/0//1/mytopic/cbor"],
|
||||
"Application field cannot be empty"
|
||||
);
|
||||
});
|
||||
|
||||
it("throws on content topic with empty version field", () => {
|
||||
testInvalidCases(
|
||||
["/0/myapp//mytopic/cbor"],
|
||||
"Version field cannot be empty"
|
||||
);
|
||||
});
|
||||
|
||||
it("throws on content topic with empty topic name field", () => {
|
||||
testInvalidCases(["/0/myapp/1//cbor"], "Topic name field cannot be empty");
|
||||
});
|
||||
|
||||
it("throws on content topic with empty encoding field", () => {
|
||||
testInvalidCases(["/0/myapp/1/mytopic/"], "Encoding field cannot be empty");
|
||||
});
|
||||
});
|
|
@ -18,3 +18,42 @@ export function ensurePubsubTopicIsConfigured(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, will throw an error if it is not formatted as a valid content topic for autosharding based on https://rfc.vac.dev/spec/51/
|
||||
* @param contentTopic String to validate
|
||||
*/
|
||||
export function ensureValidContentTopic(contentTopic: string): void {
|
||||
const parts = contentTopic.split("/");
|
||||
if (parts.length < 5 || parts.length > 6) {
|
||||
throw Error("Content topic format is invalid");
|
||||
}
|
||||
// Validate generation field if present
|
||||
if (parts.length == 6) {
|
||||
const generation = parseInt(parts[1]);
|
||||
if (isNaN(generation)) {
|
||||
throw new Error("Invalid generation field in content topic");
|
||||
}
|
||||
if (generation > 0) {
|
||||
throw new Error("Generation greater than 0 is not supported");
|
||||
}
|
||||
}
|
||||
// Validate remaining fields
|
||||
const fields = parts.splice(-4);
|
||||
// Validate application field
|
||||
if (fields[0].length == 0) {
|
||||
throw new Error("Application field cannot be empty");
|
||||
}
|
||||
// Validate version field
|
||||
if (fields[1].length == 0) {
|
||||
throw new Error("Version field cannot be empty");
|
||||
}
|
||||
// Validate topic name field
|
||||
if (fields[2].length == 0) {
|
||||
throw new Error("Topic name field cannot be empty");
|
||||
}
|
||||
// Validate encoding field
|
||||
if (fields[3].length == 0) {
|
||||
throw new Error("Encoding field cannot be empty");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue