def test_get_apps(self):
        """
        When the list of apps is requested, a list of apps added via add_app()
        should be returned.
        """
        app = {
            'id':
            '/my-app_1',
            'cmd':
            'sleep 50',
            'tasks': [{
                "host": "host1.local",
                "id": "my-app_1-1396592790353",
                "ports": []
            }, {
                "host": "host2.local",
                "id": "my-app_1-1396592784349",
                "ports": []
            }]
        }
        self.marathon.add_app(app)

        response = self.client.get('http://localhost/v2/apps')
        assert_that(
            response,
            succeeded(
                MatchesAll(
                    IsJsonResponseWithCode(200),
                    After(json_content, succeeded(Equals({'apps': [app]}))))))
Exemple #2
0
def WithErrorTypeAndMessage(error_type, message):
    """
    Check that a Twisted failure was caused by a certain error type with a
    certain message.
    """
    return MatchesAll(MatchesStructure(value=IsInstance(error_type)),
                      After(methodcaller('getErrorMessage'), Equals(message)))
Exemple #3
0
 def test_async_error_propagates(self):
     """
     If an unhandled asynchronous error occurs in an interceptor it
     propogates along the execution.
     """
     interceptors = [tracer('a'), tracer('b'), thrower('c'), tracer('d')]
     self.assertThat(execute(empty_context, interceptors),
                     failed(After(lambda f: f.type, Is(TracingError))))
Exemple #4
0
 def test_tuple_list(self):
     """
     The indexed items of lists are their children.
     """
     node = (u'key', [u'a', u'b', u'c'])
     self.assertThat(
         # The ignores intentionally do nothing.
         get_children({2, u'c'}, node),
         After(list, Equals([(0, u'a'), (1, u'b'), (2, u'c')])))
Exemple #5
0
 def test_body(self):
     """
     ``body`` is a file-like containing the request content.
     """
     request = fakeNevowRequest(body=b'hello')
     self.assertThat(
         _nevow_request_to_request_map(request),
         ContainsDict({
             'body': After(lambda x: x.read(), Equals(b'hello')),
             'content_length': Equals(5)
         }))
 def test_get_apps_empty(self):
     """
     When the list of apps is requested and there are no apps, an empty list
     of apps should be returned.
     """
     response = self.client.get('http://localhost/v2/apps')
     assert_that(
         response,
         succeeded(
             MatchesAll(
                 IsJsonResponseWithCode(200),
                 After(json_content, succeeded(Equals({'apps': []}))))))
    def test_get_events(self):
        """
        When a request is made to the event stream endpoint, an SSE stream
        should be received in response and an event should be fired that
        indicates that the stream was attached to.
        """
        response = self.client.get('http://localhost/v2/events',
                                   headers={'Accept': 'text/event-stream'})

        assert_that(
            response,
            succeeded(
                MatchesAll(
                    IsSseResponse(),
                    After(
                        partial(collect_events, 'event_stream_attached'),
                        MatchesListwise([
                            After(
                                json.loads,
                                IsMarathonEvent(
                                    'event_stream_attached',
                                    remoteAddress=Equals('127.0.0.1')))
                        ])))))
    def test_add_app_triggers_api_post_event(self):
        """
        When an app is added to the underlying fake Marathon, an
        ``api_post_event`` should be received by any event listeners.
        """
        response = self.client.get('http://localhost/v2/events',
                                   headers={'Accept': 'text/event-stream'})
        assert_that(response, succeeded(IsSseResponse()))

        app = {
            'id': '/my-app_1',
            'labels': {
                'HAPROXY_GROUP': 'external',
                'MARATHON_ACME_0_DOMAIN': 'example.com'
            },
            'portDefinitions': [{
                'port': 9000,
                'protocol': 'tcp',
                'labels': {}
            }]
        }
        self.marathon.add_app(app)

        assert_that(
            response,
            succeeded(
                After(
                    partial(collect_events, 'api_post_event'),
                    MatchesListwise([
                        After(
                            json.loads,
                            IsMarathonEvent('api_post_event',
                                            clientIp=Is(None),
                                            uri=Equals('/v2/apps/my-app_1'),
                                            appDefinition=Equals(app)))
                    ]))))
Exemple #9
0
 def test_namespaced(self):
     """
     `namespaced` creates a function that when called produces a namespaced
     name.
     """
     self.assertThat(
         namespaced(u'foo'),
         MatchesAll(
             MatchesPredicate(callable, '%s is not callable'),
             After(
                 lambda f: f(u'bar'),
                 MatchesAll(
                     MatchesListwise([Equals(u'foo'),
                                      Equals(u'bar')]),
                     MatchesStructure(prefix=Equals(u'foo'),
                                      name=Equals(u'bar'))))))
Exemple #10
0
def IsMarathonEvent(event_type, **kwargs):
    """
    Match a dict (deserialized from JSON) as a Marathon event. Matches the
    event type and checks for a recent timestamp.

    :param event_type: The event type ('eventType' field value)
    :param kwargs: Any other matchers to apply to the dict
    """
    matching_dict = {
        'eventType':
        Equals(event_type),
        'timestamp':
        After(_parse_marathon_event_timestamp,
              matches_time_or_just_before(datetime.utcnow()))
    }
    matching_dict.update(kwargs)
    return MatchesDict(matching_dict)
