Esempio n. 1
0
    def test_mismatched_request_causes_failure(self):
        """
        If a request is made that is not expected as the next request,
        causes a failure.
        """
        sequence = RequestSequence(
            [(('get', 'https://anything/', {'1': ['2']},
               HasHeaders({'1': ['1']}), 'what'),
              (418, {}, 'body')),
             (('get', 'http://anything', {}, HasHeaders({'2': ['1']}), 'what'),
              (202, {}, 'deleted'))],
            async_failure_reporter=self.async_failures.append)

        stub = StubTreq(StringStubbingResource(sequence))
        get = partial(stub.get, 'https://anything?1=2', data='what',
                      headers={'1': '1'})

        resp = self.successResultOf(get())
        self.assertEqual(418, resp.code)
        self.assertEqual('body', self.successResultOf(stub.content(resp)))
        self.assertEqual([], self.async_failures)

        resp = self.successResultOf(get())
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("Expected the next request to be",
                      self.async_failures[0])

        self.assertFalse(sequence.consumed())
Esempio n. 2
0
    def test_consume_context_manager_fails_on_remaining_requests(self):
        """
        If the `consume` context manager is used, if there are any remaining
        expecting requests, the test case will be failed.
        """
        sequence = RequestSequence(
            [((ANY, ANY, ANY, ANY, ANY), (418, {}, 'body'))] * 2,
            async_failure_reporter=self.async_failures.append)
        stub = StubTreq(StringStubbingResource(sequence))

        consume_failures = []
        with sequence.consume(sync_failure_reporter=consume_failures.append):
            self.successResultOf(stub.get('https://anything', data='what',
                                          headers={'1': '1'}))

        self.assertEqual(1, len(consume_failures))
        self.assertIn(
            "Not all expected requests were made.  Still expecting:",
            consume_failures[0])
        self.assertIn(
            "{0}(url={0}, params={0}, headers={0}, data={0})".format(
                repr(ANY)),
            consume_failures[0])

        # no asynchronous failures (mismatches, etc.)
        self.assertEqual([], self.async_failures)
Esempio n. 3
0
 def test_successful_post_request(self):
     pytest.importorskip("treq")
     from treq.testing import (StubTreq,
                               StringStubbingResource,
                               RequestSequence)
     credentials = {
         "partner_code": "123456",
         "username": "******",
         "password": "******"
     }
     errors = []
     from mock import ANY
     sequence_stubs = RequestSequence(
         [((ANY, ANY, ANY, ANY, ANY),
           (200, {}, etree.tostring(
               test_utils.create_node_from_file('get_order_by_poid.xml')
           )))],
         errors.append
     )
     stub_treq = StubTreq(StringStubbingResource(sequence_stubs))
     with sequence_stubs.consume(errors.append):
         d = post_request_treq(stub_treq,
                               "https://symantec.endpoint.example.com",
                               GetModifiedOrderRequest(),
                               credentials)
         responses = []
         d.addCallback(responses.append)
         d.addErrback(errors.append)
         assert len(responses) == 1
     assert errors == []
Esempio n. 4
0
    def test_get_channels_health_check(self):

        url = b'http://rabbitmq:15672/api/queues/%2F/queue-1234-1234.inbound'

        async_failures = []
        sequence_stubs = RequestSequence(
            [((b'get', url, mock.ANY, mock.ANY, mock.ANY),
             (http.OK, {b'Content-Type': b'application/json'},
              b'{"messages": 1256, "messages_details": {"rate": 1.25}, "name": "queue-1234-1234.inbound"}'))],  # noqa
            async_failures.append)
        stub_treq = StubTreq(StringStubbingResource(sequence_stubs))

        def new_get(*args, **kwargs):
            return stub_treq.request("GET", args[0])

        rabbitmq_management_client = RabbitmqManagementClient(
            "rabbitmq:15672", "guest", "guest")

        with (mock.patch('treq.client.HTTPClient.get', side_effect=new_get)):
            with sequence_stubs.consume(self.fail):
                response = yield rabbitmq_management_client.get_queue(
                    "/", "queue-1234-1234.inbound")

                yield self.assertEqual(response, {
                    "messages": 1256,
                    "messages_details": {"rate": 1.25},
                    "name": "queue-1234-1234.inbound"})
