diff --git a/src/plugins/github/referenceDetector.js b/src/plugins/github/referenceDetector.js new file mode 100644 index 0000000..ee54c3e --- /dev/null +++ b/src/plugins/github/referenceDetector.js @@ -0,0 +1,37 @@ +// @flow + +import dedent from "../../util/dedent"; +import {type NodeAddressT, NodeAddress} from "../../core/graph"; +import {MappedReferenceDetector, type URL} from "../../core/references"; +import {RelationalView} from "./relationalView"; + +/** + * Builds a GithubReferenceDetector using multiple RelationalView. + * As RelationalView should only be used for one repository at a time, you will + * commonly want to compose several of them into one GithubReferenceDetector. + * + * Note: duplicates are normally expected. However for any URL, the corresponding + * NodeAddressT should be the same, or we'll throw an error. + */ +export function fromRelationalViews( + views: $ReadOnlyArray +): GithubReferenceDetector { + const map: Map = new Map(); + for (const view of views) { + for (const [url, addr] of view.urlReferenceMap().entries()) { + const existing = map.get(url); + if (existing && existing != addr) { + throw new Error(dedent`\ + An entry for ${url} already existed, but with a different NodeAddressT. + This is probably a bug with SourceCred. Please report it on GitHub. + Old: ${NodeAddress.toString(existing)} + New: ${NodeAddress.toString(addr)} + `); + } + map.set(url, addr); + } + } + return new GithubReferenceDetector(map); +} + +export const GithubReferenceDetector = MappedReferenceDetector; diff --git a/src/plugins/github/referenceDetector.test.js b/src/plugins/github/referenceDetector.test.js new file mode 100644 index 0000000..0263a9d --- /dev/null +++ b/src/plugins/github/referenceDetector.test.js @@ -0,0 +1,75 @@ +// @flow + +import {exampleRelationalView} from "./example/example"; +import {RelationalView} from "./relationalView"; +import { + GithubReferenceDetector, + fromRelationalViews, +} from "./referenceDetector"; +import {MappedReferenceDetector} from "../../core/references"; +import {NodeAddress} from "../../core/graph"; +import dedent from "../../util/dedent"; + +describe("plugins/github/referenceDetector", () => { + describe("GithubReferenceDetector", () => { + it("should be a MappedReferenceDetector", () => { + // Given + const map = new Map(); + + // When + const refs = new GithubReferenceDetector(map); + + // Then + expect(refs).toBeInstanceOf(MappedReferenceDetector); + }); + }); + + describe("fromRelationalViews", () => { + it("should use urlReferenceMap to create the instance", () => { + // Given + const rv = new RelationalView(); + const urlReferenceMap = jest.spyOn(rv, "urlReferenceMap"); + + // When + const refs = fromRelationalViews([rv, rv]); + + // Then + expect(refs).toBeInstanceOf(MappedReferenceDetector); + expect(urlReferenceMap).toBeCalledTimes(2); + }); + + it("should deduplicate silently given the same entries", () => { + // Given + const rv = exampleRelationalView(); + + // When + const refs = fromRelationalViews([rv, rv]); + + // Then + expect(refs.map).toEqual(rv.urlReferenceMap()); + }); + + it("should throw when encountering duplicate keys with different values", () => { + // Given + const url = "http://foo.bar"; + const nodeA = NodeAddress.fromParts(["test", "A"]); + const nodeB = NodeAddress.fromParts(["test", "B"]); + const rv1 = new RelationalView(); + const rv2 = new RelationalView(); + const rv1Spy = jest.spyOn(rv1, "urlReferenceMap"); + const rv2Spy = jest.spyOn(rv2, "urlReferenceMap"); + rv1Spy.mockReturnValue(new Map([[url, nodeA]])); + rv2Spy.mockReturnValue(new Map([[url, nodeB]])); + + // When + const fn = () => fromRelationalViews([rv1, rv2]); + + // Then + expect(fn).toThrow(dedent`\ + An entry for http://foo.bar already existed, but with a different NodeAddressT. + This is probably a bug with SourceCred. Please report it on GitHub. + Old: NodeAddress["test","A"] + New: NodeAddress["test","B"]`); + }); + }); +});