Commit Graph

966 Commits

Author SHA1 Message Date
William Chargin 83151d9fac
Remove payload definitions from V3 `git/types.js` (#402)
Test Plan:
Existing Flow and unit tests suffice.

wchargin-branch: git-v3-remove-payloads
2018-06-20 15:32:12 -07:00
William Chargin 9347348dd7
Copy graph-independent V1 Git plugin code to V3 (#401)
Summary:
Many files are unchanged. Some files have had paths updated, or new
build/test targets added.

The `types.js` file includes payload type definitions. These are
technically independent of the graph abstraction (i.e., nothing from V1
is imported and the code all still works), but it of course implicitly
depends on the V1 model. For now, we include the entirety of this file,
just so that we have a clean copy operation. Subsequent commits will
strip out this extraneous code.

Suggest reviewing with the `--find-copies-harder` argument to Git’s
diffing functions.

Test Plan:
Running `yarn travis --full` passes. Running

    ./src/v3/plugins/git/demoData/synchronizeToGithub.sh --dry-run

yields “Everything up-to-date”.

wchargin-branch: git-v3-copy
2018-06-20 15:28:37 -07:00
William Chargin 281cb574d5
Greatly simplify GitHub edge tests (#400)
Summary:
We had `edgeExamples`, wherein we constructed examples of edge addresses
(not actual edges) by manual instantiation. We checked that these
matched snapshots, and then we also called `createEdge` a bunch to
create actual edges, and checked that those matched snapshots, too.
Consequently, we had twice as many snapshots as we needed, and also
defined twice as many edge addresses as we needed.

Test Plan:
Note that snapshot contents are either deleted or unchanged.

wchargin-branch: simplify-github-edge-tests
2018-06-19 16:01:26 -07:00
William Chargin b9c01d13c9
Use `edgeToParts` in the GitHub edge tests (#399)
Test Plan:
Observe that the new snapshots are easier to read. Might as well make
sure that they encode the same data as the old snapshots, too. Note that
the backslash character no longer appears in this snapshot file. :-)

wchargin-branch: use-edgeToParts
2018-06-19 15:55:10 -07:00
William Chargin aac2fc6792
Add `edgeToParts` convenience export from `Graph` (#398)
Summary:
We have `edgeToString`, which formats edges as nicely human-readable
strings. However, these strings have some quotes in them, and so when
they are themselves stringified (e.g., as part of a Jest snapshot), they
become much harder to read. We thus introduce `edgeToParts` to make our
snapshots more readable.

Test Plan:
Unit tests added; run `yarn travis`.

wchargin-branch: add-edgeToParts
2018-06-19 15:48:04 -07:00
William Chargin ea74955a66
Fix GitHub node `fromRaw` error-path test cases (#397)
Summary:
In #394, we uppercased the constants for GitHub node types. However, we
were using string literals instead of constants in the test cases. These
test cases were supposed to cover every error path, but instead ended up
just covering the “bad type” error path many times.

Any one of the following would have prevented this regression:

 1. using string constants instead of literals in the test case;
 2. throwing and checking more precise error messages; or
 3. being alerted that coverage decreased as a result of the change.

In this commit, we enact the first of these options. I’m open to adding
a coverage bot, but don’t feel strongly about it at this time.

Test Plan:
Running `yarn coverage` now shows 100% coverage for the `nodes.js`
module, whereas previously almost all `throw fail();` lines were
uncovered (and the branch coverage was just 76%).

wchargin-branch: fix-github-node-error-tests
2018-06-19 14:57:14 -07:00
Dandelion Mané ed3397f654
Add GitHub prefixes and const types (#395)
- Switch string constant node and edge types (e.g. "REPO") to exported
consts (eg `export const REPO_TYPE`).
- Add (and internally use) a `_Prefix` psuedomodule which contains
per-type address prefixes
- Test that constructing a StructuredAddress with the wrong type is an
error.

Test plan:
Unit tests pass, snapshots unchanged.

Paired with @wchargin
2018-06-14 15:01:33 -07:00
Dandelion Mané a8bf6a36bf
Add `CommentableAddress` (#396)
Test plan: Not needed.

Paired with @wchargin
2018-06-14 14:52:29 -07:00
William Chargin b6eebddeb0
Use uppercase enum constants in GitHub addresses (#394)
Summary:
@decentralion wants this! :-)

Test Plan:
Verify that the case-insensitive diff is empty:
```
$ git config --global difftool.idiff.cmd 'diff -ui "$LOCAL" "$REMOTE"'
$ git difftool -y --tool idiff HEAD~1..HEAD
```

wchargin-branch: uppercase-enum
2018-06-14 13:45:55 -07:00
William Chargin 7ce4a0c32d
Use `NodeAddress.empty` and `EdgeAddress.empty` (#393)
Summary:
This fixes up all instances of `fromParts([])` that are not in
`address.js` or `address.test.js`.

Paired with @decentralion.

Test Plan:
Running `git grep --name-only -F 'fromParts([])'` yields only the two
modules listed above. Existing unit tests suffice for correctness.

wchargin-branch: use-address-empty
2018-06-14 13:11:08 -07:00
William Chargin 6ba6d885ad
Add `empty` (monoid identity) to address modules (#392)
Summary:
This can make invocations of `FooAddress.fromParts([])` a bit more
succinct.

Paired with @decentralion.

Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-empty
2018-06-14 13:06:26 -07:00
Dandelion Mané 7199586262
Add `Graph.edges` filtering by prefixing (#391)
Similar to #390, we now allow filtering the results from `Graph.edges`
by address prefixes. It's a little more complicated than #390, as we
allow filtering by src, dst, or address.

Test plan:
Unit tests added. `yarn travis` passes.

Paired with @wchargin
2018-06-14 11:59:58 -07:00
Dandelion Mané 1a08a48c03
Implement prefix filtering for `Graph.nodes` (#390)
Simple API addition to match v1/v2 semantics.
In the future, we can perf optimize this if we switch graph to
store nodes organized by shared prefixes.

Test plan:
Unit tests were added. `yarn travis` passes.

Paired with @wchargin
2018-06-14 11:18:47 -07:00
Dandelion Mané 95c5af36d9
Add methods for parsing Ids from GitHub urls (#388)
Test plan:
Unit tests added. Run `yarn travis`.

The GitHub regex code is inspired by work in #98
2018-06-13 16:25:15 -07:00
William Chargin 2491fcd3cb
Add GitHub `edges` module (#385)
Summary:
This module includes a raw edge type, a structured edge type, and edge
creation functions that take source and destination and create an edge.

Test Plan:
Unit tests added. These cover all of the successful cases, and none of
the unsuccessful cases. We plan to refactor this code Soon™, and it is
hard to see how to nicely factor the tests without just testing the same
code paths over and over.

wchargin-branch: github-edges
2018-06-13 16:19:50 -07:00
Dandelion Mané 17b390afe9
Add trait-specific GitHub address types (#387)
Will be useful in graph creation logic, and in #385

Test plan: Only change is to add types. No testing needed.

See design discussion [on discord].

Paired with @wchargin

[on discord]: https://discordapp.com/channels/453243919774253079/454007907663740939?jump=456564101183832064
2018-06-13 14:42:37 -07:00
William Chargin 25f74b89e9
Export `_githubAddress` from GitHub `nodes` (#384)
Summary:
We’ll want to use this in the upcoming `edges` module.

Test Plan:
Existing unit tests suffice.

wchargin-branch: expose-githubaddress
2018-06-13 13:53:09 -07:00
Dandelion Mané ad9ac55bef
Github Addresses: Rename `fragment` to `id` (#386)
Test plan:
`yarn travis`
2018-06-13 13:41:52 -07:00
William Chargin 748f9210a6
Rename various aspects of GitHub `nodes` module (#383)
Summary:
First, we rename the module itself from `address` to `nodes`: we’d like
to put the edge functions in a parallel `edges` module instead of
cramping it into this one, so it stands to reason that this one should
be called `nodes`.

We also rename the `GithubAddressT` type to `RawAddress`, so that the
module exports `RawAddress` and `StructuredAddress`. The functions then
have much better natural names of `toRaw` and `fromRaw`.

Test Plan:
Existing unit tests suffice.

wchargin-branch: rename-nodes
2018-06-12 18:09:41 -07:00
Dandelion Mané e4d9ce1565
gh plugin: use consistent concise naming for pulls (#381)
One of the slight modifications we've made in v3 is to effect the
following renames (as implemented in #380):

PullRequest -> Pull
PullRequestReview -> Review
PullRequestReviewComment -> ReviewComment

This commit just changes the rest of the github code in v3 to follow the
new convention.

Test plan:
`yarn travis --full` passes.
2018-06-12 14:15:55 -07:00
William Chargin a3f2b82073
Add snapshot test for GitHub GraphQL query (#382)
Summary:
This has two primary benefits:
  - Humans can look at this snapshot file to see what’s being queried,
    or to manually issue a query.
  - When we change the programmatically generated query, we can easily
    see what the results are in the GraphQL output. This makes it easy
    to verify that a change is correct.

Test Plan:
None.

wchargin-branch: snapshot-query
2018-06-12 14:09:58 -07:00
Dandelion Mané 773596755a
Add an address module for the GitHub plugin (#380)
Summary:
This module exposes a structured type `StructuredAddress`, an embedding
`GithubAddressT` of this type into the `NodeAddress` layer, and
functions to convert between the two.

Paired with @wchargin.

Test Plan:
Unit tests added, with full coverage. Snapshots are easily readable.
2018-06-12 11:05:09 -07:00
Dandelion Mané 0339d9f41b
Port GitHub data ingestion into v3 (#378)
This commit copies the following logic necessary for downloading GitHub
data into v3. Minimal changes have been made to accomodate the new path
structure.

Test plan:
- Manually ran plugins/github/fetchGithubRepoTest.sh and verified that
it can correctly pass and fail
- Added the v3 github repo test to `yarn travis --full`
- Ran `yarn travis --full` and it passed

Paired with @wchargin
2018-06-11 18:57:37 -07:00
William Chargin ed70947c63
Update GitHub example repository data (#379)
Summary:
We’ve added a comment directly on a pull request.

Paired with @decentralion.

Test Plan:
`yarn travis --full` passes.

wchargin-branch: update-example-github
2018-06-11 18:53:57 -07:00
William Chargin 5d3cfd82e4
Check graph invariants during tests (#372)
Summary:
Each of the invariants listed at the top of the `Graph` class is now
explicitly checked by `checkInvariants`, which is called at the end of
each `Graph` method during tests only. This is powerful: it means that
not only do our tests for `Graph` test the graph, but also any tests
that depend on `Graph`—e.g., plugin code—will give us extra invariant
testing on `Graph`. As noted in a comment, if this becomes bad for
performance, we can blacklist expensive tests or whitelist tests that we
care about.

A graph method may assume that the graph invariants hold before the
method is invoked. Within the body of a graph method, invariants may be
violated, but the method must ensure that the invariants hold
immediately before it returns or yields. A consequence of this is that
if a graph function internally calls a public function (e.g., `addEdge`
might call `hasNode` to check that the source and destination exist),
then it must ensure that the invariants hold before the internal call.
This is not an “implementation detail” or “caveat”; it is simply part of
the interface of public functions. It is legal and reasonable for
private helper functions to explicitly not expect or not guarantee that
particular invariants hold, and in this case the exception should be
documented. (This is not yet the case in any of our code.)

Finally, note that the `checkInvariants` method should not call any
public methods, because those methods in turn call `checkInvariants`. If
this becomes a huge pain, we can look into implementing some kind of
“only check invariants if the invariants are not actively being
checked”, but I’d much rather not do so if we don’t have to.

Test Plan:
Running `yarn coverage` indicates that each of the failure cases is
verified. In principle, I’d be willing to add a test that parses the
source code for `graph.js` and verifies that each `return`, `yield`, or
implicit return is preceded by an invariant check. But I don’t really
want to implement that right now.

wchargin-branch: automatic-invariants
2018-06-11 12:28:25 -07:00
Dandelion Mané c352b5b8d6
Add an `advancedGraph` test case (#377)
The `advancedGraph` is an example graph defined in `graph.test.js`.
It shows off many tricksy features, like having loop edges, multiple
edges from the same src to same dst, etc. We also provide two ways of
constructing it: `graph1` is straightforward, `graph2` adds tons of
spurious adds, removes, and odd ordering. This way we can ensure that
our functions treat `graph1` and `graph2` equivalently.

Test plan:
New unit tests are added verifying that `equals`, `merge`, and
`to/fromJSON` handle the advanced graph appropriately.
2018-06-11 12:08:53 -07:00
Dandelion Mané 5fc0d42c1f Implement `Graph.toJSON` and `Graph.fromJSON` (#374)
The serialization scheme uses `IndexedEdge`s:

```js
type Integer = number;
type IndexedEdge = {|
  Address: EdgeAddressT,
  srcIndex: Integer,
  dstIndex: Integer,
|}
```

The nodes are first sorted. Then, we generate indexed edges from the
regular edges by replacing each node address with its index in the
sorted order. This encoding reduces the number of addresses serialized
from `n + 3e` to `n + e` (where `n` is the number of nodes and `e` is
the number of edges).

This is based on work in #295, but in contrast to that PR, we do not
index the in-memory representations of graphs. Only the JSON
representation is indexed.

Test plan:
Unit tests added. A snapshot test is also included, both to make it easy
to inspect an example of a JSON-serialized graph, and to ensure
backwards-compatibility. (The snapshot likely should not change
independent of the VERSION string.)
2018-06-11 11:57:29 -07:00
Dandelion Mané 6177f6c740
Reimplement `Graph.copy` using `Graph.merge` (#376)
* Implement `Graph.merge`

Tests are mostly copied over from the v2, as implemented in #320.
Some new tests were added, e.g. checking that Merge correctly handles
10 small graphs combined.

Test plan:
See unit tests.

* Reimplement `Graph.copy` using `Graph.merge`

Test plan:
Existing unit tests suffice

Suggested by @wchargin
2018-06-11 10:55:52 -07:00
Dandelion Mané 46751d2707
Implement `Graph.merge` (#375)
Tests are mostly copied over from the v2, as implemented in #320.
Some new tests were added, e.g. checking that Merge correctly handles
10 small graphs combined.

Test plan:
See unit tests.
2018-06-11 10:55:30 -07:00
Dandelion Mané 5fde1c10a5
Copy `v1/util` to `v3/util` (#373)
No changes made to the code - it's a straight copy.

Test plan:
Unit tests are included.
2018-06-11 10:42:25 -07:00
William Chargin 831f5f571c
Make invariants more precise (#371)
Summary:
The previously listed invariants were weak on two counts. First, it was
unstated that the keys of `_inEdges`, `_outEdges`, and `_nodes` should
coincide. Second, the “exactly once” condition on edge inclusion had the
unintentional effect that edge absent in `_edges` but present twice or
more in each of `_inEdges` and `_outEdges` would not violate the
invariant.

Test Plan:
Stay tuned.

wchargin-branch: strengthen-invariants
2018-06-08 15:19:25 -07:00
Dandelion Mané d9e2850eb3
Implement `Graph.copy` (#370)
The implementation is quite simple. The tests are somewhat more
comprehensive than in v2 or v1. We now test that copies are equal to the
original in a variety of situations.

Test plan:
Unit tests added.
2018-06-08 15:19:13 -07:00
Dandelion Mané feef119250
Add and implement `Graph.equals` (#369)
It turns out we forgot to add this to the API, so I added it. I also
implemented it. The tests are pretty thorough; as an added innovation
over our previous tests (e.g. in #312 and #61), we now consistently test
that equality is commutative.

In contrast to our previous implementations, this one is massively
simpler. That's an upside of using primitive ES6 data structures to
store all of the graph's information... which is itself an upside of not
trying to store arbitrary additional information in the graph. Now we
can just do a deep equality check on the underlying nodes set and edges
map!

We might be able to performance tune this method by taking advantage of
the structure of our nodes and edges. This should suffice for now,
though.

Paired with @wchargin

Test plan:
Unit tests were added. Run `yarn travis`
2018-06-08 14:17:57 -07:00
Dandelion Mané 4a441eb287
Implement `Graph.neighbors` (#368)
This is the change that puts the Graph into `Graph` :) We add `_inEdges`
and `_outEdges`, and use them to identify the neighbors of a given
`node`.

The API is implemented pretty uncontroversially. (We've done this a few
times before: see #319, #162). As with other iterators, we check for
comodification and error if this has occurred.

The tests cover some interesting cases like absent nodes, loops, and
multiple edges with the same src and dst.

Test plan:
Unit tests have been added. Run `yarn travis`.

Paired with: @wchargin
2018-06-08 13:34:43 -07:00
William Chargin 25df874db7
Check concurrent modification in graph iterators (#367)
Summary:
A client of `Graph` is able to (e.g.) invoke `nodes()` to get a node
iterator, iterate over some of the nodes, then change the nodes by
adding or removing the nodes on the original graph. The semantics of
what to do here are not clear: ES6 specifies semantics for `Map` and
`Set`, but they have counterintuitive consequences: for instance, you
can get a `Set` iterator to yield the same value twice. Most collection
implementations in the Java standard library prohibit this entirely. In
this commit, we adopt the latter approach.

A caveat of this implementation is that a graph object may not be
mutated more than 2^53 − 1 (`Number.MAX_SAFE_INTEGER`) times. Clients
who need to mutate a graph more than nine quadrillion times are
encouraged to reconsider their data model.

Paired with @decentralion.

Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: comod-check
2018-06-08 13:03:26 -07:00
Dandelion Mané b8298123ef
Add tests for adding a conflicting edge (#366)
Adding a conflicting edge (i.e. one with the same address, but different
`src` or `dst`) is an error. However, our coverage has determined that
the behavior isn't tested. This commit adds that test.

Test plan:
Only change is adding a new test. Verify `yarn travis` passes.
2018-06-08 12:30:50 -07:00
Dandelion Mané d90cd3cf1b
Update README to reference our Discord (#363) 2018-06-08 12:30:44 -07:00
William Chargin 55d5c58e05
Add a `coverage` package script (#365)
Summary:
We’re not mandating anything about coverage right now, but by making it
easier to track coverage perhaps people will organically become more
motivated to write good tests.

Test Plan:
Run `yarn coverage`, and then open `coverage/lcov-report/index.html`.

wchargin-branch: coverage
2018-06-08 11:32:27 -07:00
William Chargin 4a70db0db1
Ignore coverage output in Prettier (#364)
Test Plan:
Run `yarn test --env=jsdom --coverage`, and note that files are
generated into `coverage/`. Then, run `yarn travis`, which fails before
this patch (on `check-pretty`) and passes after it.

wchargin-branch: prettier-ignore-coverage
2018-06-08 10:50:52 -07:00
William Chargin abb44883cd
Remove legacy V3 `NodeAddress`/`EdgeAddress` (#362)
Summary:
These are superseded by the unified implementation in `address.js`.

Test Plan:
Existing unit tests suffice.

wchargin-branch: remove-legacy-address
2018-06-07 09:35:18 -07:00
William Chargin 70c1f64854
Use new unified addresses in Graph V3 (#361)
Summary:
This is basically a textual substitution.

Test Plan:
Existing unit tests suffice. Note that `_address.js` (with underscore)
is no longer imported except from its own tests.

wchargin-branch: unified-addresses-in-graph
2018-06-07 09:32:06 -07:00
William Chargin fe1dd326ed
Add unified address `hasPrefix` (#360)
Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-hasprefix
2018-06-07 09:28:23 -07:00
William Chargin 083bd59514
Add unified address `append` (#359)
Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-append
2018-06-07 09:24:53 -07:00
William Chargin 0bf1ef8af2
Add unified address `toString` (#358)
Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-tostring
2018-06-07 09:20:55 -07:00
William Chargin 32aba15b01
Add unified address assertions, fromParts, toParts (#357)
Summary:
This implements the following functions for the unified addresses:

  - assertions: `assertValid`, `assertValidParts`
  - injection: `fromParts`
  - projection: `toParts`

(These functions depend on each other for testing, so we implement them
together.)

Test Plan:
Unit tests included. Run `yarn travis`.

wchargin-branch: address-assertion-injection-projection
2018-06-07 09:16:46 -07:00
William Chargin c1a5e01a2c
Add top-level address module error handling (#356)
Summary:
This commit implements all the code needed for the top-level
`makeAddressModule` function, without implementing any of the address
functions. This mostly comprises handling of errors in the module
options.

Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-error-handling
2018-06-07 09:10:11 -07:00
William Chargin fc7e8886b1
Add foundations to unify address implementations (#355)
Summary:
We have `NodeAddress` and `EdgeAddress`, which are opaque aliases of
`string` each with separate associated functions. We really want to keep
this setup: having the address types be structurally distinct is very
nice. But currently the implementation is extremely repetitive. Core
functionality is implemented twice, once for nodes and once for edges.
The test code is even worse, as it is forced to include ugly, hacky,
parametric generalizations to test both implementations—which are really
the same code, anyway!

In this commit, we introduce a system to unify the _implementations_
while keeping the _APIs_ separate. That is, users still see separate
opaque types `NodeAddressT` and `EdgeAddressT`. Users now also see
separate modules `NodeAddress` and `EdgeAddress`, each of which
implements the same interface for its appropriate type. These modules
are each implemented by the same address module factory.

To get this to work, we clearly need to parameterize the module type
over the address type. The problem is getting this to work in a way that
interacts nicely with the opaque types. The trick is to let the factory
always return a transparent module at type `string`, but to then
specialize the type of the resulting module in the module in which the
underlying type of the opaque type is known.

This commit includes specifications for all functions that are in the
current version of the API, but includes only as much implementation
code as is needed to convince me that tests and Flow are actually
running (i.e., very little). I’ll send implementations in separate PRs
for easier review.

The preliminary modules created in this commit _are_ exported from the
graph module, even though they are incomplete. This is so that we can be
sure that nothing will catch fire in Flow-land when we try to export
them (which is plausible, given that we have nontrivial interactions
between opaque types and parametric polymorphism).

Test Plan:
Unit tests included. Run `yarn travis`.

wchargin-branch: address-unified-foundations
2018-06-07 09:06:28 -07:00
William Chargin 08cb60e762
Make all `neighbors` options required (#354)
Summary:
In actual code, we almost always call `neighbors` with a specified
direction. Usually, you want to inspect some relation like “parents of
this commit” or “GitHub nodes referenced by this comment”, and so the
edge direction matters. In each of the above cases, forgetting to
include the direction would introduce a bug: you’d get parents _and
children_ of a commit, or GitHub nodes referenced by _or that refer to_
a comment. It’s easy to forget this when writing the code, so we prefer
to make an explicit direction required, and allow people to specify
`Direction.ANY` in the case that that is what they want.

(In other words: we want to make the common thing easy, the uncommon
thing possible, and the wrong thing impossible.)

A similar situation holds for filters. By forcing clients to think about
what kinds of edges they want to follow to what kinds of nodes, we
encourage them to write more robust code. As before, if clients do want
to consider all nodes or all edges, they can pass the appropriate empty
address (`nodeAddress([])` or `edgeAddress([]`), which is a prefix of
every address.

Therefore, we require that clients pass an `options` object to
`neighbors`, and we furthermore require that each of the three options
be present.

Paired with @decentralion, in spirit.

Test Plan:
None; this changes the API for a function that has no implementation or
clients.

wchargin-branch: neighbors-options-required
2018-06-06 15:39:08 -07:00
William Chargin dfaa7d9764
Pull `Direction` values into an enum (#353)
Summary:
This saves clients from having to pollute their global namespace with
`IN` and `OUT` (and, soon, `ANY`). Calls now look like:
```js
myNode.neighbors({direction: Direction.OUT});
```
Callers who prefer to pollute their namespaces are of course still
welcome to do so, at the cost of one `const {IN, OUT} = Direction`.

Test Plan:
New unit tests for frozenness included.

wchargin-branch: direction-enum
2018-06-06 11:54:20 -07:00
William Chargin 4a06485a99
Add address prefix testing functions (#352)
Summary:
These functions can be used for address filtering: finding all nodes
owned by a plugin, or all edges of a certain plugin-type, or similar.
Clients can implement this in terms of `toParts`, but when the
underlying type of the opaque type is known there exists a simpler and
more efficient implementation.

Test Plan:
Unit tests added. Run `yarn travis`.

wchargin-branch: address-prefix
2018-06-06 11:49:54 -07:00