Merge pull request #1711 from waku-org/adklempner/validate-content-topics

feat: add function to validate autoshard content topic
This commit is contained in:
Arseniy Klempner 2023-11-15 22:43:53 -08:00 committed by GitHub
commit 5e9c981e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 1 deletions

View File

@ -7,6 +7,8 @@
"ahadns",
"Alives",
"asym",
"autoshard",
"autosharding",
"backoff",
"backoffs",
"bitauth",

View File

@ -0,0 +1,6 @@
{
"reporterEnabled": "spec, allure-mocha",
"allureMochaReporter": {
"outputDir": "allure-results"
}
}

View File

@ -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;

View File

@ -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"

View File

@ -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");
});
});

View File

@ -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");
}
}