Skip to content
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

Add infrastructure for test-helper methods #773

Merged
merged 10 commits into from
Mar 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ venv.bak/
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/.idea
5 changes: 5 additions & 0 deletions stripe/api_resources/abstract/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@

from stripe.api_resources.abstract.custom_method import custom_method

from stripe.api_resources.abstract.test_helpers import (
test_helpers,
APIResourceTestHelpers,
)

from stripe.api_resources.abstract.nested_resource_class_methods import (
nested_resource_class_methods,
)
77 changes: 77 additions & 0 deletions stripe/api_resources/abstract/test_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from __future__ import absolute_import, division, print_function

from stripe import error, util, six
from stripe.six.moves.urllib.parse import quote_plus
from stripe.api_resources.abstract import APIResource


class APIResourceTestHelpers:
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
"""
The base type for the TestHelper nested classes.
Handles request URL generation for test_helper custom methods.
Should be used in combination with the @test_helpers decorator.

@test_helpers
class Foo(APIResource):
class TestHelpers(APIResourceTestHelpers):
"""

def __init__(self, resource):
self.resource = resource

@classmethod
def class_url(cls):
if cls == APIResourceTestHelpers:
raise NotImplementedError(
"APIResourceTestHelpers is an abstract class. You should perform "
"actions on its subclasses (e.g. Charge, Customer)"
)
# Namespaces are separated in object names with periods (.) and in URLs
# with forward slashes (/), so replace the former with the latter.
base = cls._resource_cls.OBJECT_NAME.replace(".", "/")
return "/v1/test_helpers/%ss" % (base,)
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved

def instance_url(self):
id = self.resource.get("id")

if not isinstance(id, six.string_types):
raise error.InvalidRequestError(
"Could not determine which URL to request: %s instance "
"has invalid ID: %r, %s. ID should be of type `str` (or"
" `unicode`)" % (type(self).__name__, id, type(id)),
"id",
)

id = util.utf8(id)
base = self.class_url()
extn = quote_plus(id)
return "%s/%s" % (base, extn)


def test_helpers(cls):
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
"""
test_helpers decorator adds a test_helpers property and
wires the parent resource class to the nested TestHelpers class.

Should only be used on types that inherit from APIResource.

@test_helpers
class Foo(APIResource):
class TestHelpers(APIResourceTestHelpers):
"""

def test_helpers_getter(self):
return self.TestHelpers(self)

if not issubclass(cls, APIResource):
raise ValueError(
"Could not apply @test_helpers decorator to %r."
" The class should a subclass of APIResource." % cls
)

cls.TestHelpers._resource_cls = cls
cls.TestHelpers._static_request = cls._static_request
cls.TestHelpers._static_request_stream = cls._static_request_stream
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved

cls.test_helpers = property(test_helpers_getter)
return cls
66 changes: 66 additions & 0 deletions tests/api_resources/abstract/test_test_helpers_api_resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from __future__ import absolute_import, division, print_function

import stripe
import pytest
from stripe import util
from stripe.api_resources.abstract import APIResourceTestHelpers


class TestTestHelperAPIResource(object):
@stripe.api_resources.abstract.test_helpers
class MyTestHelpersResource(stripe.api_resources.abstract.APIResource):
OBJECT_NAME = "myresource"

@stripe.api_resources.abstract.custom_method(
"do_stuff", http_verb="post", http_path="do_the_thing"
)
class TestHelpers(APIResourceTestHelpers):
def __init__(self, resource):
self.resource = resource

def do_stuff(self, idempotency_key=None, **params):
url = self.instance_url() + "/do_the_thing"
headers = util.populate_headers(idempotency_key)
self.resource.refresh_from(
self.resource.request("post", url, params, headers)
)
return self.resource

def test_call_custom_method_class(self, request_mock):
request_mock.stub_request(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"id": "mid", "thing_done": True},
rheaders={"request-id": "req_id"},
)

obj = self.MyTestHelpersResource.TestHelpers.do_stuff("mid", foo="bar")

request_mock.assert_requested(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"foo": "bar"},
)
assert obj.thing_done is True

def test_call_custom_method_instance_via_property(self, request_mock):
request_mock.stub_request(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"id": "mid", "thing_done": True},
rheaders={"request-id": "req_id"},
)

obj = self.MyTestHelpersResource.construct_from({"id": "mid"}, "mykey")
obj.test_helpers.do_stuff(foo="bar")

request_mock.assert_requested(
"post",
"/v1/test_helpers/myresources/mid/do_the_thing",
{"foo": "bar"},
)
assert obj.thing_done is True

def test_helper_decorator_raises_for_non_resource(self):
with pytest.raises(ValueError):
stripe.api_resources.abstract.test_helpers(str)
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ deps =
pytest-cov >= 2.8.1, < 2.11.0
pytest-mock >= 2.0.0
pytest-xdist >= 1.31.0
commands = pytest --cov {posargs:-n auto}
# ignore stripe directory as all tests are inside ./tests
commands = pytest --cov {posargs:-n auto} --ignore stripe
pakrym-stripe marked this conversation as resolved.
Show resolved Hide resolved
# compilation flags can be useful when prebuilt wheels cannot be used, e.g.
# PyPy 2 needs to compile the `cryptography` module. On macOS this can be done
# by passing the following flags:
Expand Down