Exemple #11
0
    def test_health_unhealthy(self):
        """
        When a GET request is made to the health endpoint, and the health
        handler reports that the service is unhealthy, a 503 status code should
        be returned together with the JSON message from the handler.
        """
        self.server.set_health_handler(
            lambda: Health(False, {'error': "I'm sad :("}))

        response = self.client.get('http://localhost/health')
        assert_that(
            response,
            succeeded(
                MatchesAll(
                    IsJsonResponseWithCode(503),
                    After(json_content,
                          succeeded(Equals({'error': "I'm sad :("}))))))
Exemple #12
0
def Multipart(content_length,
              name,
              headers,
              body,
              character_encoding=Equals('latin1'),
              filename=Is(None),
              content_type=Is(None)):
    """
    """
    return MatchesDict({
        u'content_type': content_type,
        u'content_length': content_length,
        u'name': name,
        u'character_encoding': character_encoding,
        u'filename': filename,
        u'headers': headers,
        u'body': After(lambda x: x.read(), body),
    })
Exemple #13
0
 def test_health_handler_unset(self):
     """
     When a GET request is made to the health endpoint, and the health
     handler hasn't been set, a 501 status code should be returned together
     with a JSON message that explains that the handler is not set.
     """
     response = self.client.get('http://localhost/health')
     assert_that(
         response,
         succeeded(
             MatchesAll(
                 IsJsonResponseWithCode(501),
                 After(
                     json_content,
                     succeeded(
                         Equals({
                             'error':
                             'Cannot determine service health: no handler set'
                         }))))))
Exemple #14
0
def HasRequestProperties(method=None, url=None, query={}):
    """
    Check if a HTTP request object has certain properties.

    Parses the query dict from the request URI rather than using the request
    "args" property as the args do not include query parameters that have no
    value.

    :param str method:
        The HTTP method.
    :param str url:
        The HTTP URL, without any query parameters. Should already be percent
        encoded.
    :param dict query:
        A dictionary of HTTP query parameters.
    """
    return MatchesStructure(method=Equals(method.encode('ascii')),
                            path=Equals(url.encode('ascii')),
                            uri=After(lambda u: urisplit(u).getquerydict(),
                                      Equals(query)))
    def test_get_apps_check_called(self):
        """
        When a client makes a call to the GET /v2/apps API, a flag should be
        set to indicate that the API has been called. Checking the flag should
        reset it.
        """
        # The flag should start out False
        assert_that(self.marathon_api.check_called_get_apps(), Equals(False))

        # Make a call to get_apps()
        response = self.client.get('http://localhost/v2/apps')
        assert_that(
            response,
            succeeded(
                MatchesAll(
                    IsJsonResponseWithCode(200),
                    After(json_content, succeeded(Equals({'apps': []}))))))

        # After the call the flag should be True
        assert_that(self.marathon_api.check_called_get_apps(), Equals(True))

        # Checking the flag should reset it to False
        assert_that(self.marathon_api.check_called_get_apps(), Equals(False))
    def test_signal_usr1(self):
        """
        When a client calls the ``/mlb_signal/usr1`` endpoint, the correct
        response should be returned and the ``signalled_usr1`` flag set True.
        """
        assert_that(self.marathon_lb.check_signalled_usr1(), Equals(False))

        response = self.client.get('http://localhost/_mlb_signal/usr1')
        assert_that(
            response,
            succeeded(
                MatchesAll(
                    MatchesStructure(code=Equals(200),
                                     headers=HasHeader('content-type',
                                                       ['text/plain'])),
                    After(
                        methodcaller('text'),
                        succeeded(
                            Equals('Sent SIGUSR1 signal to marathon-lb'))))))

        assert_that(self.marathon_lb.check_signalled_usr1(), Equals(True))

        # Signalled flag should be reset to false after it is checked
        assert_that(self.marathon_lb.check_signalled_usr1(), Equals(False))
Exemple #17
0
    def test_responder_resource_child(self):
        """
        When a GET request is made to the ACME challenge path, and the
        responder resource has a child resource at the correct path, the value
        of the resource should be returned.
        """
        self.responder_resource.putChild(b'foo', Data(b'bar', 'text/plain'))

        response = self.client.get(
            'http://localhost/.well-known/acme-challenge/foo')
        assert_that(
            response,
            succeeded(
                MatchesAll(
                    MatchesStructure(code=Equals(200),
                                     headers=HasHeader('Content-Type',
                                                       ['text/plain'])),
                    After(methodcaller('content'),
                          succeeded(Equals(b'bar'))))))

        # Sanity check that a request to a different subpath does not succeed
        response = self.client.get(
            'http://localhost/.well-known/acme-challenge/baz')
        assert_that(response, succeeded(MatchesStructure(code=Equals(404))))
