Skip to content

Commit

Permalink
feat(operator): add new operators
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed Mar 14, 2017
1 parent 0b179ff commit 77ab585
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 19 deletions.
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ HTTP assertions plugin for `grappa`_.

To get started, take a look to the `documentation`_, `tutorial`_ and `examples`_.


In a nutshell
-------------

.. code-block:: python
import requests
import grappa-http import grappa
Features
--------

Expand Down
6 changes: 5 additions & 1 deletion grappa_http/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
"""
# Export register function
from .plugin import register
from grappa import should, expect, use

# Register Python operator
__all__ = ('register',)
__all__ = ('should', 'expect', 'register')

# Package metadata
__author__ = 'Tomas Aparicio'
__license__ = 'MIT'

# Current package version
__version__ = '0.1.0'

# Self-register plugin in grappa
use(register)
1 change: 1 addition & 0 deletions grappa_http/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
operators = (
# Module name # Operator class to import
('attributes', ),
('method', 'MethodOperator'),
('header', 'HeaderOperator'),
('content', 'ContentTypeOperator'),
('status', 'StatusOperator', 'OkStatusOperator',
Expand Down
47 changes: 29 additions & 18 deletions grappa_http/operators/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,18 @@ class HeaderOperator(BaseOperator):
# Operator keywords
aliases = ('of', 'to', 'be', 'equal', 'match')

# Enable value diff
show_diff = True

# Error message templates
expected_message = Operator.Dsl.Message(
'a response that match header(s): "{value}"',
'a response that does not match header(s): "{value}"',
'a response that match header: "{value}"',
'a response that does not match header: "{value}"',
)

# Subject message template
subject_message = Operator.Dsl.Message(
'a response with header(s): {value}',
'a header with value: {value}',
)

def match_header(self, headers, key, value, includes=False):
Expand All @@ -66,38 +69,46 @@ def match_header(self, headers, key, value, includes=False):

return True, None

def _match(self, res, headers, value=None, includes=False):
# def differ(self):
# print('>>>> differ!')
# return self.header

def _match(self, res, header, value=None, includes=False):
# Set subject headers
self.subject = res.headers
self.subject = res.headers.get(header)

if not headers:
if not header:
return False, ['header argument cannot be empty']

if isinstance(headers, str):
headers = [headers]
if not isinstance(header, str):
return False, ['header must be a string']

# Set expected headers
self.expected = headers
self.expected = value if value else header

# Stores error reasons and header values
reasons = []
values = []
reasons = []

for header in headers:
matches, reason = self.match_header(res.headers, header,
value, includes=includes)
if not matches and reason:
reasons.append(reason)
matches, reason = self.match_header(res.headers, header,
value, includes=includes)
if not matches and reason:
reasons.append(reason)

header = res.headers.get(header)
if header:
values.append(header)
header = res.headers.get(header)
if header:
values.append(header)

# Stores if the tests passed
passed = len(reasons) == 0

# Set headers
self.header = header
# self.ctx.value = {}

# Assign match value
if value is None:
self.ctx.value = values[0] if len(values) == 1 else values
self.ctx.yielded = values[0] if len(values) == 1 else values

return passed, reasons
54 changes: 54 additions & 0 deletions grappa_http/operators/method.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
from .base import BaseOperator, Operator


class MethodOperator(BaseOperator):
"""
Asserts HTTP request/response method.
Example::
# Should style
res | should.be.method('GET')
# Should style - negation form
res | should.not_be.method('GET')
# Expect style
res | expect.to.be.method('GET')
# Expect style - negation form
res | expect.to_not.be.method('GET')
"""

# Defines operator kind
kind = Operator.Type.MATCHER

# Operator keywords
operators = ('method', 'verb')

# Operator aliases
aliases = ('name', 'equal', 'to', 'be', 'of')

# Error message templates
expected_message = Operator.Dsl.Message(
'a request method that is equal to "{value}"',
'a request method that is not equal to "{value}"',
)

# Subject message template
subject_message = Operator.Dsl.Message(
'a request method "{value}"',
)

def _match(self, res, method):
# Define subject as request method
self.subject = res.method

if not isinstance(method, str):
return False, ['method argument must be a string']

if not method:
return False, ['method argument cannot be empty']

return method.upper() == res.method, ['request method does not match']
8 changes: 8 additions & 0 deletions grappa_http/plugin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from .operators import get

# Stores if the plugin has been registered
registered = False


def register(engine):
"""
Plugin register function.
"""
global registered
if registered:
return

registered = True
engine.register(*get())
18 changes: 18 additions & 0 deletions tests/operators/method_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pook
import pytest
import requests


def test_method_presence(should):
pook.get('foo.com', reply=200, response_type='json')
res = requests.get('http://foo.com')

res | should.have.method('GET')
res | should.have.method.equal.to('GET')
res | should.have_not.method.equal.to('POST')

with pytest.raises(AssertionError):
res | should.have.method.equal.to('POST')

with pytest.raises(AssertionError):
res | should.have_not.method.equal.to('GET')

0 comments on commit 77ab585

Please sign in to comment.