Esempio n. 5
0
    def test_mismatched_request_causes_failure(self):
        """
        If a request is made that is not expected as the next request,
        causes a failure.
        """
        sequence = RequestSequence(
            [
                (
                    (b"get", "https://anything/", {b"1": [b"2"]}, HasHeaders({b"1": [b"1"]}), b"what"),
                    (418, {}, b"body"),
                ),
                ((b"get", "http://anything", {}, HasHeaders({b"2": [b"1"]}), b"what"), (202, {}, b"deleted")),
            ],
            async_failure_reporter=self.async_failures.append,
        )

        stub = StubTreq(StringStubbingResource(sequence))
        get = partial(stub.get, "https://anything?1=2", data=b"what", headers={b"1": b"1"})

        resp = self.successResultOf(get())

        self.assertEqual(418, resp.code)
        self.assertEqual(b"body", self.successResultOf(stub.content(resp)))
        self.assertEqual([], self.async_failures)

        resp = self.successResultOf(get())
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("Expected the next request to be", self.async_failures[0])

        self.assertFalse(sequence.consumed())
Esempio n. 6
0
    def test_get_channels_health_check(self):

        url = b'http://rabbitmq:15672/api/queues/%2F/queue-1234-1234.inbound'

        async_failures = []
        sequence_stubs = RequestSequence(
            [((b'get', url, mock.ANY, mock.ANY, mock.ANY), (http.OK, {
                b'Content-Type':
                b'application/json'
            }, b'{"messages": 1256, "messages_details": {"rate": 1.25}, "name": "queue-1234-1234.inbound"}'
                                                            ))],  # noqa
            async_failures.append)
        stub_treq = StubTreq(StringStubbingResource(sequence_stubs))

        def new_get(*args, **kwargs):
            return stub_treq.request("GET", args[0])

        rabbitmq_management_client = RabbitmqManagementClient(
            "rabbitmq:15672", "guest", "guest")

        with (mock.patch('treq.client.HTTPClient.get', side_effect=new_get)):
            with sequence_stubs.consume(self.fail):
                response = yield rabbitmq_management_client.get_queue(
                    "/", "queue-1234-1234.inbound")

                yield self.assertEqual(
                    response, {
                        "messages": 1256,
                        "messages_details": {
                            "rate": 1.25
                        },
                        "name": "queue-1234-1234.inbound"
                    })
Esempio n. 7
0
 def test_successful_post_request(self):
     pytest.importorskip("treq")
     from treq.testing import (StubTreq, StringStubbingResource,
                               RequestSequence)
     credentials = {
         "partner_code": "123456",
         "username": "******",
         "password": "******"
     }
     errors = []
     from mock import ANY
     sequence_stubs = RequestSequence([
         ((ANY, ANY, ANY, ANY, ANY),
          (200, {},
           etree.tostring(
               test_utils.create_node_from_file('get_order_by_poid.xml'))))
     ], errors.append)
     stub_treq = StubTreq(StringStubbingResource(sequence_stubs))
     with sequence_stubs.consume(errors.append):
         d = post_request_treq(stub_treq,
                               "https://symantec.endpoint.example.com",
                               GetModifiedOrderRequest(), credentials)
         responses = []
         d.addCallback(responses.append)
         d.addErrback(errors.append)
         assert len(responses) == 1
     assert errors == []
Esempio n. 8
0
 def setup_treq(self, code=200, body={}):
     self.async_failures = []
     self.stubs = RequestSequence(
         [((b"get", "http://server:8989/index", {},
            HasHeaders({"Bloc-Session-ID": ["sid"]}), b''),
           (code, {}, json.dumps(body).encode("utf-8")))],
         self.async_failures.append)
     self.client.treq = StubTreq(StringStubbingResource(self.stubs))
Esempio n. 9
0
 def test_set_clb_attributes(self):
     """
     :func:`set_clb_attributes` calls
     ``PATCH .../loadbalancer/lb_id/attributes`` with given key-value pairs
     """
     stubs = RequestSequence(
         [(("patch", "http://host/loadbalancer/3/attributes", {}, mock.ANY,
            '{"a": "b"}'), (204, {}, ""))], self.fail)
     self.clb.treq = StubTreq(StringStubbingResource(stubs))
     with stubs.consume(self.fail):
         self.clb.set_clb_attributes(self.rcs, 3, {"a": "b"})