Exemple #18
0
def EnterStage(matcher, context=empty_context):
    """
    """
    return MatchesAll(
        IsInstance(Interceptor),
        After(lambda interceptor: interceptor.enter(context), matcher))
    def test_get_events_lost_connection(self):
        """
        When two connections are made to the event stream, the first connection
        should receive events for both connections attaching to the stream.
        Then, when the first connection is disconnected, the second should
        receive a detach event for the first.
        """
        response1_d = self.client.get('http://localhost/v2/events',
                                      headers={'Accept': 'text/event-stream'})
        # First listener attaches and receives event it attached
        assert_that(response1_d, succeeded(IsSseResponse()))

        response1 = response1_d.result
        attach_data1 = []
        detach_data1 = []
        handler1 = dict_handler({
            'event_stream_attached': attach_data1.append,
            'event_stream_detached': detach_data1.append
        })
        finished, protocol = _sse_content_with_protocol(response1, handler1)

        response2_d = self.client.get('http://localhost/v2/events',
                                      headers={'Accept': 'text/event-stream'})
        assert_that(response2_d, succeeded(IsSseResponse()))

        response2 = response2_d.result
        attach_data2 = []
        detach_data2 = []
        handler2 = dict_handler({
            'event_stream_attached': attach_data2.append,
            'event_stream_detached': detach_data2.append
        })
        sse_content(response2, handler2)

        # Close request 1's connection
        # FIXME: Currently the only way to get the underlying transport so that
        # we can simulate a lost connection is to get the transport that the
        # SseProtocol receives. This transport is actually a
        # TransportProxyProducer (because that's what HTTP11ClientProtocol
        # gives our protocol). Get the actual wrapped transport from the
        # _producer attribute.
        protocol.transport._producer.loseConnection()
        # Flush the client so that the disconnection propagates
        self.client.flush()
        assert_that(finished, succeeded(Is(None)))

        # Assert request 1's response data
        assert_that(
            attach_data1,
            MatchesListwise([
                # First attach event on request 1 from itself connecting
                After(
                    json.loads,
                    IsMarathonEvent('event_stream_attached',
                                    remoteAddress=Equals('127.0.0.1'))),
                # Second attach event on request 1 from request 2 connecting
                After(
                    json.loads,
                    IsMarathonEvent('event_stream_attached',
                                    remoteAddress=Equals('127.0.0.1')))
            ]))

        # Request 1 shouldn't receive any detach events
        assert_that(detach_data1, Equals([]))

        # Now look at request 2's events
        # Attach event only for itself
        assert_that(
            attach_data2,
            MatchesListwise([
                After(
                    json.loads,
                    IsMarathonEvent('event_stream_attached',
                                    remoteAddress=Equals('127.0.0.1')))
            ]))

        # Detach event for request 1
        assert_that(
            detach_data2,
            MatchesListwise([
                After(
                    json.loads,
                    IsMarathonEvent('event_stream_detached',
                                    remoteAddress=Equals('127.0.0.1')))
            ]))
Exemple #20
0
 def test_default(self):
     """
     Default content parsers.
     """
     interceptors = [body_params()]
     request = m(
         content_type=
         'multipart/form-data; boundary=---------------------------114772229410704779042051621609',
         body=open_test_data('data/multipart_request'))
     context = empty_context.set(REQUEST, request)
     self.assertThat(
         execute(context, interceptors),
         succeeded(
             ContainsDict({
                 REQUEST:
                 ContainsDict({
                     'multipart_params':
                     MatchesDict({
                         u'name':
                         Multipart(content_length=Equals(8),
                                   name=Equals(u'name'),
                                   headers=MatchesDict({
                                       u'Content-Disposition':
                                       Equals(u'form-data; name="name"'),
                                   }),
                                   body=Equals(b'Some One')),
                         u'email':
                         Multipart(content_length=Equals(16),
                                   name=Equals(u'email'),
                                   headers=MatchesDict({
                                       u'Content-Disposition':
                                       Equals(u'form-data; name="email"'),
                                   }),
                                   body=Equals(b'*****@*****.**')),
                         u'avatar':
                         Multipart(
                             content_length=Equals(869),
                             content_type=Equals(u'image/png'),
                             filename=Equals(u'smiley-cool.png'),
                             name=Equals(u'avatar'),
                             headers=MatchesDict({
                                 u'Content-Type':
                                 Equals(u'image/png'),
                                 u'Content-Disposition':
                                 Equals(
                                     u'form-data; name="avatar"; filename="smiley-cool.png"'
                                 ),
                             }),
                             body=After(
                                 lambda x: hashlib.sha256(x).hexdigest(),
                                 Equals(
                                     b'25fbe073db80f71a13fb8e0a190a76c0fda494d18849fa6fa87ea5a0924baa07'
                                 ))),
                         # XXX: This syntax isn't supported by the multipart
                         # parser, multiple things with the same name are
                         # overwritten.
                         u'attachments[]':
                         Always(),
                     }),
                     'form_params':
                     MatchesDict({
                         u'name': Equals(u'Some One'),
                         u'email': Equals(u'*****@*****.**')
                     })
                 })
             })))
def is_response_with_body(body, content_type=None, code=200, method="read"):
    return MatchesAll(
        is_response(code, content_type),
        After(methodcaller(method), Equals(body)),
    )