From 791cad90599b417d167e51442c898c89d935f7b1 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Mon, 26 Feb 2018 22:57:00 -0800 Subject: [PATCH] Add conversion functions for id (#38) Test Plan: Run `yarn test` and note that tests pass. wchargin-branch: id-conversion --- src/backend/graph.js | 29 +++++++++++++++ src/backend/graph.test.js | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/backend/graph.test.js diff --git a/src/backend/graph.js b/src/backend/graph.js index 91f84f0..bce2d5e 100644 --- a/src/backend/graph.js +++ b/src/backend/graph.js @@ -24,3 +24,32 @@ export type Graph = { nodes: {[stringID: string]: GraphNode}, edges: {[stringID: string]: GraphEdge}, }; + +export function idToString(id: ID) { + if (id.pluginName.includes("$")) { + const escaped = JSON.stringify(id.pluginName); + throw new Error(`id.pluginName must not include "\$": ${escaped}`); + } + if (id.repositoryName.includes("$")) { + const escaped = JSON.stringify(id.repositoryName); + throw new Error(`id.repositoryName must not include "\$": ${escaped}`); + } + if (id.name.includes("$")) { + const escaped = JSON.stringify(id.name); + throw new Error(`id.name must not include "\$": ${escaped}`); + } + return `${id.pluginName}\$${id.repositoryName}\$${id.name}`; +} + +export function stringToID(string: string) { + const parts = string.split("$"); + if (parts.length !== 3) { + const escaped = JSON.stringify(string); + throw new Error(`Input should have exactly two \$s: ${escaped}`); + } + return { + pluginName: parts[0], + repositoryName: parts[1], + name: parts[2], + }; +} diff --git a/src/backend/graph.test.js b/src/backend/graph.test.js new file mode 100644 index 0000000..47b5c79 --- /dev/null +++ b/src/backend/graph.test.js @@ -0,0 +1,76 @@ +import {idToString, stringToID} from "./graph"; + +describe("graph", () => { + describe("idToString", () => { + const makeSimpleID = () => ({ + pluginName: "widgets", + repositoryName: "megacorp/megawidget", + name: "issue#123", + }); + it("stringifies a simple ID", () => { + const input = makeSimpleID(); + const expected = "widgets$megacorp/megawidget$issue#123"; + expect(idToString(input)).toEqual(expected); + }); + function expectRejection(attribute, value) { + const input = {...makeSimpleID(), [attribute]: value}; + expect(() => idToString(input)).toThrow(RegExp(attribute)); + // (escaping regexp in JavaScript is a nightmare; ignore it) + } + it("rejects an ID with $-signs in plugin name", () => { + expectRejection("pluginName", "widg$ets"); + }); + it("rejects an ID with $-signs in repository name", () => { + expectRejection("repositoryName", "megacorp$megawidget"); + }); + it("rejects an ID with $-signs in name", () => { + expectRejection("name", "issue$123"); + }); + }); + + describe("stringToID", () => { + it("parses a simple ID-string", () => { + const input = "widgets$megacorp/megawidget$issue#123"; + const expected = { + pluginName: "widgets", + repositoryName: "megacorp/megawidget", + name: "issue#123", + }; + expect(stringToID(input)).toEqual(expected); + }); + [0, 1, 3, 4].forEach((n) => { + it(`rejects an ID-string with ${n} occurrences of "\$"`, () => { + const dollars = Array(n + 1).join("$"); + const input = `mega${dollars}corp`; + expect(() => stringToID(input)).toThrow(/exactly two \$s/); + }); + }); + }); + + describe("stringToID and idToString interop", () => { + const examples = () => [ + { + object: { + pluginName: "widgets", + repositoryName: "megacorp/megawidget", + name: "issue#123", + }, + string: "widgets$megacorp/megawidget$issue#123", + }, + ]; + examples().forEach((example, index) => { + describe(`for example at 0-index ${index}`, () => { + it("has stringToID a left identity for idToString", () => { + expect(stringToID(idToString(example.object))).toEqual( + example.object + ); + }); + it("has stringToID a right identity for idToString", () => { + expect(idToString(stringToID(example.string))).toEqual( + example.string + ); + }); + }); + }); + }); +});