Esempio n. 10
0
    def test_bad_file(self):
        """
        Adding a file outside the magic-folder fails
        """
        stdout = StringIO()
        stderr = StringIO()

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                (b"post",
                 self.url.child("magic-folder", "default", "snapshot").to_text(),
                 {b"path": [b"../../../foo"]},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'0'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + self.global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 b""),
                (406, {}, b'{"reason": "a really good one"}')
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            self.global_config,
            http_client,
        )

        with self.assertRaises(SystemExit):
            with request_sequence.consume(self.fail):
                yield dispatch_magic_folder_api_command(
                    ["--config", self.magic_config.path, "add-snapshot",
                     "--file", "../../../foo",
                     "--folder", "default"],
                    stdout=stdout,
                    stderr=stderr,
                    client=client,
                )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("")
        )
        self.assertThat(
            stderr.getvalue().strip(),
            Equals('{"reason": "a really good one"}')
        )
Esempio n. 11
0
    def responses(self, responses):
        failures = []
        responses = RequestSequence(responses, failures.append)
        stub = StubTreq(StringStubbingResource(responses))
        self.patch(httpclientservice, 'treq', stub)

        try:
            with responses.consume(self.fail):
                yield
        finally:
            assert failures == []
Esempio n. 12
0
    def test_list_participants(self):
        """
        List all participants in a magic-folder
        """
        stdout = StringIO()
        stderr = StringIO()

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                # expected request
                (b"get",
                 self.url.child("magic-folder", "default", "participants").to_text(),
                 {},
                 {
                     b'Host': [b'invalid.'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + self.global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 b"",
                ),
                # expected response
                (200, {}, b"{}"),
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            self.global_config,
            http_client,
        )
        with request_sequence.consume(self.fail):
            yield dispatch_magic_folder_api_command(
                ["--config", self.magic_config.path, "list-participants",
                 "--folder", "default",
                ],
                stdout=stdout,
                stderr=stderr,
                client=client,
            )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("{}")
        )
        self.assertThat(
            stderr.getvalue().strip(),
            Equals("")
        )
Esempio n. 13
0
 def test_update_clb_node_status(self):
     """
     :func:`update_clb_node_status` calls
     ``PUT .../loadbalancers/lb_id/nodes/node_id/status`` with given status
     in body
     """
     stubs = RequestSequence(
         [(("put", "http://host/loadbalancers/3/nodes/2/status", {},
            mock.ANY, '{"status": "ONLINE"}'), (200, {}, ""))], self.fail)
     self.clb.treq = StubTreq(StringStubbingResource(stubs))
     with stubs.consume(self.fail):
         self.clb.update_clb_node_status(self.rcs, 3, 2, "ONLINE")
Esempio n. 14
0
    def test_happy(self):
        """
        A file is successfully added.
        """
        stdout = StringIO()
        stderr = StringIO()

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                (b"post",
                 self.url.child("magic-folder", "default", "snapshot").to_text().encode("utf8"),
                 {b"path": [b"foo"]},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'0'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + self.global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 b""),
                (200, {}, b"{}")
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            self.global_config,
            http_client,
        )
        with request_sequence.consume(self.fail):
            yield dispatch_magic_folder_api_command(
                ["--config", self.magic_config.path, "add-snapshot",
                 "--file", "foo",
                 "--folder", "default"],
                stdout=stdout,
                stderr=stderr,
                client=client,
            )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("{}")
        )
        self.assertThat(
            stderr.getvalue().strip(),
            Equals("")
        )
Esempio n. 15
0
    def test_async_failures_logged(self):
        """
        When no `async_failure_reporter` is passed async failures are logged by
        default.
        """
        sequence = RequestSequence([])
        stub = StubTreq(StringStubbingResource(sequence))

        with sequence.consume(self.fail):
            self.successResultOf(stub.get('https://example.com'))

        [failure] = self.flushLoggedErrors()
        self.assertIsInstance(failure.value, AssertionError)
Esempio n. 16
0
    def test_200_ok(self):
        """On a 200 response, return the response's JSON."""
        req_seq = RequestSequence([
            ((b'get', 'http://an.example/foo', {b'a': [b'b']},
              HasHeaders({'Accept': ['application/json']}), b''),
             (http.OK, {b'Content-Type': b'application/json'}, b'{"status": "ok"}'))
        ], log.error)
        treq = StubTreq(StringStubbingResource(req_seq))

        with req_seq.consume(self.fail):
            result = self.successResultOf(make_a_request(treq))

        self.assertEqual({"status": "ok"}, result)
