From 98fa063162636917fc3673160746895feeea3e01 Mon Sep 17 00:00:00 2001 From: David Lacho Date: Thu, 27 Jun 2024 03:01:41 -0400 Subject: [PATCH] feat: remove vcr dependency and upgrade urllib3 (#98) --- CHANGELOG.md | 5 + chartmogul/version.py | 2 +- fixtures/delete_invoice.yaml | 142 ------------------ fixtures/fetch_account.yaml | 60 -------- requirements-test.txt | 1 - requirements.txt | 2 +- setup.py | 3 +- test/integration/test_delete_invoice.py | 183 ++++++++++++++++++++++-- test/integration/test_fetch_account.py | 23 ++- 9 files changed, 202 insertions(+), 219 deletions(-) delete mode 100644 fixtures/delete_invoice.yaml delete mode 100644 fixtures/fetch_account.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2118435..68d7f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning]. [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ [Semantic Versioning]: https://semver.org/spec/v2.0.0.html +## [4.3.2] - 2024-06-26 +- Remove VCR dependencies +- Replaced unit tests that depended on VCR using `request_mock` +- Updated urllib3 to latest secure version + ## [4.3.1] - 2024-06-20 - Update the urllib3 dependency to a secure version diff --git a/chartmogul/version.py b/chartmogul/version.py index ed48cda..1709a57 100644 --- a/chartmogul/version.py +++ b/chartmogul/version.py @@ -1 +1 @@ -__version__ = "4.3.1" +__version__ = "4.3.2" diff --git a/fixtures/delete_invoice.yaml b/fixtures/delete_invoice.yaml deleted file mode 100644 index 1fbad18..0000000 --- a/fixtures/delete_invoice.yaml +++ /dev/null @@ -1,142 +0,0 @@ -interactions: -- request: - body: !!python/unicode '{"name": "Test"}' - headers: - Accept: ['*/*'] - Accept-Encoding: ['gzip, deflate'] - Connection: [keep-alive] - Content-Length: ['16'] - User-Agent: [python-requests/2.13.0] - content-type: [application/json] - method: POST - uri: https://api.chartmogul.com/v1/data_sources - response: - body: {string: !!python/unicode '{"uuid":"ds_20b98cde-565d-11e7-953a-57e7aa662af2","name":"Test","system":"Import - API","created_at":"2017-06-21T08:39:23.884Z","status":"idle"}'} - headers: - access-control-allow-credentials: ['true'] - connection: [keep-alive] - content-length: ['142'] - content-type: [application/json] - date: ['Wed, 21 Jun 2017 08:39:23 GMT'] - server: [nginx/1.10.1] - status: [201 Created] - status: {code: 201, message: Created} -- request: - body: !!python/unicode '{"city": "New York", "name": "Adam Smith", "country": - "US", "data_source_uuid": "ds_20b98cde-565d-11e7-953a-57e7aa662af2", "external_id": - "cus_0001", "email": "adam@smith.com"}' - headers: - Accept: ['*/*'] - Accept-Encoding: ['gzip, deflate'] - Connection: [keep-alive] - Content-Length: ['176'] - User-Agent: [python-requests/2.13.0] - content-type: [application/json] - method: POST - uri: https://api.chartmogul.com/v1/customers - response: - body: {string: !!python/unicode '{"id":9232806,"uuid":"cus_20fdab30-565d-11e7-953a-c3eecbf6873c","external_id":"cus_0001","name":"Adam - Smith","email":"adam@smith.com","status":"Lead","customer-since":null,"attributes":{"custom":{},"clearbit":{},"stripe":{},"tags":[]},"data_source_uuid":"ds_20b98cde-565d-11e7-953a-57e7aa662af2","data_source_uuids":["ds_20b98cde-565d-11e7-953a-57e7aa662af2"],"external_ids":["cus_0001"],"company":"","country":"US","state":null,"city":"New - York","zip":null,"lead_created_at":null,"free_trial_started_at":null,"address":{"country":"United - States","state":null,"city":"New York","address_zip":null},"mrr":0,"arr":0,"billing-system-url":null,"chartmogul-url":"https://app.chartmogul.com/#customers/9232806-Adam_Smith","billing-system-type":"Import - API","currency":"USD","currency-sign":"$"}'} - headers: - access-control-allow-credentials: ['true'] - connection: [keep-alive] - content-length: ['788'] - content-type: [application/json] - date: ['Wed, 21 Jun 2017 08:39:24 GMT'] - server: [nginx/1.10.1] - status: [201 Created] - status: {code: 201, message: Created} -- request: - body: !!python/unicode '{"interval_unit": "month", "external_id": "plan_0001", - "name": "Bronze Plan", "data_source_uuid": "ds_20b98cde-565d-11e7-953a-57e7aa662af2", - "interval_count": 1}' - headers: - Accept: ['*/*'] - Accept-Encoding: ['gzip, deflate'] - Connection: [keep-alive] - Content-Length: ['161'] - User-Agent: [python-requests/2.13.0] - content-type: [application/json] - method: POST - uri: https://api.chartmogul.com/v1/plans - response: - body: {string: !!python/unicode '{"external_id":"plan_0001","name":"Bronze Plan","interval_count":1,"interval_unit":"month","data_source_uuid":"ds_20b98cde-565d-11e7-953a-57e7aa662af2","uuid":"pl_21436c24-565d-11e7-99b5-c30bc3e995a3"}'} - headers: - cache-control: ['max-age=0, private, must-revalidate'] - connection: [keep-alive] - content-type: [application/json; charset=utf-8] - date: ['Wed, 21 Jun 2017 08:39:24 GMT'] - etag: [W/"7f595dd6f5144f7845844809661d0df5"] - server: [nginx/1.10.1] - status: [201 Created] - strict-transport-security: [max-age=15768000] - x-content-type-options: [nosniff] - x-frame-options: [SAMEORIGIN] - x-request-id: [05f1714e-1e39-4f93-b959-a49d0a450b0f] - x-runtime: ['0.079308'] - x-xss-protection: [1; mode=block] - status: {code: 201, message: Created} -- request: - body: !!python/unicode '{"invoices": [{"line_items": [{"tax_amount_in_cents": - 900, "service_period_end": "2015-12-01T00:00:00", "amount_in_cents": 5000, "service_period_start": - "2015-11-01T00:00:00", "plan_uuid": "pl_21436c24-565d-11e7-99b5-c30bc3e995a3", - "discount_amount_in_cents": 1000, "type": "subscription", "subscription_external_id": - "sub_0001", "discount_code": "PSO86", "quantity": 1}], "due_date": "2015-11-15T00:00:00", - "transactions": [{"date": "2015-11-05T00:14:23", "type": "payment", "result": - "successful"}], "currency": "USD", "date": "2015-11-01T00:00:00", "external_id": - "INV0001"}]}' - headers: - Accept: ['*/*'] - Accept-Encoding: ['gzip, deflate'] - Connection: [keep-alive] - Content-Length: ['579'] - User-Agent: [python-requests/2.13.0] - content-type: [application/json] - method: POST - uri: https://api.chartmogul.com/v1/import/customers/cus_20fdab30-565d-11e7-953a-c3eecbf6873c/invoices - response: - body: {string: !!python/unicode '{"invoices":[{"uuid":"inv_fb369587-7ed5-4049-9f02-8f6a8000499c","external_id":"INV0001","date":"2015-11-01T00:00:00.000Z","due_date":"2015-11-15T00:00:00.000Z","currency":"USD","line_items":[{"uuid":"li_1421544c-04bc-49f8-819c-8d37fe0bea9f","external_id":null,"type":"subscription","subscription_uuid":"sub_81a2a3b2-94f1-4858-94ea-852d9487acad","subscription_external_id":"sub_0001","plan_uuid":"pl_21436c24-565d-11e7-99b5-c30bc3e995a3","prorated":false,"service_period_start":"2015-11-01T00:00:00.000Z","service_period_end":"2015-12-01T00:00:00.000Z","amount_in_cents":5000,"quantity":1,"discount_code":"PSO86","discount_amount_in_cents":1000,"tax_amount_in_cents":900,"account_code":""}],"transactions":[{"uuid":"tr_a3d85ea1-7ae0-4a72-af28-3457993b3b4e","external_id":null,"type":"payment","date":"2015-11-05T00:14:23.000Z","result":"successful"}]}]}'} - headers: - cache-control: ['max-age=0, private, must-revalidate'] - connection: [keep-alive] - content-type: [application/json; charset=utf-8] - date: ['Wed, 21 Jun 2017 08:39:25 GMT'] - etag: [W/"22a408937b44a26f3ebe7e04fea0fe40"] - server: [nginx/1.10.1] - strict-transport-security: [max-age=15768000] - x-content-type-options: [nosniff] - x-frame-options: [SAMEORIGIN] - x-request-id: [ea103d4e-ea0c-43b2-98b5-c67596b470db] - x-runtime: ['0.381086'] - x-xss-protection: [1; mode=block] - status: {code: 201, message: Created} -- request: - body: null - headers: - Accept: ['*/*'] - Accept-Encoding: ['gzip, deflate'] - Connection: [keep-alive] - Content-Length: ['0'] - User-Agent: [python-requests/2.13.0] - content-type: [application/json] - method: DELETE - uri: https://api.chartmogul.com/v1/invoices/inv_fb369587-7ed5-4049-9f02-8f6a8000499c - response: - body: {string: !!python/unicode ''} - headers: - cache-control: [no-cache] - connection: [keep-alive] - date: ['Wed, 21 Jun 2017 08:39:25 GMT'] - server: [nginx/1.10.1] - strict-transport-security: [max-age=15768000] - x-content-type-options: [nosniff] - x-frame-options: [SAMEORIGIN] - x-request-id: [2cc5f524-46f3-4752-bd05-0c72893cc106] - x-runtime: ['0.148497'] - x-xss-protection: [1; mode=block] - status: {code: 204, message: No Content} -version: 1 diff --git a/fixtures/fetch_account.yaml b/fixtures/fetch_account.yaml deleted file mode 100644 index 017da13..0000000 --- a/fixtures/fetch_account.yaml +++ /dev/null @@ -1,60 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - chartmogul-python/4.0.0 - content-type: - - application/json - method: GET - uri: https://api.chartmogul.com/v1/account - response: - body: - string: '{"name":"Chartmogul Test","currency":"EUR","time_zone":"Europe/Lisbon","week_start_on":"monday"}' - headers: - Access-Control-Allow-Credentials: - - 'true' - Access-Control-Allow-Headers: - - Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization - Access-Control-Allow-Methods: - - GET, OPTIONS - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - max-age=0, private, must-revalidate - Connection: - - keep-alive - Content-Length: - - '96' - Content-Type: - - application/json; charset=utf-8 - Date: - - Sun, 10 Sep 2023 20:18:46 GMT - ETag: - - W/"4c95c647e6119fa4820a4ca4d344ec3d" - Referrer-Policy: - - strict-origin-when-cross-origin - X-Content-Type-Options: - - nosniff - X-Download-Options: - - noopen - X-Frame-Options: - - DENY - X-Permitted-Cross-Domain-Policies: - - none - X-Request-Id: - - a388e7b97b3cdbb5aceba74e80d345fc - X-Runtime: - - '0.022753' - X-XSS-Protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/requirements-test.txt b/requirements-test.txt index 502cf79..667bb21 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,6 @@ -r requirements.txt mock>=5.1.0 requests-mock>=1.11.0 -vcrpy>=4.4.0 PyYAML>=6.0.1 httpretty>=1.1.4 wrapt>=1.15.0 diff --git a/requirements.txt b/requirements.txt index 8cb2680..edf4a47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ uritemplate>=3.0.0 promise>=1.0.1 marshmallow>=3.19.0 future>=0.18.3 -urllib3==1.26.19 +urllib3==2.2.2 \ No newline at end of file diff --git a/setup.py b/setup.py index 9cae1ca..117e058 100644 --- a/setup.py +++ b/setup.py @@ -19,12 +19,11 @@ "promise>=2.3.0", "marshmallow>=3.19.0", "future>=0.18.3", - "urllib3==1.26.19", + "urllib3==2.2.2", ] test_requirements = [ "mock>=5.1.0", "requests-mock>=1.11.0", - "vcrpy>=4.4.0", "PyYAML>=6.0.1", "httpretty>=1.1.4", "wrapt>=1.15.0", diff --git a/test/integration/test_delete_invoice.py b/test/integration/test_delete_invoice.py index 0b86c8e..0dc07f0 100644 --- a/test/integration/test_delete_invoice.py +++ b/test/integration/test_delete_invoice.py @@ -1,11 +1,8 @@ import unittest -import vcr -from datetime import date, datetime +from datetime import datetime from promise import Promise import requests_mock -from requests.exceptions import HTTPError - from chartmogul import DataSource, Customer, Plan, Config, Invoice config = Config("-") @@ -81,11 +78,103 @@ def _delete_invoice(result): class DeleteInvoiceTestCase(unittest.TestCase): """ - Tests errors & user mistakes. + Tests deleting an invoice. """ - @vcr.use_cassette("fixtures/delete_invoice.yaml", filter_headers=["authorization"]) - def test_delete_invoice(self): + @requests_mock.Mocker() + def test_delete_invoice(self, m): + # Mock DataSource.create + m.post( + "https://api.chartmogul.com/v1/data_sources", + json={ + "uuid": "ds_20b98cde-565d-11e7-953a-57e7aa662af2", + "name": "Test", + "system": "Import API", + "created_at": "2017-06-21T08:39:23.884Z", + "status": "idle", + }, + status_code=201, + ) + + # Mock Customer.create + m.post( + "https://api.chartmogul.com/v1/customers", + json={ + "id": 9232806, + "uuid": "cus_20fdab30-565d-11e7-953a-c3eecbf6873c", + "external_id": "cus_0001", + "name": "Adam Smith", + "email": "adam@smith.com", + "status": "Lead", + "data_source_uuid": "ds_20b98cde-565d-11e7-953a-57e7aa662af2", + "country": "US", + "city": "New York", + }, + status_code=201, + ) + + # Mock Plan.create + m.post( + "https://api.chartmogul.com/v1/plans", + json={ + "external_id": "plan_0001", + "name": "Bronze Plan", + "interval_count": 1, + "interval_unit": "month", + "data_source_uuid": "ds_20b98cde-565d-11e7-953a-57e7aa662af2", + "uuid": "pl_21436c24-565d-11e7-99b5-c30bc3e995a3", + }, + status_code=201, + ) + + # Mock Invoice.create + m.post( + "https://api.chartmogul.com/v1/import/customers/cus_20fdab30-565d-11e7-953a-c3eecbf6873c/invoices", + json={ + "invoices": [ + { + "uuid": "inv_fb369587-7ed5-4049-9f02-8f6a8000499c", + "external_id": "INV0001", + "date": "2015-11-01T00:00:00.000Z", + "due_date": "2015-11-15T00:00:00.000Z", + "currency": "USD", + "line_items": [ + { + "uuid": "li_1421544c-04bc-49f8-819c-8d37fe0bea9f", + "type": "subscription", + "subscription_uuid": "sub_81a2a3b2-94f1-4858-94ea-852d9487acad", + "subscription_external_id": "sub_0001", + "plan_uuid": "pl_21436c24-565d-11e7-99b5-c30bc3e995a3", + "service_period_start": "2015-11-01T00:00:00.000Z", + "service_period_end": "2015-12-01T00:00:00.000Z", + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + } + ], + "transactions": [ + { + "uuid": "tr_a3d85ea1-7ae0-4a72-af28-3457993b3b4e", + "type": "payment", + "date": "2015-11-05T00:14:23.000Z", + "result": "successful", + } + ], + } + ] + }, + status_code=201, + ) + + # Mock Invoice.destroy + m.delete( + "https://api.chartmogul.com/v1/invoices/inv_fb369587-7ed5-4049-9f02-8f6a8000499c", + status_code=204, + ) + + # Execute the chain of promises and get the result result = ( DataSource.create(config, data={"name": "Test"}) .then(_create_plan) @@ -93,4 +182,82 @@ def test_delete_invoice(self): .then(_delete_invoice) .get() ) - print(result) + + # Assertions + # Ensure that the DataSource, Customer, and Plan were created with the correct UUIDs + ds_result = DataSource.create(config, data={"name": "Test"}).get() + self.assertEqual(ds_result.uuid, "ds_20b98cde-565d-11e7-953a-57e7aa662af2") + + customer_result = Customer.create( + config, + data={ + "data_source_uuid": ds_result.uuid, + "external_id": "cus_0001", + "name": "Adam Smith", + "email": "adam@smith.com", + "country": "US", + "city": "New York", + }, + ).get() + self.assertEqual( + customer_result.uuid, "cus_20fdab30-565d-11e7-953a-c3eecbf6873c" + ) + + plan_result = Plan.create( + config, + data={ + "data_source_uuid": ds_result.uuid, + "name": "Bronze Plan", + "interval_count": 1, + "interval_unit": "month", + "external_id": "plan_0001", + }, + ).get() + self.assertEqual(plan_result.uuid, "pl_21436c24-565d-11e7-99b5-c30bc3e995a3") + + # Ensure that the invoice was created correctly + invoice_creation_result = Invoice.create( + config, + uuid=customer_result.uuid, + data={ + "invoices": [ + { + "external_id": "INV0001", + "date": datetime(2015, 11, 1, 0, 0, 0), + "currency": "USD", + "due_date": datetime(2015, 11, 15, 0, 0, 0), + "line_items": [ + { + "type": "subscription", + "subscription_external_id": "sub_0001", + "plan_uuid": plan_result.uuid, + "service_period_start": datetime(2015, 11, 1, 0, 0, 0), + "service_period_end": datetime(2015, 12, 1, 0, 0, 0), + "amount_in_cents": 5000, + "quantity": 1, + "discount_code": "PSO86", + "discount_amount_in_cents": 1000, + "tax_amount_in_cents": 900, + } + ], + "transactions": [ + { + "date": datetime(2015, 11, 5, 0, 14, 23), + "type": "payment", + "result": "successful", + } + ], + } + ] + }, + ).get() + self.assertEqual( + invoice_creation_result.invoices[0].uuid, + "inv_fb369587-7ed5-4049-9f02-8f6a8000499c", + ) + + # Ensure that the invoice was deleted + deletion_result = Invoice.destroy( + config, uuid=invoice_creation_result.invoices[0].uuid + ) + self.assertIsNone(deletion_result.get()) diff --git a/test/integration/test_fetch_account.py b/test/integration/test_fetch_account.py index 5088493..24842c3 100644 --- a/test/integration/test_fetch_account.py +++ b/test/integration/test_fetch_account.py @@ -1,5 +1,5 @@ import unittest -import vcr +import requests_mock from chartmogul import Config, Account config = Config(api_key="-") @@ -7,10 +7,25 @@ class FetchAccountTestCase(unittest.TestCase): """ - Tests errors & user mistakes. + Tests fetching account details. """ - @vcr.use_cassette("fixtures/fetch_account.yaml", filter_headers=["authorization"], record=True) - def test_fetch_account(self): + @requests_mock.Mocker() + def test_fetch_account(self, m): + m.get( + "https://api.chartmogul.com/v1/account", + json={ + "name": "Chartmogul Test", + "currency": "EUR", + "time_zone": "Europe/Lisbon", + "week_start_on": "monday", + }, + status_code=200, + ) + account = Account.retrieve(config).get() self.assertTrue(isinstance(account, Account)) + self.assertEqual(account.name, "Chartmogul Test") + self.assertEqual(account.currency, "EUR") + self.assertEqual(account.time_zone, "Europe/Lisbon") + self.assertEqual(account.week_start_on, "monday")