Skip to content

Commit

Permalink
Merge pull request #5 from im-voracity/workflows
Browse files Browse the repository at this point in the history
Workflows
  • Loading branch information
im-voracity authored Mar 21, 2024
2 parents 8d05491 + ca98fff commit 692def7
Show file tree
Hide file tree
Showing 10 changed files with 442 additions and 153 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*.py]
max_line_length = 100
6 changes: 6 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

[flake8]
exclude =
.venv
__init__.py
max-line-length = 100
39 changes: 39 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
name: Checks

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
test-lint:
name: Test and Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.8.1
architecture: x64

- name: Install Poetry
uses: snok/[email protected]
with:
virtualenvs-create: true
virtualenvs-in-project: true

- name: Install dependencies
run: poetry install --groups dev --no-root

- name: Run tests
run: poetry run python -m unittest discover -s tests

- name: Run flake8
run: poetry run flake8
12 changes: 11 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
0.3.0 / 2024-03-21
==================

* Breaking change: Removed support for python <3.8 due to the use of flake8
for linting.
* Added .editorconfig file for better code standardization.
* Code slightly refactored to comply with flake8.
* Added GitHub Actions for Testing and Linting.

0.2.2 / 2024-03-21
==================

Expand All @@ -11,7 +20,8 @@

* Added subscriptions endpoint
* Added tests for subscriptions
* Changed _get_with_token and _post_with_token methods to use the new _request_with_token for more flexibility
* Changed _get_with_token and _post_with_token methods to use the new _request_with_token for more
flexibility
* Fixed issue with pagination
* Updated docs for the new subscriptions endpoint
* Renamed README.dev.md to CONTRIBUTING.md, for better standardization
Expand Down
162 changes: 103 additions & 59 deletions hotmart_python/hotmart.py

Large diffs are not rendered by default.

54 changes: 46 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
[tool.poetry]
name = "hotmart-python"
version = "0.2.2"
version = "0.3.0"
description = "A Python library for the Hotmart API, simplifying endpoint access and resource management."
authors = ["Matheus Tenório <[email protected]>"]
license = "Apache License, Version 2.0"
readme = "docs/README.md"

[tool.poetry.dependencies]
python = "^3.7"
python = "^3.8.1"
coloredlogs = "^15.0.1"
requests = "^2.31.0"


[tool.poetry.group.dev.dependencies]
flake8 = "^7.0.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
109 changes: 78 additions & 31 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
from unittest.mock import patch
from hotmart_python import Hotmart, RequestException, HTTPRequestException

client_id = 'b32450c1-1352-246a-b6d3-d49d6db815ea'
client_secret = '90bcc221-cebd-5a5b-00e2-72cab47d9282'
basic = ('Basic YjIzNTQxYzAtMyEzNS20MjVhLWI1ZDItZDM4ZDVkYjcwNGVhOjA5Y2JiMTEz'
'LWRiZWMtNGI0YS05OWUxLTI3Y2FiNDdkOTI4Mg==')


class TestHotmart(unittest.TestCase):
def setUp(self):
self.hotmart = Hotmart(client_id='b32450c1-1352-246a-b6d3-d49d6db815ea',
client_secret='90bcc221-cebd-5a5b-00e2-72cab47d9282',
basic='Basic '
'YjIzNTQxYzAtMyEzNS20MjVhLWI1ZDItZDM4ZDVkYjcwNGVhOjA5Y2JiMTEzLWRiZWMtNGI0YS05OWUx'
'LTI3Y2FiNDdkOTI4Mg==')
self.hotmart = Hotmart(client_id=client_id,
client_secret=client_secret,
basic=basic)

# Build Payload
def test_build_payload_with_all_values(self):
Expand All @@ -27,12 +30,14 @@ def test_build_payload_with_no_values(self):

# Sandbox Mode
def test_sandbox_mode_true(self):
hotmart = Hotmart(client_id='123', client_secret='123', basic='123', sandbox=True)
hotmart = Hotmart(client_id='123', client_secret='123', basic='123',
sandbox=True)
self.assertTrue(hotmart.sandbox)

