Skip to content
This repository has been archived by the owner on Mar 10, 2020. It is now read-only.

feat: add bitswap.unwant javascript spec #254

Closed
wants to merge 1 commit into from
Closed

Conversation

wraithgar
Copy link
Contributor

@ghost ghost assigned wraithgar Apr 16, 2018
@ghost ghost added the in progress label Apr 16, 2018
Copy link
Contributor

@daviddias daviddias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a test.

@wraithgar
Copy link
Contributor Author

I'm very confused by the testing of this entire suite. Should the existing and new tests from js-ipfs move here? Should they be duplicated now?

@daviddias
Copy link
Contributor

@travisperson compiled some good notes on how it is all set up, it is good for understanding - https://github.com/ipfs/wg-qa-testing-dx/issues/35

As noted on the README https://github.com/ipfs/interface-ipfs-core#background

The primary goal of this module is to define and ensure that both IPFS core implementations and their respective HTTP client libraries offer the same interface, so that developers can quickly change between a local and a remote node without having to change their applications. In addition to the definition of the expected interface, this module offers a suite of tests that can be run in order to check if the interface is used as described.

This translates to: if there is a spec, we need a test to make sure js-ipfs and js-ipfs-api comply to it.

@wraithgar
Copy link
Contributor Author

There is no bitswap spec in this repo yet, but it appears that everything is implemented in go already. Does this just mean the tests were never added here? If I were to add tests here how would I make sure the go-ipfs lib passes against them?

@travisperson
Copy link
Contributor

Does this just mean the tests were never added here?

