From 2740c2aa406b68befcaa89deee6f465b9643f1be Mon Sep 17 00:00:00 2001 From: jonasnick Date: Wed, 14 May 2014 00:07:40 +0200 Subject: [PATCH] Test peer discovery and fetching market page. --- README.md | 3 ++ Vagrantfile | 2 +- features/environment.py | 5 +++ features/p2p.feature | 23 ++++++++-- features/steps/p2p.py | 86 +++++++++++++++---------------------- features/steps/test_util.py | 54 ++++++++++++++++++----- 6 files changed, 107 insertions(+), 66 deletions(-) create mode 100644 features/environment.py diff --git a/README.md b/README.md index 7ac3cdaff..72b84ad40 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,9 @@ To get the identity server running for querying nicknames in the UI you need to `python ident/identity.py` +### Tests +Install [behave](https://github.com/behave/behave) and run `behave` in the OpenBazaar folder. The tests themselves are located in the `features` folder. + ## Artwork Contributions diff --git a/Vagrantfile b/Vagrantfile index a3e71bea1..deb286786 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -17,7 +17,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| apt-get update apt-get install -y build-essential python-dev python-pip python-zmq mongodb pip install tornado Twisted - easy_install pymongo websocket + easy_install pymongo websocket behave cp -R /vagrant/ecdsa /vagrant/pyelliptic /vagrant/obelisk /usr/local/lib/python2.7/dist-packages/ mongo --eval "db = db.getSiblingDB('openbazaar')" SCRIPT diff --git a/features/environment.py b/features/environment.py new file mode 100644 index 000000000..b968ded4a --- /dev/null +++ b/features/environment.py @@ -0,0 +1,5 @@ +def after_scenario(context, scenario): + if hasattr(context, 'proc'): + for proc in context.proc: + proc.terminate() + proc.join() diff --git a/features/p2p.feature b/features/p2p.feature index 3792aa301..25b2d44e1 100644 --- a/features/p2p.feature +++ b/features/p2p.feature @@ -6,6 +6,23 @@ Feature: Websocket Client Interface Then it will introduce itself Scenario: Connect to a node - Given there are two nodes - When a node connects to another - Then they will have each other as peers + Given there are 2 nodes + When node 0 connects to node 1 + Then node 0 is connected to node 1 + And node 1 is connected to node 0 + + Scenario: Peer discovery + Given there are 3 nodes + When node 0 connects to node 1 + And node 1 connects to node 2 + Then node 0 is connected to node 1 + And node 1 is connected to node 0 + And node 1 is connected to node 2 + And node 2 is connected to node 1 + And node 0 is connected to node 2 + And node 2 is connected to node 0 + + Scenario: Fetch Market Page + Given there are 3 connected nodes + Then node 0 can query page of node 1 + And node 0 can query page of node 2 diff --git a/features/steps/p2p.py b/features/steps/p2p.py index 5d596e21e..7a1233e73 100644 --- a/features/steps/p2p.py +++ b/features/steps/p2p.py @@ -1,71 +1,53 @@ from behave import * -from node.tornadoloop import start_node from threading import Thread from test_util import * -import time -from multiprocessing import Process @given('there is a node') def step_impl(context): - context.proc = Process(target=start_node, - args=('ppl/default', - '127.0.0.1', - 12345, - None, - 'test1.log')) - context.proc.start() - time.sleep(0.1) + create_nodes(context, 1) @when('we connect') def step_impl(context): - context.response = ws_connect(8888) + context.response = ws_connect(0) @then('it will introduce itself') def step_impl(context): assert context.response['result']['type'] == u'myself' - context.proc.terminate() - time.sleep(0.1) -@given('there are two nodes') -def step_impl(context): - context.proc = [] - context.proc.append(Process(target=start_node, - args=('ppl/default', '127.0.0.1', 12345, None, 'test1.log'))) - time.sleep(0.1) - context.proc.append(Process(target=start_node, - args=('ppl/default', '127.0.0.2', 12345, None, 'test1.log'))) - context.proc[0].start() - context.proc[1].start() - time.sleep(0.1) - - -@when('a node connects to another') -def step_impl(context): - response1 = ws_connect(8888) - response2 = ws_connect(8889) - context.pubkeys = [] - context.pubkeys.append(response1[u'result'][u'pubkey']) - context.pubkeys.append(response2[u'result'][u'pubkey']) - print context.pubkeys - ws_send(8888, 'connect', {'uri': 'tcp://127.0.0.2:12345'}) - time.sleep(0.5) +@given('there are {num_nodes} connected nodes') +def step_impl(context, num_nodes): + create_connected_nodes(context, int(num_nodes)) -@then('they will have each other as peers') -def step_impl(context): - response1 = ws_send(8888, 'peers') - response1 = response1[u'result'] - response2 = ws_send(8889, 'peers')[u'result'] - print 'resp1', response1 - print 'resp2', response2 - assert response1['type'] == u'peers' - assert response1['type'] == u'peers' - assert response1['peers'][0]['pubkey'] == context.pubkeys[1] - assert response1['peers'][0]['uri'] == u'tcp://127.0.0.2:12345' - assert response2['peers'][0]['pubkey'] == context.pubkeys[0] - assert response2['peers'][0]['uri'] == u'tcp://127.0.0.1:12345' - context.proc[0].terminate() - context.proc[1].terminate() +@given('there are {num_nodes} nodes') +def step_impl(context, num_nodes): + create_nodes(context, int(num_nodes)) + + +@when('node {i} connects to node {j}') +def step_impl(context, i, j): + ws_send(int(i), 'connect', {'uri': node_addr(int(j))}) + + +@then('node {i} is connected to node {j}') +def step_impl(context, i, j): + pubkey_j = ws_connect(int(j))[u'result'][u'pubkey'] + + response = ws_send(int(i), 'peers')[u'result'] + assert response['type'] == 'peers' + assert {'pubkey': pubkey_j, 'uri': node_addr(int(j))} in response['peers'] + + +@then('node {i} can query page of node {j}') +def step_impl(context, i, j): + pubkey_j = ws_connect(int(j))[u'result'][u'pubkey'] + + response = ws_send(int(i), 'query_page', {'pubkey': pubkey_j})[u'result'] + f = open('ppl/default') + data = json.loads(f.read()) + assert response[u'type'] == u'page' + assert response['nickname'] == data['nickname'] + assert response['text'] == data['nickname'] + ': ' + data['desc'] diff --git a/features/steps/test_util.py b/features/steps/test_util.py index f3bce351e..84913753a 100644 --- a/features/steps/test_util.py +++ b/features/steps/test_util.py @@ -1,10 +1,16 @@ import json +import time +from multiprocessing import Process + +from node.tornadoloop import start_node from tornado.ioloop import IOLoop from tornado import gen from tornado.websocket import websocket_connect -def ws_connect(port): +def ws_connect(node_index): + port = str(node_to_ws_port(node_index)) + @gen.coroutine def client(): client = yield websocket_connect('ws://localhost:%s/ws' % port) @@ -13,6 +19,7 @@ def client(): return IOLoop.current().run_sync(client) + def ws_send_raw(port, string): @gen.coroutine def client(): @@ -20,17 +27,44 @@ def client(): message = yield client.read_message() client.write_message(json.dumps(string)) message = yield client.read_message() - if message is not None: - print 'yes', message - raise gen.Return(json.loads(message)) - else: - print 'no' + raise gen.Return(json.loads(message)) return IOLoop.current().run_sync(client) -def ws_send(port, command, params={}): +def ws_send(node_index, command, params={}): + port = str(node_to_ws_port(node_index)) cmd = {'command': command, - 'id': 1, - 'params': params} - return ws_send_raw(port, cmd) + 'id': 1, + 'params': params} + ret = ws_send_raw(port, cmd) + time.sleep(0.1) + return ret + + +def create_nodes(context, num_nodes): + proc = [] + for i in range(num_nodes): + proc.append(Process(target=start_node, + args=('ppl/default', + '127.0.0.%s' % str(i+1), + 12345, + None, + 'test%s.log' % str(i)))) + proc[i].start() + time.sleep(0.1) + context.proc = proc + + +def create_connected_nodes(context, num_nodes): + create_nodes(context, num_nodes) + for i in range(num_nodes-1): + ws_send(i, 'connect', {'uri': node_addr(i+1)}) + + +def node_addr(node_index): + return 'tcp://127.0.0.%s:12345' % str(node_index+1) + + +def node_to_ws_port(node_index): + return node_index + 8888