From 1bd444a33b212015f068bc62f220a5d6ef5b68e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Tue, 15 May 2018 19:10:30 -0700 Subject: [PATCH] Refactor Git plugin to use NodeReference (#288) See #286 for context. I also upgraded client code in src/app. Test plan: Unit tests are extensive, including testing that `get`, `ref`, and constructors are overriden on every `GitReference` and `GitPorcelain` type that is exposed to clients. Paired with @wchargin --- src/app/credExplorer/pagerankTable.js | 2 +- src/plugins/git/porcelain.js | 373 +++++++++++++++++--------- src/plugins/git/porcelain.test.js | 131 ++++++--- src/plugins/git/render.js | 20 +- src/plugins/git/render.test.js | 26 +- 5 files changed, 366 insertions(+), 186 deletions(-) diff --git a/src/app/credExplorer/pagerankTable.js b/src/app/credExplorer/pagerankTable.js index d344278..48fe2ee 100644 --- a/src/app/credExplorer/pagerankTable.js +++ b/src/app/credExplorer/pagerankTable.js @@ -31,7 +31,7 @@ function nodeDescription(ref) { return githubNodeDescription(ref); } case GIT_PLUGIN_NAME: { - return gitNodeDescription(ref.graph(), ref.address()); + return gitNodeDescription(ref); } default: { return stringify(ref.address()); diff --git a/src/plugins/git/porcelain.js b/src/plugins/git/porcelain.js index 03243f6..8bdb633 100644 --- a/src/plugins/git/porcelain.js +++ b/src/plugins/git/porcelain.js @@ -21,7 +21,8 @@ import stringify from "json-stable-stringify"; import {Graph} from "../../core/graph"; -import type {Node} from "../../core/graph"; +import {NodeReference, NodePorcelain} from "../../core/porcelain"; +import type {Edge} from "../../core/graph"; import type {Address} from "../../core/address"; import type { @@ -30,214 +31,320 @@ import type { BlobNodePayload, TreeNodePayload, CommitNodePayload, + IncludesEdgePayload, NodePayload, NodeType, Hash, } from "./types"; -import {GIT_PLUGIN_NAME} from "./types"; +import { + BECOMES_EDGE_TYPE, + BLOB_NODE_TYPE, + COMMIT_NODE_TYPE, + GIT_PLUGIN_NAME, + HAS_CONTENTS_EDGE_TYPE, + HAS_PARENT_EDGE_TYPE, + HAS_TREE_EDGE_TYPE, + INCLUDES_EDGE_TYPE, + SUBMODULE_COMMIT_NODE_TYPE, + TREE_ENTRY_NODE_TYPE, + TREE_NODE_TYPE, +} from "./types"; import {commitAddress} from "./address"; -export class PorcelainGraph { +function assertAddressType(address: Address, t: NodeType) { + if (address.type !== t) { + throw new Error( + `Expected entity at ${stringify(address)} to have type ${t}` + ); + } +} + +export class GraphPorcelain { graph: Graph; constructor(graph: Graph) { this.graph = graph; } - // Note that this method is presently unsafe, as the hash may not exist. - // In the future, we will come up with a general case solution to have - // the type system verify that returned porcelains must be existence-tested - // before their properties are usable. - commitByHash(h: Hash): Commit { + commitByHash(h: Hash): CommitReference { const addr = commitAddress(h); - return new Commit(this.graph, addr); + const nodeReference = new NodeReference(this.graph, addr); + return new CommitReference(nodeReference); } } -export type GitPorcelain = Commit | Blob | Tree | TreeEntry | SubmoduleCommit; - -class BaseGitPorcelain { - graph: Graph; - nodeAddress: Address; - - constructor(graph: Graph, nodeAddress: Address) { - if (nodeAddress.pluginName !== GIT_PLUGIN_NAME) { - throw new Error( - `Tried to create Git porcelain for node from plugin: ${ - nodeAddress.pluginName - }` - ); +export class GitReference<+T: NodePayload> extends NodeReference { + constructor(ref: NodeReference) { + const addr = ref.address(); + if (addr.pluginName !== GIT_PLUGIN_NAME) { + throw new Error(`Wrong plugin name ${addr.pluginName} for Git plugin!`); } - this.graph = graph; - this.nodeAddress = nodeAddress; + super(ref.graph(), addr); } type(): NodeType { - return (this.address().type: any); + return ((super.type(): string): any); } - node(): Node { - return this.graph.node(this.nodeAddress); - } - - address(): Address { - return this.nodeAddress; + get(): ?GitPorcelain { + const nodePorcelain = super.get(); + if (nodePorcelain != null) { + return new GitPorcelain(nodePorcelain); + } } } -export class Commit extends BaseGitPorcelain { - static from(n: BaseGitPorcelain): Commit { - if (n.type() !== "COMMIT") { - throw new Error(`Unable to cast ${n.type()} to Commit`); +export class GitPorcelain<+T: NodePayload> extends NodePorcelain { + constructor(nodePorcelain: NodePorcelain) { + if (nodePorcelain.ref().address().pluginName !== GIT_PLUGIN_NAME) { + throw new Error( + `Wrong plugin name ${ + nodePorcelain.ref().address().pluginName + } for Git plugin!` + ); } - return new Commit(n.graph, n.nodeAddress); + super(nodePorcelain.ref(), nodePorcelain.node()); + } +} + +export class CommitReference extends GitReference { + constructor(ref: NodeReference) { + super(ref); + assertAddressType(ref.address(), COMMIT_NODE_TYPE); } hash(): Hash { return this.address().id; } - parents(): Commit[] { - return this.graph - .neighborhood(this.nodeAddress, { - nodeType: "COMMIT", - edgeType: "HAS_PARENT", - direction: "OUT", - }) - .map(({neighbor}) => new Commit(this.graph, neighbor)); + parents(): CommitReference[] { + return this.neighbors({ + nodeType: COMMIT_NODE_TYPE, + edgeType: HAS_PARENT_EDGE_TYPE, + direction: "OUT", + }).map(({ref}) => new CommitReference(ref)); } - tree(): Tree { - const trees = this.graph - .neighborhood(this.nodeAddress, { - nodeType: "TREE", - edgeType: "HAS_TREE", - direction: "OUT", - }) - .map(({neighbor}) => new Tree(this.graph, neighbor)); + tree(): TreeReference { + const trees = this.neighbors({ + nodeType: TREE_NODE_TYPE, + edgeType: HAS_TREE_EDGE_TYPE, + direction: "OUT", + }).map(({ref}) => new TreeReference(ref)); if (trees.length !== 1) { throw new Error( - `Commit ${stringify(this.nodeAddress)} has wrong number of trees` + `Commit ${stringify(this.address())} has wrong number of trees` ); } return trees[0]; } + + get(): ?CommitPorcelain { + const x = super.get(); + if (x != null) { + return new CommitPorcelain(x); + } + } } -export class Tree extends BaseGitPorcelain { - static from(n: BaseGitPorcelain): Tree { - if (n.type() !== "TREE") { - throw new Error(`Unable to cast ${n.type()} to Tree`); - } - return new Tree(n.graph, n.nodeAddress); +export class CommitPorcelain extends GitPorcelain { + constructor(nodePorcelain: NodePorcelain) { + assertAddressType(nodePorcelain.ref().address(), COMMIT_NODE_TYPE); + super(nodePorcelain); + } + ref(): CommitReference { + return new CommitReference(super.ref()); + } +} + +export class TreeReference extends GitReference { + constructor(ref: NodeReference) { + super(ref); + assertAddressType(ref.address(), TREE_NODE_TYPE); } hash(): Hash { return this.address().id; } - entries(): TreeEntry[] { - return this.graph - .neighborhood(this.nodeAddress, { - nodeType: "TREE_ENTRY", - edgeType: "INCLUDES", - direction: "OUT", - }) - .map(({neighbor}) => new TreeEntry(this.graph, neighbor)); + entries(): TreeEntryReference[] { + return this.neighbors({ + nodeType: TREE_ENTRY_NODE_TYPE, + edgeType: INCLUDES_EDGE_TYPE, + direction: "OUT", + }).map(({ref}) => new TreeEntryReference(ref)); } - entry(name: string): ?TreeEntry { - return this.entries().filter((te) => te.name() === name)[0]; + entry(name: string): ?TreeEntryReference { + return this.neighbors({ + nodeType: TREE_ENTRY_NODE_TYPE, + edgeType: INCLUDES_EDGE_TYPE, + direction: "OUT", + }) + .filter( + ({edge}) => (edge: Edge).payload.name === name + ) + .map(({ref}) => new TreeEntryReference(ref))[0]; + } + + get(): ?TreePorcelain { + const x = super.get(); + if (x != null) { + return new TreePorcelain(x); + } } } -export class TreeEntry extends BaseGitPorcelain { - static from(n: BaseGitPorcelain): TreeEntry { - if (n.type() !== "TREE_ENTRY") { - throw new Error(`Unable to cast ${n.type()} to TreeEntry`); - } - return new TreeEntry(n.graph, n.nodeAddress); +export class TreePorcelain extends GitPorcelain { + constructor(nodePorcelain: NodePorcelain) { + assertAddressType(nodePorcelain.ref().address(), TREE_NODE_TYPE); + super(nodePorcelain); + } + ref(): TreeReference { + return new TreeReference(super.ref()); + } +} + +export class TreeEntryReference extends GitReference { + constructor(ref: NodeReference) { + super(ref); + assertAddressType(ref.address(), TREE_ENTRY_NODE_TYPE); } name(): string { - return this.node().payload.name; + const includesEdges = this.neighbors({ + nodeType: TREE_NODE_TYPE, + edgeType: INCLUDES_EDGE_TYPE, + direction: "IN", + }).map(({edge}) => edge); + if (includesEdges.length !== 1) { + throw new Error( + `Malformed tree structure at ${stringify(this.address())}` + ); + } + return (includesEdges[0]: Edge).payload.name; } - evolvesTo(): TreeEntry[] { - return this.graph - .neighborhood(this.nodeAddress, { - nodeType: "TREE_ENTRY", - edgeType: "BECOMES", - direction: "OUT", - }) - .map(({neighbor}) => new TreeEntry(this.graph, neighbor)); + evolvesTo(): TreeEntryReference[] { + return this.neighbors({ + nodeType: TREE_ENTRY_NODE_TYPE, + edgeType: BECOMES_EDGE_TYPE, + direction: "OUT", + }).map(({ref}) => new TreeEntryReference(ref)); } - evolvesFrom(): TreeEntry[] { - return this.graph - .neighborhood(this.nodeAddress, { - nodeType: "TREE_ENTRY", - edgeType: "BECOMES", - direction: "IN", - }) - .map(({neighbor}) => new TreeEntry(this.graph, neighbor)); + evolvesFrom(): TreeEntryReference[] { + return this.neighbors({ + nodeType: TREE_ENTRY_NODE_TYPE, + edgeType: BECOMES_EDGE_TYPE, + direction: "IN", + }).map(({ref}) => new TreeEntryReference(ref)); } - /* - * May be a single Tree, single Blob, or zero or more - * SubmoduleCommits. The Tree or Blob are put in an array for - * consistency. - * - */ - contents(): Tree[] | Blob[] | SubmoduleCommit[] { - // Note: the function has the correct type signature, - // but as-implemented it should be a flow error. - // When flow fixes this, maintain the current method signature. - return this.graph - .neighborhood(this.nodeAddress, { - edgeType: "HAS_CONTENTS", - direction: "OUT", - }) - .map(({neighbor}) => { - switch (neighbor.type) { - case "BLOB": - return new Blob(this.graph, neighbor); - case "TREE": - return new Tree(this.graph, neighbor); - case "SUBMODULE_COMMIT": - return new SubmoduleCommit(this.graph, neighbor); - default: - throw new Error(`Neighbor had invalid type ${neighbor.type}`); - } - }); + blob(): ?BlobReference { + return this.neighbors({ + edgeType: HAS_CONTENTS_EDGE_TYPE, + nodeType: BLOB_NODE_TYPE, + direction: "OUT", + }).map(({ref}) => new BlobReference(ref))[0]; + } + + tree(): ?TreeReference { + return this.neighbors({ + edgeType: HAS_CONTENTS_EDGE_TYPE, + nodeType: TREE_NODE_TYPE, + direction: "OUT", + }).map(({ref}) => new TreeReference(ref))[0]; + } + + submoduleCommits(): SubmoduleCommitReference[] { + return this.neighbors({ + edgeType: HAS_CONTENTS_EDGE_TYPE, + nodeType: SUBMODULE_COMMIT_NODE_TYPE, + direction: "OUT", + }).map(({ref}) => new SubmoduleCommitReference(ref)); + } + + get(): ?TreeEntryPorcelain { + const x = super.get(); + if (x != null) { + return new TreeEntryPorcelain(x); + } } } -export class Blob extends BaseGitPorcelain { - static from(n: BaseGitPorcelain): Blob { - if (n.type() !== "BLOB") { - throw new Error(`Unable to cast ${n.type()} to Blob`); - } - return new Blob(n.graph, n.nodeAddress); +export class TreeEntryPorcelain extends GitPorcelain { + constructor(nodePorcelain: NodePorcelain) { + assertAddressType(nodePorcelain.ref().address(), TREE_ENTRY_NODE_TYPE); + super(nodePorcelain); + } + ref(): TreeEntryReference { + return new TreeEntryReference(super.ref()); + } +} + +export class BlobReference extends GitReference { + constructor(ref: NodeReference) { + super(ref); + assertAddressType(ref.address(), BLOB_NODE_TYPE); } hash(): Hash { - return this.nodeAddress.id; + return this.address().id; + } + + get(): ?BlobPorcelain { + const x = super.get(); + if (x != null) { + return new BlobPorcelain(x); + } } } -export class SubmoduleCommit extends BaseGitPorcelain { - static from(n: BaseGitPorcelain): SubmoduleCommit { - if (n.type() !== "SUBMODULE_COMMIT") { - throw new Error(`Unable to cast ${n.type()} to SubmoduleCommit`); +export class BlobPorcelain extends GitPorcelain { + constructor(nodePorcelain: NodePorcelain) { + assertAddressType(nodePorcelain.ref().address(), BLOB_NODE_TYPE); + super(nodePorcelain); + } + ref(): BlobReference { + return new BlobReference(super.ref()); + } +} + +export class SubmoduleCommitReference extends GitReference< + SubmoduleCommitPayload +> { + constructor(ref: NodeReference) { + super(ref); + assertAddressType(ref.address(), SUBMODULE_COMMIT_NODE_TYPE); + } + + get(): ?SubmoduleCommitPorcelain { + const x = super.get(); + if (x != null) { + return new SubmoduleCommitPorcelain(x); } - return new SubmoduleCommit(n.graph, n.nodeAddress); + } +} + +export class SubmoduleCommitPorcelain extends GitPorcelain< + SubmoduleCommitPayload +> { + constructor(nodePorcelain: NodePorcelain) { + assertAddressType( + nodePorcelain.ref().address(), + SUBMODULE_COMMIT_NODE_TYPE + ); + super(nodePorcelain); } url(): string { - return this.node().payload.url; + return this.payload().url; } hash(): Hash { - return this.node().payload.hash; + return this.payload().hash; + } + ref(): SubmoduleCommitReference { + return new SubmoduleCommitReference(super.ref()); } } diff --git a/src/plugins/git/porcelain.test.js b/src/plugins/git/porcelain.test.js index 98d884f..a605914 100644 --- a/src/plugins/git/porcelain.test.js +++ b/src/plugins/git/porcelain.test.js @@ -1,14 +1,32 @@ // @flow import cloneDeep from "lodash.clonedeep"; -import {PorcelainGraph, Blob, Tree, SubmoduleCommit} from "./porcelain"; +import { + COMMIT_NODE_TYPE, + TREE_NODE_TYPE, + SUBMODULE_COMMIT_NODE_TYPE, + BLOB_NODE_TYPE, +} from "./types"; +import { + GraphPorcelain, + BlobReference, + TreeReference, + BlobPorcelain, + TreePorcelain, + TreeEntryReference, + TreeEntryPorcelain, + SubmoduleCommitPorcelain, + SubmoduleCommitReference, + CommitReference, + CommitPorcelain, +} from "./porcelain"; import {createGraph} from "./createGraph"; -const makePorcelainGraph = () => - new PorcelainGraph(createGraph(cloneDeep(require("./demoData/example-git")))); +const makeGraphPorcelain = () => + new GraphPorcelain(createGraph(cloneDeep(require("./demoData/example-git")))); const getCommit = () => { - const graph = makePorcelainGraph(); + const graph = makeGraphPorcelain(); const commitHash = "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f"; const commit = graph.commitByHash(commitHash); if (commit.hash() !== commitHash) { @@ -18,6 +36,30 @@ const getCommit = () => { } return commit; }; + +function really(x: ?T): T { + if (x == null) { + throw new Error(`It wasn't, really`); + } + return x; +} + +const getTree = () => { + return getCommit().tree(); +}; + +const getTreeEntry = (name: string) => { + return really(getTree().entry(name)); +}; + +const getBlob = () => { + return really(getTreeEntry("science.txt").blob()); +}; + +const getSubmoduleCommit = () => { + return getTreeEntry("pygravitydefier").submoduleCommits()[0]; +}; + describe("Git porcelain", () => { it("commits have hashes", () => { const commit = getCommit(); @@ -34,18 +76,17 @@ describe("Git porcelain", () => { it("some commits have no parents", () => { const commitHash = "c2b51945e7457546912a8ce158ed9d294558d294"; - const commit = makePorcelainGraph().commitByHash(commitHash); + const commit = makeGraphPorcelain().commitByHash(commitHash); expect(commit.parents()).toEqual([]); }); it("Commits have a unique, hash-identified tree", () => { - const tree = getCommit().tree(); + const tree = getTree(); expect(tree.hash()).toEqual("7be3ecfee5314ffa9b2d93fc4377792b2d6d70ed"); }); it("Trees have tree entries", () => { - const tree = getCommit().tree(); - const entries = tree.entries(); + const entries = getTree().entries(); expect(entries).toHaveLength(5); const entryNames = entries.map((x) => x.name()); expect(entryNames).toEqual( @@ -59,35 +100,18 @@ describe("Git porcelain", () => { ); }); it("Tree entries can have blobs", () => { - const entry = getCommit() - .tree() - .entry("science.txt"); - if (entry == null) { - throw new Error("Where is science?!"); - } - const blob: Blob = Blob.from(entry.contents()[0]); + const blob = getBlob(); expect(blob.hash()).toEqual("f1f2514ca6d7a6a1a0511957021b1995bf9ace1c"); }); it("Tree entries can have trees", () => { - const entry = getCommit() - .tree() - .entry("src"); - if (entry == null) { - throw new Error("Where is src?!"); - } - const tree: Tree = Tree.from(entry.contents()[0]); + const treeEntry = getTreeEntry("src"); + const tree = really(treeEntry.tree()); expect(tree.hash()).toEqual("78fc9c83023386854c6bfdc5761c0e58f68e226f"); }); it("Tree entries can have submodule commits", () => { - const entry = getCommit() - .tree() - .entry("pygravitydefier"); - if (entry == null) { - throw new Error("We've stopped defying gravity :("); - } - const sc: SubmoduleCommit = SubmoduleCommit.from(entry.contents()[0]); + const sc = really(getSubmoduleCommit().get()); expect(sc.hash()).toEqual("29ef158bc982733e2ba429fcf73e2f7562244188"); expect(sc.url()).toEqual( "https://github.com/sourcecred/example-git-submodule.git" @@ -97,7 +121,7 @@ describe("Git porcelain", () => { it("Tree entries can evolve to/from other tree entries", () => { const parentCommitHash = "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc"; const childCommitHash = "69c5aad50eec8f2a0a07c988c3b283a6490eb45b"; - const graph = makePorcelainGraph(); + const graph = makeGraphPorcelain(); const parentEntry = graph .commitByHash(parentCommitHash) .tree() @@ -112,4 +136,51 @@ describe("Git porcelain", () => { expect(parentEntry.evolvesTo()).toEqual([childEntry]); expect(childEntry.evolvesFrom()).toEqual([parentEntry]); }); + + describe("flow and runtime type verification", () => { + it("for Commit", () => { + const c: CommitReference = getCommit(); + const p: CommitPorcelain = really(c.get()); + const _: CommitReference = p.ref(); + const err = `to have type ${COMMIT_NODE_TYPE}`; + expect(() => new CommitReference(getTree())).toThrow(err); + expect(() => new CommitPorcelain(really(getTree().get()))).toThrow(err); + }); + it("for Tree", () => { + const c: TreeReference = getTree(); + const p: TreePorcelain = really(c.get()); + const _: TreeReference = p.ref(); + const err = `to have type ${TREE_NODE_TYPE}`; + expect(() => new TreeReference(getCommit())).toThrow(err); + expect(() => new TreePorcelain(really(getCommit().get()))).toThrow(err); + }); + it("for TreeEntry", () => { + const c: TreeEntryReference = getTreeEntry("src"); + const p: TreeEntryPorcelain = really(c.get()); + const _: TreeEntryReference = p.ref(); + const err = `to have type ${TREE_NODE_TYPE}`; + expect(() => new TreeEntryReference(getCommit())).toThrow(err); + expect(() => new TreeEntryPorcelain(really(getTree().get()))).toThrow( + err + ); + }); + it("for Blob", () => { + const c: BlobReference = getBlob(); + const p: BlobPorcelain = really(c.get()); + const _: BlobReference = p.ref(); + const err = `to have type ${BLOB_NODE_TYPE}`; + expect(() => new BlobReference(getCommit())).toThrow(err); + expect(() => new BlobPorcelain(really(getTree().get()))).toThrow(err); + }); + it("for SubmoduleCommit", () => { + const c: SubmoduleCommitReference = getSubmoduleCommit(); + const p: SubmoduleCommitPorcelain = really(c.get()); + const _: SubmoduleCommitReference = p.ref(); + const err = `to have type ${SUBMODULE_COMMIT_NODE_TYPE}`; + expect(() => new SubmoduleCommitReference(getCommit())).toThrow(err); + expect( + () => new SubmoduleCommitPorcelain(really(getTree().get())) + ).toThrow(err); + }); + }); }); diff --git a/src/plugins/git/render.js b/src/plugins/git/render.js index d809b44..cfd395c 100644 --- a/src/plugins/git/render.js +++ b/src/plugins/git/render.js @@ -1,14 +1,15 @@ // @flow -import type {Address} from "../../core/address"; -import type {Graph} from "../../core/graph"; -import type {NodeType, SubmoduleCommitPayload} from "./types"; +import {NodeReference} from "../../core/porcelain"; +import {SubmoduleCommitReference, GitReference} from "./porcelain"; /** * Describe a node provided by the Git plugin. */ -export function nodeDescription(graph: Graph, address: Address) { - const type: NodeType = (address.type: any); +export function nodeDescription(nodeReference: NodeReference) { + const gReference = new GitReference(nodeReference); + const type = gReference.type(); + const address = gReference.address(); switch (type) { case "COMMIT": return `commit ${address.id}`; @@ -17,8 +18,13 @@ export function nodeDescription(graph: Graph, address: Address) { case "BLOB": return `blob ${address.id}`; case "SUBMODULE_COMMIT": { - const payload: SubmoduleCommitPayload = graph.node(address).payload; - return `submodule commit ${payload.hash} in ${payload.url}`; + const scRef = new SubmoduleCommitReference(gReference); + const scPorcelain = scRef.get(); + if (scPorcelain != null) { + return `submodule commit ${scPorcelain.hash()} in ${scPorcelain.url()}`; + } else { + return `submodule commit [unknown]`; + } } case "TREE_ENTRY": return `entry ${address.id}`; diff --git a/src/plugins/git/render.test.js b/src/plugins/git/render.test.js index 5c6458a..d62e2a8 100644 --- a/src/plugins/git/render.test.js +++ b/src/plugins/git/render.test.js @@ -9,6 +9,7 @@ import type { } from "./types"; import type {Node} from "../../core/graph"; import {Graph} from "../../core/graph"; +import {NodeReference} from "../../core/porcelain"; import {_makeAddress, commitAddress} from "./address"; import {nodeDescription} from "./render"; import {submoduleCommitId, treeEntryId} from "./types"; @@ -19,9 +20,8 @@ describe("nodeDescription", () => { address: commitAddress("cafebabe"), payload: (({}: any): {||}), }; - expect(nodeDescription(new Graph().addNode(node), node.address)).toEqual( - "commit cafebabe" - ); + const ref = new NodeReference(new Graph().addNode(node), node.address); + expect(nodeDescription(ref)).toEqual("commit cafebabe"); }); it("describes trees", () => { @@ -29,9 +29,8 @@ describe("nodeDescription", () => { address: _makeAddress("TREE", "deadbeef"), payload: (({}: any): {||}), }; - expect(nodeDescription(new Graph().addNode(node), node.address)).toEqual( - "tree deadbeef" - ); + const ref = new NodeReference(new Graph().addNode(node), node.address); + expect(nodeDescription(ref)).toEqual("tree deadbeef"); }); it("describes blobs", () => { @@ -39,9 +38,8 @@ describe("nodeDescription", () => { address: _makeAddress("BLOB", "01010101"), payload: (({}: any): {||}), }; - expect(nodeDescription(new Graph().addNode(node), node.address)).toEqual( - "blob 01010101" - ); + const ref = new NodeReference(new Graph().addNode(node), node.address); + expect(nodeDescription(ref)).toEqual("blob 01010101"); }); it("describes submodule commits", () => { @@ -51,9 +49,8 @@ describe("nodeDescription", () => { address: _makeAddress("SUBMODULE_COMMIT", submoduleCommitId(hash, url)), payload: {hash, url}, }; - expect(nodeDescription(new Graph().addNode(node), node.address)).toEqual( - `submodule commit ${hash} in ${url}` - ); + const ref = new NodeReference(new Graph().addNode(node), node.address); + expect(nodeDescription(ref)).toEqual(`submodule commit ${hash} in ${url}`); }); it("describes tree entries", () => { @@ -63,8 +60,7 @@ describe("nodeDescription", () => { address: _makeAddress("TREE_ENTRY", treeEntryId(tree, name)), payload: {name}, }; - expect(nodeDescription(new Graph().addNode(node), node.address)).toEqual( - `entry ${tree}:${name}` - ); + const ref = new NodeReference(new Graph().addNode(node), node.address); + expect(nodeDescription(ref)).toEqual(`entry ${tree}:${name}`); }); });