Esempio n. 17
0
 def test_set_clb_attributes(self):
     """
     :func:`set_clb_attributes` calls
     ``PATCH .../loadbalancer/lb_id/attributes`` with given key-value pairs
     """
     stubs = RequestSequence(
         [(("patch", "http://host/loadbalancer/3/attributes", {}, mock.ANY,
            '{"a": "b"}'),
           (204, {}, ""))],
         self.fail)
     self.clb.treq = StubTreq(StringStubbingResource(stubs))
     with stubs.consume(self.fail):
         self.clb.set_clb_attributes(self.rcs, 3, {"a": "b"})
Esempio n. 18
0
    def test_418_teapot(self):
        """On an unexpected response code, raise an exception"""
        req_seq = RequestSequence([
            ((b'get', 'http://an.example/foo', {b'a': [b'b']},
              HasHeaders({'Accept': ['application/json']}), b''),
             (418, {b'Content-Type': b'text/plain'}, b"I'm a teapot!"))
        ], log.error)
        treq = StubTreq(StringStubbingResource(req_seq))

        with req_seq.consume(self.fail):
            failure = self.failureResultOf(make_a_request(treq))

        self.assertEqual(u"Got an error from the server: I'm a teapot!",
                         failure.getErrorMessage())
Esempio n. 19
0
 def test_update_clb_node_status(self):
     """
     :func:`update_clb_node_status` calls
     ``PUT .../loadbalancers/lb_id/nodes/node_id/status`` with given status
     in body
     """
     stubs = RequestSequence(
         [(("put", "http://host/loadbalancers/3/nodes/2/status", {},
            mock.ANY, '{"status": "ONLINE"}'),
           (200, {}, ""))],
         self.fail)
     self.clb.treq = StubTreq(StringStubbingResource(stubs))
     with stubs.consume(self.fail):
         self.clb.update_clb_node_status(self.rcs, 3, 2, "ONLINE")
Esempio n. 20
0
    def test_unexpected_number_of_request_causes_failure(self):
        """
        If there are no more expected requests, making a request causes a
        failure.
        """
        sequence = RequestSequence([], async_failure_reporter=self.async_failures.append)
        stub = StubTreq(StringStubbingResource(sequence))
        d = stub.get("https://anything", data=b"what", headers={b"1": b"1"})
        resp = self.successResultOf(d)
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("No more requests expected, but request", self.async_failures[0])

        # the expected requests have all been made
        self.assertTrue(sequence.consumed())
Esempio n. 21
0
    def responses(self, responses):
        # otherwise it bails pytest because of a DeprecationWarning
        from treq.testing import StubTreq
        from treq.testing import RequestSequence, StringStubbingResource

        failures = []
        responses = RequestSequence(responses, failures.append)
        stub = StubTreq(StringStubbingResource(responses))
        self.patch(httpclientservice, 'treq', stub)

        try:
            with responses.consume(self.fail):
                yield
        finally:
            assert failures == []
Esempio n. 22
0
 def test_stopservice_deletes_session(self):
     """
     :func:`stopService` will delete the session and will stop the loop
     """
     self.test_settled()
     stubs = RequestSequence(
         [((b"delete", "http://server:8989/session", {},
            HasHeaders({"Bloc-Session-ID": ["sid"]}), b''),
           (200, {}, b''))],
         self.fail)
     self.client.treq = StubTreq(StringStubbingResource(stubs))
     with stubs.consume(self.fail):
         d = self.client.stopService()
         self.assertIsNone(self.successResultOf(d))
         # Moving time would fail treq if it tried to heartbeat
         self.clock.advance(4)
Esempio n. 23
0
    def test_unexpected_number_of_request_causes_failure(self):
        """
        If there are no more expected requests, making a request causes a
        failure.
        """
        sequence = RequestSequence(
            [], async_failure_reporter=self.async_failures.append)
        stub = StubTreq(StringStubbingResource(sequence))
        d = stub.get('https://anything', data=b'what', headers={b'1': b'1'})
        resp = self.successResultOf(d)
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("No more requests expected, but request",
                      self.async_failures[0])

        # the expected requests have all been made
        self.assertTrue(sequence.consumed())
