From 0d833815c9a5510945e441cb32fdf18e49e83c4d Mon Sep 17 00:00:00 2001 From: Paul Cothenet Date: Wed, 14 Jul 2021 21:59:50 +0200 Subject: [PATCH] Add bitcoin estimates and registry_url (#26) * Add bitcoin and registry_url * Fix existing test * Reformat files * Inline black * Add bitcoin test * Autoformat * Pre-commit instructions * Fix flake8 * Remove ; --- .github/workflows/black.yml | 13 --- .github/workflows/test.yml | 7 ++ .pre-commit-config.yaml | 14 +-- CHANGELOG.md | 7 ++ Makefile | 3 +- README.md | 45 ++++++--- patch_api/__init__.py | 2 +- patch_api/api/estimates_api.py | 159 +++++++++++++++++++++++++++++++ patch_api/api/orders_api.py | 16 +++- patch_api/api/preferences_api.py | 10 ++ patch_api/api/projects_api.py | 6 ++ patch_api/api_client.py | 2 +- patch_api/configuration.py | 2 +- patch_api/models/order.py | 37 ++++++- setup.py | 2 +- test-requirements.txt | 1 + test/test_estimates_api.py | 27 ++++++ test/test_projects_api.py | 4 +- 18 files changed, 315 insertions(+), 42 deletions(-) delete mode 100644 .github/workflows/black.yml diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml deleted file mode 100644 index 9f371ef..0000000 --- a/.github/workflows/black.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Lint -on: - push: - branches: - - main - pull_request: -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: psf/black@stable diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8dbf2a..4457110 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,13 @@ on: pull_request: jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: psf/black@stable + build-and-test: runs-on: ubuntu-latest name: Python Library tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 53ad7c4..39aaa05 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,13 @@ repos: -- repo: https://github.com/ambv/black - rev: stable - hooks: - - id: black - language_version: python3.6 - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v1.2.3 + rev: v2.3.0 hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace - id: flake8 args: ['--config=.flake8'] +- repo: https://github.com/psf/black + rev: 21.6b0 + hooks: + - id: black diff --git a/CHANGELOG.md b/CHANGELOG.md index c036929..a7163ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6.0] - 2021-07-14 + +### Added + +- Order responses return a `registry_url` field +- Add support for Bitcoin estimates + ## [1.5.2] - 2021-03-30 ### Fixed diff --git a/Makefile b/Makefile index 1cebf71..5d1db3e 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ build: python setup.py install lint: - pre-commit + pip install -r test-requirements.txt && \ + black . test: pip install -r test-requirements.txt && \ diff --git a/README.md b/README.md index 6475a9f..29e6ab7 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,10 @@ model = "Corolla" year = 1995 patch.estimates.create_mass_estimate(distance_m=distance_m, make=make, model=model, year=year) +# Create a bitcoin estimate +transaction_value_btc_sats = 1000 # [Optional] Pass in the transaction value in satoshis +patch.estimates.create_bitcoin_estimate(transaction_value_btc_sats=transaction_value_btc_sats) + # Retrieve an estimate estimate_id = 'est_test_1234' patch.estimates.retrieve_estimate(id=estimate_id) @@ -218,45 +222,60 @@ patch.preferences.retrieve_preferences(page=page) ## Development +### Pre-commit + +This project uses pre-commit to automatically lint code on commit. Set-up lint commit the first time around using +``` +pre-commit install +``` + +### Linter + +This project uses black for code formatting. To run the automatic formatting, run: + +```bash +make lint +``` + ### Running tests Set up the required environment variable: -``` -$ export SANDBOX_API_KEY= +```bash +export SANDBOX_API_KEY= ``` Run tests: -``` -$ make test +```bash +make test ``` To run an individual test: -``` -$ python -m unittest +```bash +python -m unittest test/xxx_test.py ``` ### Testing the built package locally To build the library locally, run: -``` -$ make build +```bash +make build ``` In another directory, create a file called `patch.py` and install the local package in this directory: -``` -$ touch patch.py -$ pip install ../patch-python +```bash +touch patch.py +pip install ../patch-python ``` Set up the required environment variable: -``` -$ export SANDBOX_API_KEY= +```bash +export SANDBOX_API_KEY= ``` To test the package locally, create a python file in a sibling directory and add the following: diff --git a/patch_api/__init__.py b/patch_api/__init__.py index ff02840..7c132ae 100644 --- a/patch_api/__init__.py +++ b/patch_api/__init__.py @@ -15,7 +15,7 @@ from __future__ import absolute_import -__version__ = "1.5.2" +__version__ = "1.6.0" # import ApiClient from patch_api.api_client import ApiClient diff --git a/patch_api/api/estimates_api.py b/patch_api/api/estimates_api.py index cad0c13..e4b6070 100644 --- a/patch_api/api/estimates_api.py +++ b/patch_api/api/estimates_api.py @@ -40,11 +40,158 @@ class EstimatesApi(object): "model", "make", "year", + "transaction_value_btc_sats", + "timestamp", ] def __init__(self, api_client=None): self.api_client = api_client + def create_bitcoin_estimate( + self, create_bitcoin_estimate_request={}, **kwargs + ): # noqa: E501 + """Create a bitcoin estimate given a timestamp and transaction value # noqa: E501 + + Creates a bitcoin estimate for the amount of CO2 to be compensated. An order in the `draft` state may be created based on the parameters, linked to the estimate. # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + >>> thread = api.create_bitcoin_estimate(create_bitcoin_estimate_request, async_req=True) + >>> result = thread.get() + + :param async_req bool: execute request asynchronously + :param CreateBitcoinEstimateRequest create_bitcoin_estimate_request: (required) + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: EstimateResponse + If the method is called asynchronously, + returns the request thread. + """ + kwargs["_return_http_data_only"] = True + return self.create_bitcoin_estimate_with_http_info( + create_bitcoin_estimate_request, **kwargs + ) # noqa: E501 + + def create_bitcoin_estimate_with_http_info( + self, create_bitcoin_estimate_request, **kwargs + ): # noqa: E501 + """Create a bitcoin estimate given a timestamp and transaction value # noqa: E501 + + Creates a bitcoin estimate for the amount of CO2 to be compensated. An order in the `draft` state may be created based on the parameters, linked to the estimate. # noqa: E501 + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async_req=True + >>> thread = api.create_bitcoin_estimate_with_http_info(create_bitcoin_estimate_request, async_req=True) + >>> result = thread.get() + + :param async_req bool: execute request asynchronously + :param CreateBitcoinEstimateRequest create_bitcoin_estimate_request: (required) + :param _return_http_data_only: response data without head status code + and headers + :param _preload_content: if False, the urllib3.HTTPResponse object will + be returned without reading/decoding response + data. Default is True. + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :return: tuple(EstimateResponse, status_code(int), headers(HTTPHeaderDict)) + If the method is called asynchronously, + returns the request thread. + """ + + local_var_params = locals() + + all_params = ["create_bitcoin_estimate_request"] # noqa: E501 + all_params.append("async_req") + all_params.append("_return_http_data_only") + all_params.append("_preload_content") + all_params.append("_request_timeout") + all_params.append("mass_g") + all_params.append("total_price_cents_usd") + all_params.append("project_id") + all_params.append("metadata") + all_params.append("distance_m") + all_params.append("transportation_method") + all_params.append("package_mass_g") + all_params.append("create_order") + all_params.append("make") + all_params.append("model") + all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") + + for key, val in six.iteritems(local_var_params["kwargs"]): + if key not in all_params: + raise ApiTypeError( + "Got an unexpected keyword argument '%s'" + " to method create_bitcoin_estimate" % key + ) + local_var_params[key] = val + del local_var_params["kwargs"] + # verify the required parameter 'create_bitcoin_estimate_request' is set + if ( + "create_bitcoin_estimate_request" not in local_var_params + or local_var_params["create_bitcoin_estimate_request"] is None + ): + raise ApiValueError( + "Missing the required parameter `create_bitcoin_estimate_request` when calling `create_bitcoin_estimate`" + ) # noqa: E501 + + collection_formats = {} + + path_params = {} + + query_params = [] + for key in kwargs: + query_params.append([key, kwargs.get(key)]) + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + if "create_bitcoin_estimate_request" in local_var_params: + body_params = local_var_params["create_bitcoin_estimate_request"] + # HTTP header `Accept` + header_params["Accept"] = self.api_client.select_header_accept( + ["application/json"] + ) # noqa: E501 + + # HTTP header `Content-Type` + header_params[ + "Content-Type" + ] = self.api_client.select_header_content_type( # noqa: E501 + ["application/json"] + ) # noqa: E501 + + # Authentication setting + auth_settings = ["bearer_auth"] # noqa: E501 + + return self.api_client.call_api( + "/v1/estimates/crypto/btc", + "POST", + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type="EstimateResponse", # noqa: E501 + auth_settings=auth_settings, + async_req=local_var_params.get("async_req"), + _return_http_data_only=local_var_params.get( + "_return_http_data_only" + ), # noqa: E501 + _preload_content=local_var_params.get("_preload_content", True), + _request_timeout=local_var_params.get("_request_timeout"), + collection_formats=collection_formats, + ) + def create_flight_estimate( self, create_flight_estimate_request={}, **kwargs ): # noqa: E501 @@ -119,6 +266,8 @@ def create_flight_estimate_with_http_info( all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -262,6 +411,8 @@ def create_mass_estimate_with_http_info( all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -405,6 +556,8 @@ def create_shipping_estimate_with_http_info( all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -548,6 +701,8 @@ def create_vehicle_estimate_with_http_info( all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -685,6 +840,8 @@ def retrieve_estimate_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -812,6 +969,8 @@ def retrieve_estimates_with_http_info(self, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: diff --git a/patch_api/api/orders_api.py b/patch_api/api/orders_api.py index 96175f1..995e1a1 100644 --- a/patch_api/api/orders_api.py +++ b/patch_api/api/orders_api.py @@ -40,6 +40,8 @@ class OrdersApi(object): "model", "make", "year", + "transaction_value_btc_sats", + "timestamp", ] def __init__(self, api_client=None): @@ -48,7 +50,7 @@ def __init__(self, api_client=None): def cancel_order(self, id={}, **kwargs): # noqa: E501 """Cancel an order # noqa: E501 - Cancelling an order removes the associated offset allocation from an order. You will not be charged for cancelled orders. Only orders in the `draft` state can be cancelled. # noqa: E501 + Cancelling an order removes the associated offset allocation from an order. You will not be charged for cancelled orders. Only orders in the `draft` or `placed` state can be cancelled. # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True >>> thread = api.cancel_order(id, async_req=True) @@ -73,7 +75,7 @@ def cancel_order(self, id={}, **kwargs): # noqa: E501 def cancel_order_with_http_info(self, id, **kwargs): # noqa: E501 """Cancel an order # noqa: E501 - Cancelling an order removes the associated offset allocation from an order. You will not be charged for cancelled orders. Only orders in the `draft` state can be cancelled. # noqa: E501 + Cancelling an order removes the associated offset allocation from an order. You will not be charged for cancelled orders. Only orders in the `draft` or `placed` state can be cancelled. # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True >>> thread = api.cancel_order_with_http_info(id, async_req=True) @@ -113,6 +115,8 @@ def cancel_order_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -242,6 +246,8 @@ def create_order_with_http_info(self, create_order_request, **kwargs): # noqa: all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -379,6 +385,8 @@ def place_order_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -506,6 +514,8 @@ def retrieve_order_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -633,6 +643,8 @@ def retrieve_orders_with_http_info(self, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: diff --git a/patch_api/api/preferences_api.py b/patch_api/api/preferences_api.py index 188fa57..bcac4e0 100644 --- a/patch_api/api/preferences_api.py +++ b/patch_api/api/preferences_api.py @@ -40,6 +40,8 @@ class PreferencesApi(object): "model", "make", "year", + "transaction_value_btc_sats", + "timestamp", ] def __init__(self, api_client=None): @@ -117,6 +119,8 @@ def create_preference_with_http_info( all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -254,6 +258,8 @@ def delete_preference_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -381,6 +387,8 @@ def retrieve_preference_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -508,6 +516,8 @@ def retrieve_preferences_with_http_info(self, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: diff --git a/patch_api/api/projects_api.py b/patch_api/api/projects_api.py index 9111de5..4374966 100644 --- a/patch_api/api/projects_api.py +++ b/patch_api/api/projects_api.py @@ -40,6 +40,8 @@ class ProjectsApi(object): "model", "make", "year", + "transaction_value_btc_sats", + "timestamp", ] def __init__(self, api_client=None): @@ -113,6 +115,8 @@ def retrieve_project_with_http_info(self, id, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: @@ -246,6 +250,8 @@ def retrieve_projects_with_http_info(self, **kwargs): # noqa: E501 all_params.append("make") all_params.append("model") all_params.append("year") + all_params.append("transaction_value_btc_sats") + all_params.append("timestamp") for key, val in six.iteritems(local_var_params["kwargs"]): if key not in all_params: diff --git a/patch_api/api_client.py b/patch_api/api_client.py index 12c37cd..2d57a77 100644 --- a/patch_api/api_client.py +++ b/patch_api/api_client.py @@ -91,7 +91,7 @@ def __init__( self.default_headers[header_name] = header_value self.cookie = cookie # Set default User-Agent. - self.user_agent = "OpenAPI-Generator/1.5.2/python" + self.user_agent = "OpenAPI-Generator/1.6.0/python" def __del__(self): if self._pool: diff --git a/patch_api/configuration.py b/patch_api/configuration.py index 82120b3..97c5995 100644 --- a/patch_api/configuration.py +++ b/patch_api/configuration.py @@ -341,7 +341,7 @@ def to_debug_report(self): "OS: {env}\n" "Python Version: {pyversion}\n" "Version of the API: v1\n" - "SDK Package Version: 1.5.2".format(env=sys.platform, pyversion=sys.version) + "SDK Package Version: 1.6.0".format(env=sys.platform, pyversion=sys.version) ) def get_host_settings(self): diff --git a/patch_api/models/order.py b/patch_api/models/order.py index 8274f28..c9559d9 100644 --- a/patch_api/models/order.py +++ b/patch_api/models/order.py @@ -42,6 +42,7 @@ class Order(object): "price_cents_usd": "str", "patch_fee_cents_usd": "str", "allocations": "list[Allocation]", + "registry_url": "str", "metadata": "object", } @@ -54,6 +55,7 @@ class Order(object): "price_cents_usd": "price_cents_usd", "patch_fee_cents_usd": "patch_fee_cents_usd", "allocations": "allocations", + "registry_url": "registry_url", "metadata": "metadata", } @@ -67,6 +69,7 @@ def __init__( price_cents_usd=None, patch_fee_cents_usd=None, allocations=None, + registry_url=None, metadata=None, local_vars_configuration=None, ): # noqa: E501 @@ -83,6 +86,7 @@ def __init__( self._price_cents_usd = None self._patch_fee_cents_usd = None self._allocations = None + self._registry_url = None self._metadata = None self.discriminator = None @@ -94,6 +98,8 @@ def __init__( self.price_cents_usd = price_cents_usd self.patch_fee_cents_usd = patch_fee_cents_usd self.allocations = allocations + if registry_url is not None: + self.registry_url = registry_url self.metadata = metadata @property @@ -223,7 +229,13 @@ def state(self, state): raise ValueError( "Invalid value for `state`, must not be `None`" ) # noqa: E501 - allowed_values = ["draft", "placed", "complete", "cancelled"] # noqa: E501 + allowed_values = [ + "draft", + "placed", + "processing", + "complete", + "cancelled", + ] # noqa: E501 if ( self.local_vars_configuration.client_side_validation and state not in allowed_values @@ -351,6 +363,29 @@ def allocations(self, allocations): self._allocations = allocations + @property + def registry_url(self): + """Gets the registry_url of this Order. # noqa: E501 + + The url of this order in the public registry. # noqa: E501 + + :return: The registry_url of this Order. # noqa: E501 + :rtype: str + """ + return self._registry_url + + @registry_url.setter + def registry_url(self, registry_url): + """Sets the registry_url of this Order. + + The url of this order in the public registry. # noqa: E501 + + :param registry_url: The registry_url of this Order. # noqa: E501 + :type: str + """ + + self._registry_url = registry_url + @property def metadata(self): """Gets the metadata of this Order. # noqa: E501 diff --git a/setup.py b/setup.py index ac958ec..7993f67 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ from setuptools import setup, find_packages # noqa: H301 NAME = "patch-api" -VERSION = "1.5.2" +VERSION = "1.6.0" # To install the library, run the following # # python setup.py install diff --git a/test-requirements.txt b/test-requirements.txt index 4ed3991..86f8e70 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,3 +1,4 @@ +black>=21.6b0 pytest~=4.6.7 # needed for python 2.7+3.4 pytest-cov>=2.8.1 pytest-randomly==1.2.3 # needed for python 2.7+3.4 diff --git a/test/test_estimates_api.py b/test/test_estimates_api.py index d987032..65af346 100644 --- a/test/test_estimates_api.py +++ b/test/test_estimates_api.py @@ -115,6 +115,33 @@ def test_create_and_retrieve_vehicle_estimate_best_match(self): retrieved_estimate = self.api.retrieve_estimate(id=estimate.data.id) self.assertTrue(retrieved_estimate) + def test_create_bitcoin_estimate_no_params(self): + """Test case for create_bitcoin_estimate + + Create an estimate based on a transaction amount # noqa: E501 + """ + + estimate = self.api.create_bitcoin_estimate() + self.assertEqual(estimate.data.type, "bitcoin") + self.assertGreater( + estimate.data.mass_g, 200 + ) # not setting an exact value since this is changing daily + + def test_create_bitcoin_estimate_transaction_value(self): + """Test case for create_bitcoin_estimate + + Create an estimate based on a transaction amount # noqa: E501 + """ + transaction_value_btc_sats = 100000 + + estimate = self.api.create_bitcoin_estimate( + transaction_value_btc_sats=transaction_value_btc_sats + ) + self.assertEqual(estimate.data.type, "bitcoin") + self.assertGreater( + estimate.data.mass_g, 200 + ) # not setting an exact value since this is changing daily + if __name__ == "__main__": unittest.main() diff --git a/test/test_projects_api.py b/test/test_projects_api.py index 7f58ff6..a01aa6e 100644 --- a/test/test_projects_api.py +++ b/test/test_projects_api.py @@ -55,11 +55,11 @@ def test_retrieve_projects(self): self.assertGreater(project.average_price_per_tonne_cents_usd, 0) self.assertGreater(project.remaining_mass_g, 0) self.assertEqual(project.standard, None) - self.assertEqual(project.name, "Carbo Culture Biochar") + self.assertEqual(project.name, "Patch's Biomass Test Offset Project") self.assertTrue(project.description) self.assertEqual(project.country, "US") self.assertEqual(project.type, "biomass") - self.assertEqual(project.developer, "Carbo Culture") + self.assertEqual(project.developer, "Patch Sandbox Supplier Test") self.assertTrue(isinstance(project.photos, list)) def test_retrieve_biomass_projects(self):