def test_sandbox_mode_false(self):
hotmart1 = Hotmart(client_id='123', client_secret='123', basic='123')
hotmart2 = Hotmart(client_id='123', client_secret='123', basic='123', sandbox=False)
hotmart2 = Hotmart(client_id='123', client_secret='123', basic='123',
sandbox=False)

self.assertFalse(hotmart1.sandbox)
self.assertFalse(hotmart2.sandbox)
Expand All @@ -41,12 +46,15 @@ def test_sandbox_mode_false(self):
def test_successful_request(self, mock_get):
mock_get.return_value.status_code = 200
mock_get.return_value.json.return_value = {"success": True}
response = self.hotmart._make_request(requests.get, 'https://example.com')
response = self.hotmart._make_request(requests.get,
'https://example.com')
self.assertEqual(response, {"success": True})

@patch('requests.get')
def test_http_error_request(self, mock_get):
mock_get.return_value.raise_for_status.side_effect = requests.exceptions.HTTPError
mock_get.return_value.raise_for_status.side_effect = (
requests.exceptions.HTTPError)

with self.assertRaises(HTTPRequestException):
self.hotmart._make_request(requests.get, 'https://example.com')

Expand All @@ -60,15 +68,19 @@ def test_request_exception(self, mock_get):
def test_forbidden_request_in_sandbox_mode(self, mock_get):
self.hotmart.sandbox = True
mock_get.return_value.status_code = 403
mock_get.return_value.raise_for_status.side_effect = requests.exceptions.HTTPError
mock_get.return_value.raise_for_status.side_effect = (
requests.exceptions.HTTPError)

with self.assertRaises(HTTPRequestException):
self.hotmart._make_request(requests.get, 'https://example.com')

@patch('requests.get')
def test_forbidden_request_in_production_mode(self, mock_get):
self.hotmart.sandbox = False
mock_get.return_value.status_code = 403
mock_get.return_value.raise_for_status.side_effect = requests.exceptions.HTTPError
mock_get.return_value.raise_for_status.side_effect = (
requests.exceptions.HTTPError)

with self.assertRaises(HTTPRequestException):
self.hotmart._make_request(requests.get, 'https://example.com')

Expand All @@ -86,7 +98,8 @@ def test_token_not_expired_when_expiry_in_future(self, mock_time):

@patch('requests.post')
def test_token_obtained_successfully(self, mock_post):
mock_post.return_value.json.return_value = {'access_token': 'test_token'}
mock_post.return_value.json.return_value = {
'access_token': 'test_token'}
token = self.hotmart._fetch_new_token()
self.assertEqual(token, 'test_token')

Expand All @@ -98,7 +111,8 @@ def test_token_obtained_failure(self, mock_post):

@patch.object(Hotmart, '_is_token_expired')
@patch.object(Hotmart, '_fetch_new_token')
def test_token_found_in_cache(self, mock_fetch_new_token, mock_is_token_expired):
def test_token_found_in_cache(self, mock_fetch_new_token,
mock_is_token_expired):
mock_is_token_expired.return_value = False
self.hotmart.token_cache = 'test_token'
token = self.hotmart._get_token()
Expand All @@ -107,70 +121,103 @@ def test_token_found_in_cache(self, mock_fetch_new_token, mock_is_token_expired)

@patch.object(Hotmart, '_is_token_expired')
@patch.object(Hotmart, '_fetch_new_token')
def test_token_not_in_cache_and_fetched_successfully(self, mock_fetch_new_token, mock_is_token_expired):
def test_token_not_in_cache_and_fetched_success(self,
mock_fetch_new_token,
mock_is_token_expired):
mock_is_token_expired.return_value = True
mock_fetch_new_token.return_value = 'new_token'
token = self.hotmart._get_token()
self.assertEqual(token, 'new_token')