Esempio n. 24
0
    def test_works_with_mock_any(self):
        """
        :obj:`mock.ANY` can be used with the request parameters.
        """
        sequence = RequestSequence(
            [((ANY, ANY, ANY, ANY, ANY), (418, {}, b"body"))], async_failure_reporter=self.async_failures.append
        )
        stub = StubTreq(StringStubbingResource(sequence))

        with sequence.consume(sync_failure_reporter=self.fail):
            d = stub.get("https://anything", data=b"what", headers={b"1": b"1"})
            resp = self.successResultOf(d)
            self.assertEqual(418, resp.code)
            self.assertEqual(b"body", self.successResultOf(stub.content(resp)))

        self.assertEqual([], self.async_failures)

        # the expected requests have all been made
        self.assertTrue(sequence.consumed())
Esempio n. 25
0
    def test_works_with_mock_any(self):
        """
        :obj:`mock.ANY` can be used with the request parameters.
        """
        sequence = RequestSequence(
            [((ANY, ANY, ANY, ANY, ANY), (418, {}, 'body'))],
            async_failure_reporter=self.async_failures.append)
        stub = StubTreq(StringStubbingResource(sequence))

        with sequence.consume(sync_failure_reporter=self.fail):
            d = stub.get('https://anything', data='what', headers={'1': '1'})
            resp = self.successResultOf(d)
            self.assertEqual(418, resp.code)
            self.assertEqual('body', self.successResultOf(stub.content(resp)))

        self.assertEqual([], self.async_failures)

        # the expected requests have all been made
        self.assertTrue(sequence.consumed())
Esempio n. 26
0
 def _requestSequenceGenerator(self, response, statusCode=http.OK):
     return RequestSequence(
         [((b'get', 'https://k8s.wmnet.test:1234/v1/nodes', {
             b'pretty': [b'false']
         },
            HasHeaders({
                'Accept': ['application/json'],
                'Authorization': ['Bearer token'],
            }), b''), (statusCode, {
                b'Content-Type': b'application/json'
            }, response))], log.error)
Esempio n. 27
0
    def test_api_error(self):
        """
        An error is reported if the API reports an error
        """
        stdout = StringIO()
        stderr = StringIO()

        basedir = FilePath(self.mktemp())
        global_config = create_global_configuration(
            basedir,
            "tcp:-1",
            FilePath(u"/no/tahoe/node-directory"),
            "tcp:127.0.0.1:-1",
        )

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                (b"post",
                 self.url.child("magic-folder", "default", "snapshot").to_text(),
                 {b"path": [b"foo"]},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'0'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 b""),
                (406, {}, b'{"reason": "an explanation"}')
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            global_config,
            http_client,
        )

        with self.assertRaises(SystemExit):
            with request_sequence.consume(self.fail):
                yield dispatch_magic_folder_api_command(
                    ["--config", basedir.path, "add-snapshot",
                     "--file", "foo",
                     "--folder", "default"],
                    stdout=stdout,
                    stderr=stderr,
                    client=client,
                )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("")
        )
        self.assertThat(
            json.loads(stderr.getvalue()),
            Equals({"reason": "an explanation"})
        )
Esempio n. 28
0
    def XXXtest_add_participant(self):
        """
        A new participant is added to a magic-folder
        """
        stdout = StringIO()
        stderr = StringIO()

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                # expected request
                (b"post",
                 self.url.child("magic-folder", "default", "participants").to_text(),
                 {},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'149'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + self.global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 # XXX args, this fails because of different sorting of keys in body serialization
                 json.dumps({
                     "personal_dmd": "URI:DIR2-CHK:lq34kr5sp7mnvkhce4ahl2nw4m:dpujdl7sol6xih5gzil525tormolzaucq4re7snn5belv7wzsdha:1:5:328",
                     "author": {
                         "name": "amaya",
                     }
                 }).encode("utf8"),
                ),
                # expected response
                (200, {}, b"{}"),
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            self.global_config,
            http_client,
        )
        with request_sequence.consume(self.fail):
            yield dispatch_magic_folder_api_command(
                ["--config", self.magic_config.path, "add-participant",
                 "--folder", "default",
                 "--author-name", "amaya",
                 "--personal-dmd", 'URI:DIR2-CHK:lq34kr5sp7mnvkhce4ahl2nw4m:dpujdl7sol6xih5gzil525tormolzaucq4re7snn5belv7wzsdha:1:5:328',
                ],
                stdout=stdout,
                stderr=stderr,
                client=client,
            )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("{}")
        )
        self.assertThat(
            stderr.getvalue().strip(),
            Equals("")
        )