Basically. This project came along quite a bit after the go-ipfs project as a way to help make sure that different projects implemented the same API. For the JavaScript side of things, this was ensuring that the the js-ipfs-api (originally created to allow JavaScript projects to talk to the go-ipfs implementation) project and the js-ipfs core module implemented the same interface. There is a new project underway though to help with this effort from the JavaScript side (ipfs/js-ipfs#1260). David added some details in the linked issue above (https://github.com/ipfs/wg-qa-testing-dx/issues/35)

If I were to add tests here how would I make sure the go-ipfs lib passes against them?

There isn't a project that runs these interface tests against a golang daemon as far as I'm aware, but it should be fairly trivial to run them with js-ipfsd-ctl.

I've created a gist that you can clone.

git clone https://gist.github.com/f2eeb8e20be1e88a0a8f45a4869a4896.git interface-golang-test

$ pwd
/home/travis/src/github.com/ipfs/interface-golang-test/
$ yarn install
$ yarn test

To develop interface-ipfs-core using the above for the bitswap test, you will want to clone down this project, and link it with yarn (or npm, all commands here can swap yarn for npm).

$ pwd
/home/travis/src/github.com/ipfs/
$ git clone https://github.com/ipfs/interface-ipfs-core
$ cd interface-ipfs-core && yarn install && yarn link

From interface-golang-test, you can then link interface-ipfs-core which will use your cloned version of interface-ipfs-core instead of the one installed through yarn install

$ pwd
/home/travis/src/github.com/ipfs/interface-golang-test/
$ yarn link interface-ipfs-core

You should now be able to write a simple skeleton test inside of interface-ipfs-core introducing the bitswap tests.

diff --git a/js/src/bitswap.js b/js/src/bitswap.js
new file mode 100644
index 0000000..ae62fea
--- /dev/null
+++ b/js/src/bitswap.js
@@ -0,0 +1,39 @@
+
+/* eslint-env mocha */
+/* eslint max-nested-callbacks: ["error", 8] */
+
+'use strict'
+
+const chai = require('chai')
+const dirtyChai = require('dirty-chai')
+const expect = chai.expect
+chai.use(dirtyChai)
+
+module.exports = (common) => {
+  describe('.bitswap', () => {
+    let ipfs
+
+    before(function (done) {
+      // CI takes longer to instantiate the daemon, so we need to increase the
+      // timeout for the before step
+      this.timeout(60 * 1000)
+
+      common.setup((err, factory) => {
+        expect(err).to.not.exist()
+
+        factory.spawnNode((err, node) => {
+          expect(err).to.not.exist()
+          ipfs = node
+
+          done()
+        })
+      })
+    })
+
+    after((done) => common.teardown(done))
+
+    it('A test', () => {
+      expect(true).to.be.true()
+    })
+  })
+}
diff --git a/js/src/index.js b/js/src/index.js
index 9f527e1..90463eb 100644
--- a/js/src/index.js
+++ b/js/src/index.js
@@ -16,3 +16,4 @@ exports.key = require('./key')
 exports.stats = require('./stats')
 exports.repo = require('./repo')
 exports.bootstrap = require('./bootstrap')
+exports.bitswap = require('./bitswap')

Then you just need to add it to the end of (or replace) interface-golang-test/interface.spec.js.

 test.block(common)
 test.dht(common)
+test.bitswap(common)

This should get you setup to write and run tests.

Resources

Bitswap tests from the js-ipfs project
https://github.com/ipfs/js-ipfs/blob/master/test/core/bitswap.spec.js

An example of running tests from this project
https://github.com/ipfs/js-ipfs/blob/master/test/http-api/interface/block.js

Daemon Controller (spawns daemons, js and go)
https://github.com/ipfs/js-ipfsd-ctl

@wraithgar
Copy link
Contributor Author

I've created a bitswap spec test based on the existing cli/http/interface tests in js-ipfs. Not all of them, there is a suite of tests in js-ipfs that deals with transferring blocks that I will tackle next. Haven't tested this in go yet, just the js-ipfs (using npm link).

I think this is the right direction. Had to guess on how we would want to have tests for the "error when stopped" state that js-ipfs was doing.

@wraithgar
Copy link
Contributor Author

The javascript implementation of bitswap.wantlist currently returns an immediate result, whereas the go implementation uses either callbacks or promises. I modeled unwant after this so it also has this disparity.

Should the javascript implementation be changed?

@travisperson
Copy link
Contributor

Should the javascript implementation be changed?

Yes, I think it should be changed. If it's not I think we are breaking one of the main goals this project is trying to enforce

The primary goal of this module is to define and ensure that both IPFS core implementations and their respective HTTP client libraries offer the same interface, so that developers can quickly change between a local and a remote node without having to change their applications.

I think the interface has gotten away with being broken like this primary because with the CLI, the core module is started in offline mode, and CLI is the only use of js-ipfs (in the project) that runs the core module in offline mode. This may not be true for other use cases, such as IPFS Companion, which can use both js-ipfs-api and the core module in online mode.

@wraithgar
Copy link
Contributor Author

Thank you so much for your help in this issue @travisperson. I'll get the js-ipfs stuff changed so it matches go (I don't think it will effect the surface area much because as long as I change the cli and http hooks to expect that behavior they don't have to change their output).

If the js cli is the only thing that deals with offline mode I am wondering if the 'should error on offline' tests don't actually belong back in the js repo with specific cli tests? It doesn't feel part of spec at that point.

@wraithgar
Copy link
Contributor Author

I could not get this to work when switching to promisified calls. There was an error that popped up that it took me till today to finally track down. Ultimately it's good cause there's a bug in block get that we wouldn't have found otherwise. I don't know why it didn't surface before but I'm not in a position to dig too deeply into THAT after tracking down the bug.

I typed up my journey here https://gist.github.com/wraithgar/c05cd1980a9f73d9b36504706f3980bf

TLDR: block get can get back an empty result if the block was unwanted before it downloaded. The simple solution is to just check that it didn't get back undefined but perhaps this should be an error (get should return an error if the block was unwanted before it was gotten)

@wraithgar
Copy link
Contributor Author

Once we figure out what to do about the block get the original PR in js-ipfs can be updated and we can move forward on this.

@wraithgar wraithgar force-pushed the bitswap_unwant branch 2 times, most recently from ff1cc7e to bd226a4 Compare May 2, 2018 18:13
@wraithgar wraithgar force-pushed the bitswap_unwant branch 3 times, most recently from b90c42e to d196f84 Compare May 14, 2018 16:05
@wraithgar
Copy link
Contributor Author

wraithgar commented May 18, 2018

Update on tests: It would appear that the tests in js-ipfs and js-ipfs-api have amost complete overlap, many of which are running the tests from this repo. The difference being that api runs the tests against go. This is a piece of the puzzle I was missing. This manual attempt of running these tests w/ that gist code up above wasn't the right path, I see now.

I think the path forward is this, for adding a feature to js-ipfs (which affects this repo and js-ipfs-api too). If we can figure this out it will definitely help me and others trying to add new implementations to js-ipfs

  1. Add surface area to js-ipfs-api for feature, make sure tests in that repo still pass.
  2. Add feature to js-ipfs
  3. Add tests to interface-ipfs-core that test this new feature, making sure they pass in both js-ipsf-api and js-ipfs via npm link
  4. Merge code from # 3, publish
  5. Update # 1 to include newly published interface-ipfs-core, new tests should still pass
  6. Merge code from # 1, publish
  7. Update # 2 to include newly published js-ipfs-api, new tests should still pass
  8. Merge code from # 2, publish

@wraithgar
Copy link
Contributor Author

wraithgar commented May 23, 2018

This PR has been merged with master, and the tests have been tweaked to pass when run when linked back into the js-ipfs-api PR for this feature: ipfs-inactive/js-ipfs-http-client#754

HOWEVER: tests to not pass in that repo. It would appear that there is either a bug in the go implementation of bitswap.unwant or I am improperly testing it. I would appreciate help determining which is the case.

Steps to reproduce:

You will see that unwanting a key does not remove it from the wantlist. The tests in js-ipfs-api were only checking against an already empty wantlist.

@wraithgar
Copy link
Contributor Author

@achingbrain I wanted to ping you in this issue because it relates to what we were wanting to start discussing as per Monday's js-ipfs standup.

The comment I linked to in the js-ipfs-api repo is my initial attempt at a "script" for adding features into js-ipfs. It is my hope that it can be iterated on and tested here as I attempt to add more "done" icons to this list https://github.com/ipfs/ipfs/blob/master/IMPLEMENTATION_STATUS.md, and that eventually it can be an easily discoverable documentation about how testing is done (right now, not how it may be in the future). This will help others understand what is expected when they want to contribute to js-ipfs, since that is currently a rather complex and hard to discover process.

@wraithgar
Copy link
Contributor Author

While trying to get these tests passing in js-ipfs I came across another thing that I am going to need some help with.

Here is an example response for wantlist that the go implementation returns:

{ Keys: [
  { "/": "QmUBdnXXPyoDFXj3Hj39dNJ5VkN3QFRskXxcGaYFBB8CNR" }
] }

The go implementation of cid has a MarshalJSON function that returns cids in this way (an object indexed by "/") https://github.com/ipfs/go-cid/blob/63d4b33fcf1feda3c9f6ee8391851aaea9884344/cid.go#L407

I don't see this represented anywhere in js-cid or the js-ipfs code. Is this something we have to change, or are the js and go implementations different enough that the interface tests are going to have to change their assertions based on if we're testing against go or js?

@achingbrain
Copy link
Collaborator

achingbrain commented May 24, 2018

The IPLD spec says merkle-links should be an object with a / key that corresponds to either a string representation of the content hash or a merkle-path.

For a directory containing a file hello.txt with the content hello (ignoring the fact that all the hashes are different):

Go:

$ ipfs dag get QmdtgNzDnLKkhyziU3kXRtf4SfAeh1Lah4T5hwVdmXh6Br | underscore print
{
  "data": "CAE=",
  "links": [
    { "Name": "hello.txt", "Size": 64, "Cid": { "/": "QmcFUHo1DBpydnTSeuXszKgQqnXQCJq6Wu1BUdzEfuRahM" } }
  ]
}

JS:

$ jsipfs dag get QmU5PLEGqjetW4RAmXgHpEFL7nVCL3vFnEyrCKUfRk4MSq | underscore print
{
  "data": "CAE=",
  "links": [
    { "name": "hello.txt", "size": 14, "multihash": "QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN" }
  ],
  "size": 69
}

Neither seem to follow the spec. The go implementation uses sentence case for the keys and uses Cid where it should probably be link (although the spec does not mandate a key name for links within pseudo links - perhaps it should) but it does at least show the merkle-link with the correct / key. We should probably bring the js-ipfs implementation in line with that as a minimum.

@alanshaw
Copy link
Contributor

@wraithgar on the tests, the interface-ipfs-core tests will check that the core interface exposed by js-ipfs (or any other impl) works as expected. They don't test the HTTP API or the CLI for the same feature. So there's additional tests in js-ipfs for testing these interfaces.

I think there is possibly some unnecessary overlap due to interface-ipfs-core not existing until later.

AFAIK if you want to add a feature to js-ipfs you need to:

  • Add to js-ipfs core
  • Add to js-ipfs cli
  • Add to js-ipfs http
  • Add to js-ipfs-api
  • Add interface-ipfs-core tests for testing js-ipfs core, js-ipfs http (via js-ipfs-api) and js-ipfs-api
    • Enable these tests in both js-ipfs and js-ipfs-api
  • Add tests for js-ipfs cli

Hopefully I'm not missing anything there.

@wraithgar
Copy link
Contributor Author

wraithgar commented May 24, 2018

@alanshaw The interface tests in the js-ipfs repo are (mostly?) used for all three purposes (core, http, cli).

ETA: It looks like in js-ipfs the http-api tests mostly use the interface tests (with some local tests too, mostly matching the core tests. The cli tests don't have any interface tests.

@alanshaw
Copy link
Contributor

ETA: It looks like in js-ipfs the http-api tests mostly use the interface tests

Yes we can use the interface-ipfs-core tests to test the http interface via js-ipfs-api since js-ipfs-api exposes an interface-ipfs-core compatible interface.

(with some local tests too, mostly matching the core tests.

It would be good to identify and remove local tests that are duplicates.

The cli tests don't have any interface tests.

CLI tests are all here: https://github.com/ipfs/js-ipfs/tree/master/test/cli

The CLI does not expose an interface-ipfs-core compatible interface so we can't use interface-ipfs-core tests to test it, only local tests.

@alanshaw
Copy link
Contributor

Hope I'm helping here and not just re-iterating what you already know!

@wraithgar
Copy link
Contributor Author

@alanshaw you're helping. There is a lot of undocumented and assumed behavior in the testing suite. We've already found here that the go and js implementations aren't systematically compared against each other and in some places diverge (sometimes neither match the spec).

I'm trying to shake out these assumptions, and asking these questions is the way to do this.

With that, my next question is: what are we assuming the cli test are? Are they duplicates of the interface tests or are they simply testing the cli surface area (i.e. that the commands exist and can accept all the parameters we expect).

@alanshaw
Copy link
Contributor

I found this diagram super helpful when I started getting my head around this: https://github.com/ipfs/js-ipfs#code-architecture-and-folder-structure

With the CLI tests, yes they should be similar to the interface-ipfs-core tests because we need to assert that they accept at least all the inputs that interface-ipfs-core would pass and we need to assert that given these inputs they are "translating" them to the appropriate calls to the core. We also want to assert that the CLI formats the output from core correctly.

Additionally (and somewhat less importantly) the CLI will often have shorthand names for flags that should also be tested!

@wraithgar
Copy link
Contributor Author

Here's what I think we've determined

js-ipfs-api - Tests here mostly use the interface-ipfs-core tests, with some custom local tests. These local tests (when not specifically about testing something in the api code itself) should be moved to interface-ipfs-core. Also, tests run in this repo are testing the go implementation of ipfs.

js-ipfs - Tests here sometimes use the interface-ipfs-core tests, with lots of custom local tests. These local tests (when not specifically about testing something in the js implementation code itself) should be moved to interface-ipfs-core. Also, these tests are used to test both the implementation itself (i.e. calling these functions natively using the library) and the http interface. There are also custom tests that test the cli interface, and these never use the interface-ipfs-core tests. These should, when possible, duplicate all of the testing done in interface-ipfs-core as well as test cli-specific things (i.e. cli flag aliases).

If this is accurate, it gives me a clearer picture both of the current state of things, and where they should be moving. It also helps us in coming up with a path for adding or fixing things in js-ipfs that ensures that it's properly tested, and that the implementation here matches that of go.

@wraithgar wraithgar force-pushed the bitswap_unwant branch 3 times, most recently from 3f714d4 to 5d04e53 Compare June 1, 2018 17:10
@wraithgar
Copy link
Contributor Author

The tests here now pass in both js-ipfs-api and ipfs/js-ipfs#1306

Because the other two PRs I have that deal w/ adding bitswap implementations are so tightly coupled to this effort of sorting the testing out I will be rebasing them against this PR and we can likely deal with all three at once (this PR represents the bulk of the efforts, the rest is just adding the minimal code and tests for the other two features being added).

expect(err).to.not.exist()
withGo = id.agentVersion.startsWith('go-ipfs')
setTimeout(done, 250) //Give block.get time to take effect
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have a spawnNodeWithId to help reduce this code duplication around the project.

https://github.com/ipfs/interface-ipfs-core/blob/master/js/src/utils/spawn.js#L5,L14

nit: I'm not a huge fan of the timeout here. Does it really take a lot of time for the key to enter the want list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the timeout appeared to be cruft leftover from debugging other errors in the wantlist return that had to do with the disparity between go and the js implementations. It works fine without for me in both js-ipfs and js-ipfs-api.

Switched to spawnWithNodeId too.

@alanshaw
Copy link
Contributor

Closing as this was superseded by #267 (now merged)

@alanshaw alanshaw closed this Jun 18, 2018
@ghost ghost removed the in progress label Jun 18, 2018
@wraithgar wraithgar deleted the bitswap_unwant branch June 18, 2018 15:03
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants