-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Cassettes mutation #14
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
try: | ||
from inspect import Signature | ||
|
||
get_signature = Signature.from_callable # pylint: disable=unused-import | ||
except ImportError: | ||
from funcsigs import signature as get_signature # pylint: disable=unused-import | ||
|
||
|
||
try: | ||
from collections.abc import Iterable # pylint: disable=unused-import | ||
except ImportError: | ||
# Python 2 | ||
from collections import Iterable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from .mutagens import status_code, malform_body, empty_body, reduce_body | ||
from .core import mutation, MutationGroup | ||
|
||
|
||
DEFAULT_STATUS_CODES = (400, 401, 403, 404, 500, 501, 502, 503) | ||
|
||
DEFAULT = MutationGroup((status_code(code=DEFAULT_STATUS_CODES), empty_body, malform_body, reduce_body)) | ||
|
||
del MutationGroup # Not needed in the API |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import attr | ||
|
||
from .._compat import get_signature | ||
|
||
|
||
@attr.s(slots=True) | ||
class Mutation(object): | ||
func = attr.ib() | ||
context = attr.ib(factory=dict) | ||
_signature = attr.ib(init=False) | ||
|
||
@classmethod | ||
def from_function(cls, func): | ||
return cls(func) | ||
|
||
@property | ||
def signature(self): | ||
if not hasattr(self, "_signature"): | ||
self._signature = get_signature(self.func) | ||
return self._signature | ||
|
||
def __call__(self, **context): | ||
self.validate_call(**context) | ||
return self.__class__(self.func, context) | ||
|
||
def validate_call(self, **context): | ||
self.signature.bind(cassette={}, **context) | ||
|
||
def generate(self): | ||
yield self | ||
|
||
def apply(self, cassette): | ||
return self.func(cassette, **self.context) | ||
|
||
|
||
mutation = Mutation.from_function | ||
|
||
|
||
@attr.s(slots=True) | ||
class MutationGroup(object): | ||
mutations = attr.ib() | ||
|
||
def generate(self): | ||
for mutation in self.mutations: | ||
# change to "yield from" after dropping Python 2 | ||
for one in self.generate_one(mutation): | ||
yield one | ||
|
||
def generate_one(self, mutation): | ||
# change to "yield from" after dropping Python 2 | ||
for one in mutation.generate(): | ||
yield one |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import json | ||
import random | ||
|
||
from .._compat import Iterable | ||
from .core import mutation, Mutation | ||
|
||
|
||
class ChangeStatusCode(Mutation): | ||
def generate(self): | ||
# "code" will always be in the context, because it is validated before | ||
if isinstance(self.context["code"], Iterable): | ||
for status_code in self.context["code"]: | ||
yield Mutation(self.func, {"code": status_code}) | ||
else: | ||
yield self | ||
|
||
|
||
@ChangeStatusCode.from_function | ||
def status_code(cassette, code): | ||
cassette["status"]["code"] = code | ||
|
||
|
||
@mutation | ||
def empty_body(cassette): | ||
"""Make the body empty.""" | ||
cassette["body"]["string"] = b"" | ||
|
||
|
||
@mutation | ||
def malform_body(cassette): | ||
"""Add chars to the body to make it not decodable.""" | ||
cassette["body"]["string"] = b"xX" + cassette["body"]["string"] + b"Xx" | ||
|
||
|
||
@mutation | ||
def reduce_body(cassette): | ||
data = json.loads(cassette["body"]["string"]) | ||
# it could be a list or other json type | ||
# take out random key | ||
key = random.choice(list(data.keys())) | ||
del data[key] | ||
cassette["body"]["string"] = json.dumps(data) | ||
|
||
|
||
# TODO. | ||
# XML - Add / remove XML tags. | ||
# JSON - add keys, remove keys, replace values | ||
# JSON - shrink lists, extend lists |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if body format is not JSON (e.g. XML)?