Esempio n. 29
0
class BlocClientTests(SynchronousTestCase):
    """
    Tests for :obj:`client.BlocClient`
    """

    def setUp(self):
        self.clock = Clock()
        self.client = BlocClient(self.clock, 'server:8989', 3, session_id='sid')

    def setup_treq(self, code=200, body={}):
        self.async_failures = []
        self.stubs = RequestSequence(
            [((b"get", "http://server:8989/index", {},
               HasHeaders({"Bloc-Session-ID": ["sid"]}), b''),
              (code, {}, json.dumps(body).encode("utf-8")))],
            self.async_failures.append)
        self.client.treq = StubTreq(StringStubbingResource(self.stubs))

    def test_settled(self):
        """
        When getting index returns SETTLED then it is set and is returned in `get_index_total`
        """
        self.setup_treq(body={"status": "SETTLED", "index": 1, "total": 1})
        self.client.startService()
        with self.stubs.consume(self.fail):
            self.assertEqual(self.client.get_index_total(), (1, 1))
            self.assertTrue(self.client._settled)
        self.assertEqual(self.async_failures, [])

    def test_settling(self):
        """
        When getting index returns SETTLING, then get_index_total returns None
        """
        self.setup_treq(body={"status": "SETTLING"})
        self.client.startService()
        with self.stubs.consume(self.fail):
            self.assertIsNone(self.client.get_index_total())
        self.assertEqual(self.async_failures, [])

    def test_get_errors(self):
        """
        If get index errors, then get_index_total will return None
        """
        self.setup_treq(code=500)
        self.client.startService()
        with self.stubs.consume(self.fail):
            self.assertIsNone(self.client.get_index_total())
        self.assertEqual(self.async_failures, [])

    def test_get_times_out(self):
        """
        If get index times out then get_index_total will return None
        """
        # lets start with settled
        self.test_settled()
        # setup client that does not return
        self.client.treq = StubTreq(DeferredResource(Deferred()))
        # next heartbeat to get index again
        self.clock.advance(3)
        # no response
        self.clock.advance(5)
        self.assertIsNone(self.client.get_index_total())

    def test_sequence(self):
        """
        Test sequence of changes from server:
        TODO: this should probably be done via hypothesis
        SETTLING -> SETTLED -> ERRORS -> SETTLING -> SETTLED
        """
        # settling
        self.setup_treq(body={"status": "SETTLING"})
        self.client.startService()
        with self.stubs.consume(self.fail):
            self.assertIsNone(self.client.get_index_total())

        # settled
        self.setup_treq(body={"status": "SETTLED", "index": 1, "total": 3})
        self.clock.advance(3)
        with self.stubs.consume(self.fail):
            self.assertEqual(self.client.get_index_total(), (1, 3))

        # errors
        self.setup_treq(code=500)
        self.clock.advance(3)
        with self.stubs.consume(self.fail):
            self.assertIsNone(self.client.get_index_total())

        # settling
        self.setup_treq(body={"status": "SETTLING"})
        self.clock.advance(3)
        with self.stubs.consume(self.fail):
            self.assertIsNone(self.client.get_index_total())

        # settled
        self.setup_treq(body={"status": "SETTLED", "index": 3, "total": 4})
        self.clock.advance(3)
        with self.stubs.consume(self.fail):
            self.assertEqual(self.client.get_index_total(), (3, 4))
        self.assertEqual(self.async_failures, [])

    def test_stopservice_deletes_session(self):
        """
        :func:`stopService` will delete the session and will stop the loop
        """
        self.test_settled()
        stubs = RequestSequence(
            [((b"delete", "http://server:8989/session", {},
               HasHeaders({"Bloc-Session-ID": ["sid"]}), b''),
              (200, {}, b''))],
            self.fail)
        self.client.treq = StubTreq(StringStubbingResource(stubs))
        with stubs.consume(self.fail):
            d = self.client.stopService()
            self.assertIsNone(self.successResultOf(d))
            # Moving time would fail treq if it tried to heartbeat
            self.clock.advance(4)

    def test_stopservice_ignores_delete_session(self):
        """
        :func:`stopService` will try deleting the session for 1 second and will stop the loop
        """
        self.test_settled()
        self.client.treq = StubTreq(DeferredResource(Deferred()))
        d = self.client.stopService()
        self.assertNoResult(d)
        self.clock.advance(1)
        self.assertIsNone(self.successResultOf(d))
        # Moving time would fail treq if it tried to heartbeat
        self.clock.advance(4)