@patch.object(Hotmart, '_is_token_expired')
@patch.object(Hotmart, '_fetch_new_token')
def test_token_not_in_cache_and_fetch_failed(self, mock_fetch_new_token, mock_is_token_expired):
def test_token_not_in_cache_and_fetch_failed(self, mock_fetch_new_token,
mock_is_token_expired):
mock_is_token_expired.return_value = True
mock_fetch_new_token.return_value = None
token = self.hotmart._get_token()
self.assertIsNone(token)

@patch.object(Hotmart, '_get_token')
@patch.object(Hotmart, '_make_request')
def test_successful_request_with_token(self, mock_make_request, mock_get_token):
def test_successful_request_with_token(self, mock_make_request,
mock_get_token):
mock_get_token.return_value = 'test_token'
mock_make_request.return_value = {"success": True}
result = self.hotmart._request_with_token('GET', 'https://example.com')
result = self.hotmart._request_with_token('GET',
'https://example.com')
self.assertEqual(result, {"success": True})

@patch.object(Hotmart, '_get_token')
@patch.object(Hotmart, '_make_request')
def test_failed_request_with_token(self, mock_make_request, mock_get_token):
def test_failed_request_with_token(self, mock_make_request,
mock_get_token):
mock_get_token.return_value = 'test_token'
mock_make_request.side_effect = RequestException("Error", "url")
mock_make_request.side_effect = RequestException("Error",
"url")
with self.assertRaises(RequestException):
self.hotmart._request_with_token('GET', 'https://example.com')
self.hotmart._request_with_token('GET',
'https://example.com')

@patch.object(Hotmart, '_get_token')
@patch.object(Hotmart, '_make_request')
def test_unsupported_method_with_token(self, mock_make_request, mock_get_token):
def test_unsupported_method_with_token(self,
mock_get_token):
mock_get_token.return_value = 'test_token'
with self.assertRaises(ValueError):
self.hotmart._request_with_token('PUT', 'https://example.com')
self.hotmart._request_with_token('PUT',
'https://example.com')

@patch.object(Hotmart, '_request_with_token')
def test_pagination_without_pagination(self, mock_request_with_token):
mock_request_with_token.return_value = {"items": ["item1", "item2"]}
result = self.hotmart._pagination('GET', 'https://example.com')
mock_request_with_token.return_value = {
"items": ["item1", "item2"]
}
result = self.hotmart._pagination('GET',
'https://example.com')

self.assertEqual(result, ["item1", "item2"])

@patch.object(Hotmart, '_request_with_token')
def test_pagination_with_single_page(self, mock_request_with_token):
mock_request_with_token.return_value = {"items": ["item1", "item2"], "page_info": {}}
result = self.hotmart._pagination('GET', 'https://example.com', paginate=True)
mock_request_with_token.return_value = {
"items": ["item1", "item2"],
"page_info": {}
}

result = self.hotmart._pagination('GET',
'https://example.com',
paginate=True)

self.assertEqual(result, ["item1", "item2"])

@patch.object(Hotmart, '_request_with_token')
def test_pagination_with_multiple_pages(self, mock_request_with_token):
mock_request_with_token.side_effect = [
{"items": ["item1", "item2"], "page_info": {"next_page_token": "token"}},
{"items": ["item3", "item4"], "page_info": {}}
{
"items": ["item1", "item2"],
"page_info": {"next_page_token": "token"}
},
{
"items": ["item3", "item4"],
"page_info": {}
}
]
params = {}
result = self.hotmart._pagination('GET', 'https://example.com', params=params, paginate=True)
result = self.hotmart._pagination('GET',
'https://example.com',
params=params,
paginate=True)

self.assertEqual(result, ["item1", "item2", "item3", "item4"])

@patch.object(Hotmart, '_request_with_token')
def test_pagination_with_failed_first_page(self, mock_request_with_token):
mock_request_with_token.return_value = None

with self.assertRaises(ValueError):
self.hotmart._pagination('GET', 'https://example.com', paginate=True)
self.hotmart._pagination('GET',
'https://example.com',
paginate=True)


if __name__ == '__main__':
Expand Down
Loading

0 comments on commit 692def7

Please sign in to comment.