diff --git a/.noserc b/.noserc deleted file mode 100644 index 6c9611e..0000000 --- a/.noserc +++ /dev/null @@ -1,5 +0,0 @@ -[nosetests] -with-coverage=true -cover-package=fixerio -cover-html=true -cover-html-dir=htmlcov \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0d92fd5..dc54b36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,17 @@ Release History --------------- +1.1.0 (2023-??-??) +~~~~~~~~~~~~~~~~~~ +- Update to the `new Fixer API endpoint on API Layer `_. +- Re-add support for changing base currency. This option is once again supported by Free Plan. +- Always use TLS encrypted endpoint, since this is once again supported by Free Plan. +- Add new API function, available_currencies(), for listing supported currency symbols. +- Replace unit tests based on nosetest with unit tests based on pytest (nose is not supported + by modern Python 3 versions). +- Remove support for Python 2. +- Add type hints for all methods. + 1.0.0-alpha (2018-06-13) ~~~~~~~~~~~~~~~~~~~~~~~~ - Update to the `new Fixer endpoint `_. diff --git a/Makefile b/Makefile index 009d7d5..337ed29 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ clean-test: .PHONY: coverage coverage: - nosetests --config=.noserc + pytest --cov=fixerio --cov-report html:cov_html --cov-report term-missing tests/ .PHONY: freeze freeze: @@ -70,7 +70,7 @@ lint: .PHONY: test test: - nosetests + pytest tests/ .PHONY: register register: diff --git a/README.rst b/README.rst index 0744afa..785c27f 100644 --- a/README.rst +++ b/README.rst @@ -1,13 +1,13 @@ -A Python client for `Fixer.io`_ -=============================== +A Python client for `Fixer API`_ +================================ |Build Status| |Coverage Status| |Supports Wheel format| |Latest PyPI version| |Documentation Status| |Requirements Status| -`Fixer.io`_ is a free JSON API for current and historical foreign -exchange rates published by the European Central Bank. +`Fixer API`_ (formerly known as Fixer.io) is a free JSON API for current and +historical foreign exchange rates published by the European Central Bank. -The rates are updated daily around 3PM CET. +The rates are updated daily around 3 pm CET. Installation ------------ @@ -40,39 +40,39 @@ Get the latest foreign exchange reference rates in JSON format. >>> fxrio = Fixerio(access_key='YOUR ACCESS KEY') >>> fxrio.latest() ''' - {u'base': u'EUR', - u'date': u'2016-05-27', - u'rates': {u'AUD': 1.5483, - u'BGN': 1.9558, - u'BRL': 4.031, - u'CAD': 1.456, - u'CHF': 1.1068, - u'CNY': 7.3281, - u'CZK': 27.028, - u'DKK': 7.4367, - u'GBP': 0.76245, - u'HKD': 8.6735, - u'HRK': 7.4905, - u'HUF': 314.21, - u'IDR': 15157.25, - u'ILS': 4.2938, - u'INR': 74.867, - u'JPY': 122.46, - u'KRW': 1316.98, - u'MXN': 20.6611, - u'MYR': 4.5554, - u'NOK': 9.282, - u'NZD': 1.6586, - u'PHP': 52.096, - u'PLN': 4.3912, - u'RON': 4.5034, - u'RUB': 73.7516, - u'SEK': 9.2673, - u'SGD': 1.536, - u'THB': 39.851, - u'TRY': 3.2928, - u'USD': 1.1168, - u'ZAR': 17.4504}} + {'base': 'EUR', + 'date': '2016-05-27', + 'rates': {'AUD': 1.5483, + 'BGN': 1.9558, + 'BRL': 4.031, + 'CAD': 1.456, + 'CHF': 1.1068, + 'CNY': 7.3281, + 'CZK': 27.028, + 'DKK': 7.4367, + 'GBP': 0.76245, + 'HKD': 8.6735, + 'HRK': 7.4905, + 'HUF': 314.21, + 'IDR': 15157.25, + 'ILS': 4.2938, + 'INR': 74.867, + 'JPY': 122.46, + 'KRW': 1316.98, + 'MXN': 20.6611, + 'MYR': 4.5554, + 'NOK': 9.282, + 'NZD': 1.6586, + 'PHP': 52.096, + 'PLN': 4.3912, + 'RON': 4.5034, + 'RUB': 73.7516, + 'SEK': 9.2673, + 'SGD': 1.536, + 'THB': 39.851, + 'TRY': 3.2928, + 'USD': 1.1168, + 'ZAR': 17.4504}} ''' Get historical rates for any day since 1999. @@ -86,39 +86,39 @@ Get historical rates for any day since 1999. >>> fxrio = Fixerio(access_key='YOUR ACCESS KEY') >>> fxrio.historical_rates(today) ''' - {u'base': u'EUR', - u'date': u'2016-05-27', - u'rates': {u'AUD': 1.5483, - u'BGN': 1.9558, - u'BRL': 4.031, - u'CAD': 1.456, - u'CHF': 1.1068, - u'CNY': 7.3281, - u'CZK': 27.028, - u'DKK': 7.4367, - u'GBP': 0.76245, - u'HKD': 8.6735, - u'HRK': 7.4905, - u'HUF': 314.21, - u'IDR': 15157.25, - u'ILS': 4.2938, - u'INR': 74.867, - u'JPY': 122.46, - u'KRW': 1316.98, - u'MXN': 20.6611, - u'MYR': 4.5554, - u'NOK': 9.282, - u'NZD': 1.6586, - u'PHP': 52.096, - u'PLN': 4.3912, - u'RON': 4.5034, - u'RUB': 73.7516, - u'SEK': 9.2673, - u'SGD': 1.536, - u'THB': 39.851, - u'TRY': 3.2928, - u'USD': 1.1168, - u'ZAR': 17.4504}} + {'base': 'EUR', + 'date': '2016-05-27', + 'rates': {'AUD': 1.5483, + 'BGN': 1.9558, + 'BRL': 4.031, + 'CAD': 1.456, + 'CHF': 1.1068, + 'CNY': 7.3281, + 'CZK': 27.028, + 'DKK': 7.4367, + 'GBP': 0.76245, + 'HKD': 8.6735, + 'HRK': 7.4905, + 'HUF': 314.21, + 'IDR': 15157.25, + 'ILS': 4.2938, + 'INR': 74.867, + 'JPY': 122.46, + 'KRW': 1316.98, + 'MXN': 20.6611, + 'MYR': 4.5554, + 'NOK': 9.282, + 'NZD': 1.6586, + 'PHP': 52.096, + 'PLN': 4.3912, + 'RON': 4.5034, + 'RUB': 73.7516, + 'SEK': 9.2673, + 'SGD': 1.536, + 'THB': 39.851, + 'TRY': 3.2928, + 'USD': 1.1168, + 'ZAR': 17.4504}} ''' Request specific exchange rates by setting the ``symbols`` parameter. @@ -130,9 +130,9 @@ Request specific exchange rates by setting the ``symbols`` parameter. >>> fxrio = Fixerio(access_key='YOUR ACCESS KEY', symbols=['USD', 'GBP']) >>> fxrio.latest() ''' - {u'base': u'EUR', - u'date': u'2016-05-27', - u'rates': {u'GBP': 0.76245, u'USD': 1.1168}} + {'base': 'EUR', + 'date': '2016-05-27', + 'rates': {'GBP': 0.76245, 'USD': 1.1168}} ''' .. code:: python @@ -142,15 +142,15 @@ Request specific exchange rates by setting the ``symbols`` parameter. >>> fxrio = Fixerio(access_key='YOUR ACCESS KEY') >>> fxrio.latest(symbols=['USD', 'GBP']) ''' - {u'base': u'EUR', - u'date': u'2016-05-27', - u'rates': {u'GBP': 0.76245, u'USD': 1.1168}} + {'base': 'EUR', + 'date': '2016-05-27', + 'rates': {'GBP': 0.76245, 'USD': 1.1168}} ''' All exceptions that ``fixerio`` explicitly raises are ``fixerio.exceptions.FixerioException``. -.. _Fixer.io: http://fixer.io/ +.. _Fixer API: https://apilayer.com/marketplace/fixer-api .. |Build Status| image:: https://travis-ci.org/amatellanes/fixerio.svg?branch=master :target: https://travis-ci.org/amatellanes/fixerio diff --git a/docs/conf.py b/docs/conf.py index 963d3a1..3b7e676 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -75,7 +75,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -426,4 +426,4 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} diff --git a/fixerio/client.py b/fixerio/client.py index 163360a..c3a4ab9 100644 --- a/fixerio/client.py +++ b/fixerio/client.py @@ -1,64 +1,88 @@ -from __future__ import unicode_literals - import datetime -try: - from urllib.parse import urljoin -except ImportError: # For Python 2 - from urlparse import urljoin # noqa +from urllib.parse import urlencode import requests from .exceptions import FixerioException -BASE_URL = 'http://data.fixer.io/api/' +from typing import Dict, Iterable, Optional, Union -LATEST_PATH = 'latest' +BASE_URL = 'https://api.apilayer.com/fixer/' class Fixerio(object): - """ A client for Fixer.io. """ + """ A client for Fixer API, available on API Layer. """ - def __init__(self, access_key, symbols=None): + def __init__(self, + access_key: str, + base: Optional[str] = None, + symbols: Union[None, str, Iterable[str]] = None): """ :param access_key: your API Key. - :type access_key: str or unicode :param symbols: currency symbols to request specific exchange rates. - :type symbols: list or tuple """ - self.access_key = access_key - self.symbols = symbols + self._access_key = access_key + self._base = base or 'EUR' + self._symbols = symbols + + def _create_headers(self) -> Dict[str, str]: + """ Creates a header with the API key required for accessing Fixer.io + on API Layer. + """ + headers = { + "apikey": self._access_key + } + return headers - def _create_payload(self, symbols): + def _create_payload(self, symbols: Union[None, str, Iterable[str]]) -> str: """ Creates a payload with no none values. :param symbols: currency symbols to request specific exchange rates. - :type symbols: list or tuple :return: a payload. - :rtype: dict """ - payload = {'access_key': self.access_key} + payload = {} + if self._base is not None: + payload['base'] = self._base if symbols is not None: - payload['symbols'] = ','.join(symbols) + if isinstance(symbols, str): + payload['symbols'] = symbols + else: + payload['symbols'] = ','.join(symbols) + + payload_str = urlencode(payload, safe=',') + return payload_str - return payload + def available_currencies(self) -> dict: + """ Get all currency symbols that can be used as base or target. + + :return: a dictinary where the member `symbols` contains a mapping + from currency symbols to the full currency name. + :raises FixerioException: if any error making a request. + """ + try: + url = BASE_URL + "symbols" + response = requests.get(url, headers=self._create_headers()) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as ex: + raise FixerioException(str(ex)) - def latest(self, symbols=None): + def latest(self, symbols: Union[None, str, Iterable[str]] = None) -> dict: """ Get the latest foreign exchange reference rates. - :param symbols: currency symbols to request specific exchange rates. - :type symbols: list or tuple + :param symbols: currency symbol(s) to request specific exchange rates. :return: the latest foreign exchange reference rates. - :rtype: dict :raises FixerioException: if any error making a request. """ try: - symbols = symbols or self.symbols + headers = self._create_headers() + symbols = symbols or self._symbols payload = self._create_payload(symbols) - url = BASE_URL + LATEST_PATH + url = BASE_URL + "latest" - response = requests.get(url, params=payload) + response = requests.get(url, headers=headers, params=payload) response.raise_for_status() @@ -66,16 +90,16 @@ def latest(self, symbols=None): except requests.exceptions.RequestException as ex: raise FixerioException(str(ex)) - def historical_rates(self, date, symbols=None): + def historical_rates(self, + date: Union[datetime.date, str], + symbols: Optional[Union[str, Iterable[str]]] + = None) -> dict: """ - Get historical rates for any day since `date`. + Get rates for a historical date. - :param date: a date - :type date: date or str - :param symbols: currency symbols to request specific exchange rates. - :type symbols: list or tuple - :return: the historical rates for any day since `date`. - :rtype: dict + :param date: the date to get rates for. + :param symbols: currency symbol(s) to request specific exchange rates. + :return: the historical rates for a specific date. :raises FixerioException: if any error making a request. """ try: @@ -83,12 +107,13 @@ def historical_rates(self, date, symbols=None): # Convert date to ISO 8601 format. date = date.isoformat() - symbols = symbols or self.symbols + symbols = symbols or self._symbols + headers = self._create_headers() payload = self._create_payload(symbols) url = BASE_URL + date - response = requests.get(url, params=payload) + response = requests.get(url, headers=headers, params=payload) response.raise_for_status() diff --git a/requirements.txt b/requirements.txt index 6edc8e9..bb2259e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,39 @@ -alabaster==0.7.10 -Babel==2.6.0 -certifi==2018.4.16 -chardet==3.0.4 -check-manifest==0.37 -cookies==2.2.1 -coverage==4.5.1 -docutils==0.14 -flake8==3.5.0 -idna==2.6 -imagesize==1.0.0 -Jinja2==2.10 -MarkupSafe==1.0 -mccabe==0.6.1 -nose==1.3.7 -packaging==17.1 -pluggy==0.6.0 -py==1.5.3 -pycodestyle==2.3.1 -pyflakes==1.6.0 -Pygments==2.2.0 -pyparsing==2.2.0 -pytz==2018.4 -requests==2.18.4 -responses==0.9.0 -six==1.11.0 -snowballstemmer==1.2.1 -Sphinx==1.7.5 -sphinxcontrib-websupport==1.1.0 -tox==3.0.0 -urllib3==1.22 -virtualenv==16.0.0 +alabaster>=0.7.13 +Babel>=2.12.1 +certifi>=2023.5.7 +charset-normalizer>=3.1.0 +coverage>=7.2.5 +docutils>=0.19 +exceptiongroup>=1.1.1 +flake8>=5.0.4 +idna>=3.4 +imagesize>=1.4.1 +importlib-metadata>=4.2.0 +iniconfig>=2.0.0 +Jinja2>=3.1.2 +MarkupSafe>=2.1.2 +mccabe>=0.7.0 +packaging>=23.1 +pluggy>=1.0.0 +pycodestyle>=2.9.1 +pyflakes>=2.5.0 +Pygments>=2.15.1 +pytest>=7.3.1 +pytest-cov>=4.0.0 +pytz>=2023.3 +PyYAML>=6.0 +requests>=2.30.0 +responses>=0.23.1 +snowballstemmer>=2.2.0 +Sphinx>=5.3.0 +sphinxcontrib-applehelp>=1.0.2 +sphinxcontrib-devhelp>=1.0.2 +sphinxcontrib-htmlhelp>=2.0.0 +sphinxcontrib-jsmath>=1.0.1 +sphinxcontrib-qthelp>=1.0.3 +sphinxcontrib-serializinghtml>=1.1.5 +tomli>=2.0.1 +types-PyYAML>=6.0.12.9 +typing_extensions>=4.5.0 +urllib3>=2.0.2 +zipp>=3.15.0 diff --git a/setup.py b/setup.py index 8065ccd..e5c8fe9 100644 --- a/setup.py +++ b/setup.py @@ -39,13 +39,12 @@ 'Natural Language :: English', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: PyPy' ], ) diff --git a/tests/responses/available_currencies_responses.yaml b/tests/responses/available_currencies_responses.yaml new file mode 100644 index 0000000..6965ddd --- /dev/null +++ b/tests/responses/available_currencies_responses.yaml @@ -0,0 +1,96 @@ +responses: +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"symbols\": {\n \"AED\": \"United\ + \ Arab Emirates Dirham\",\n \"AFN\": \"Afghan Afghani\",\n \"\ + ALL\": \"Albanian Lek\",\n \"AMD\": \"Armenian Dram\",\n \"ANG\"\ + : \"Netherlands Antillean Guilder\",\n \"AOA\": \"Angolan Kwanza\",\n\ + \ \"ARS\": \"Argentine Peso\",\n \"AUD\": \"Australian Dollar\"\ + ,\n \"AWG\": \"Aruban Florin\",\n \"AZN\": \"Azerbaijani Manat\"\ + ,\n \"BAM\": \"Bosnia-Herzegovina Convertible Mark\",\n \"BBD\"\ + : \"Barbadian Dollar\",\n \"BDT\": \"Bangladeshi Taka\",\n \"\ + BGN\": \"Bulgarian Lev\",\n \"BHD\": \"Bahraini Dinar\",\n \"\ + BIF\": \"Burundian Franc\",\n \"BMD\": \"Bermudan Dollar\",\n \ + \ \"BND\": \"Brunei Dollar\",\n \"BOB\": \"Bolivian Boliviano\",\n \ + \ \"BRL\": \"Brazilian Real\",\n \"BSD\": \"Bahamian Dollar\",\n\ + \ \"BTC\": \"Bitcoin\",\n \"BTN\": \"Bhutanese Ngultrum\",\n \ + \ \"BWP\": \"Botswanan Pula\",\n \"BYN\": \"New Belarusian Ruble\"\ + ,\n \"BYR\": \"Belarusian Ruble\",\n \"BZD\": \"Belize Dollar\"\ + ,\n \"CAD\": \"Canadian Dollar\",\n \"CDF\": \"Congolese Franc\"\ + ,\n \"CHF\": \"Swiss Franc\",\n \"CLF\": \"Chilean Unit of Account\ + \ (UF)\",\n \"CLP\": \"Chilean Peso\",\n \"CNY\": \"Chinese Yuan\"\ + ,\n \"COP\": \"Colombian Peso\",\n \"CRC\": \"Costa Rican Col\\\ + u00f3n\",\n \"CUC\": \"Cuban Convertible Peso\",\n \"CUP\": \"\ + Cuban Peso\",\n \"CVE\": \"Cape Verdean Escudo\",\n \"CZK\": \"\ + Czech Republic Koruna\",\n \"DJF\": \"Djiboutian Franc\",\n \"\ + DKK\": \"Danish Krone\",\n \"DOP\": \"Dominican Peso\",\n \"DZD\"\ + : \"Algerian Dinar\",\n \"EGP\": \"Egyptian Pound\",\n \"ERN\"\ + : \"Eritrean Nakfa\",\n \"ETB\": \"Ethiopian Birr\",\n \"EUR\"\ + : \"Euro\",\n \"FJD\": \"Fijian Dollar\",\n \"FKP\": \"Falkland\ + \ Islands Pound\",\n \"GBP\": \"British Pound Sterling\",\n \"\ + GEL\": \"Georgian Lari\",\n \"GGP\": \"Guernsey Pound\",\n \"\ + GHS\": \"Ghanaian Cedi\",\n \"GIP\": \"Gibraltar Pound\",\n \"\ + GMD\": \"Gambian Dalasi\",\n \"GNF\": \"Guinean Franc\",\n \"\ + GTQ\": \"Guatemalan Quetzal\",\n \"GYD\": \"Guyanaese Dollar\",\n \ + \ \"HKD\": \"Hong Kong Dollar\",\n \"HNL\": \"Honduran Lempira\"\ + ,\n \"HRK\": \"Croatian Kuna\",\n \"HTG\": \"Haitian Gourde\"\ + ,\n \"HUF\": \"Hungarian Forint\",\n \"IDR\": \"Indonesian Rupiah\"\ + ,\n \"ILS\": \"Israeli New Sheqel\",\n \"IMP\": \"Manx pound\"\ + ,\n \"INR\": \"Indian Rupee\",\n \"IQD\": \"Iraqi Dinar\",\n \ + \ \"IRR\": \"Iranian Rial\",\n \"ISK\": \"Icelandic Kr\\u00f3na\"\ + ,\n \"JEP\": \"Jersey Pound\",\n \"JMD\": \"Jamaican Dollar\"\ + ,\n \"JOD\": \"Jordanian Dinar\",\n \"JPY\": \"Japanese Yen\"\ + ,\n \"KES\": \"Kenyan Shilling\",\n \"KGS\": \"Kyrgystani Som\"\ + ,\n \"KHR\": \"Cambodian Riel\",\n \"KMF\": \"Comorian Franc\"\ + ,\n \"KPW\": \"North Korean Won\",\n \"KRW\": \"South Korean Won\"\ + ,\n \"KWD\": \"Kuwaiti Dinar\",\n \"KYD\": \"Cayman Islands Dollar\"\ + ,\n \"KZT\": \"Kazakhstani Tenge\",\n \"LAK\": \"Laotian Kip\"\ + ,\n \"LBP\": \"Lebanese Pound\",\n \"LKR\": \"Sri Lankan Rupee\"\ + ,\n \"LRD\": \"Liberian Dollar\",\n \"LSL\": \"Lesotho Loti\"\ + ,\n \"LTL\": \"Lithuanian Litas\",\n \"LVL\": \"Latvian Lats\"\ + ,\n \"LYD\": \"Libyan Dinar\",\n \"MAD\": \"Moroccan Dirham\"\ + ,\n \"MDL\": \"Moldovan Leu\",\n \"MGA\": \"Malagasy Ariary\"\ + ,\n \"MKD\": \"Macedonian Denar\",\n \"MMK\": \"Myanma Kyat\"\ + ,\n \"MNT\": \"Mongolian Tugrik\",\n \"MOP\": \"Macanese Pataca\"\ + ,\n \"MRO\": \"Mauritanian Ouguiya\",\n \"MUR\": \"Mauritian Rupee\"\ + ,\n \"MVR\": \"Maldivian Rufiyaa\",\n \"MWK\": \"Malawian Kwacha\"\ + ,\n \"MXN\": \"Mexican Peso\",\n \"MYR\": \"Malaysian Ringgit\"\ + ,\n \"MZN\": \"Mozambican Metical\",\n \"NAD\": \"Namibian Dollar\"\ + ,\n \"NGN\": \"Nigerian Naira\",\n \"NIO\": \"Nicaraguan C\\u00f3rdoba\"\ + ,\n \"NOK\": \"Norwegian Krone\",\n \"NPR\": \"Nepalese Rupee\"\ + ,\n \"NZD\": \"New Zealand Dollar\",\n \"OMR\": \"Omani Rial\"\ + ,\n \"PAB\": \"Panamanian Balboa\",\n \"PEN\": \"Peruvian Nuevo\ + \ Sol\",\n \"PGK\": \"Papua New Guinean Kina\",\n \"PHP\": \"\ + Philippine Peso\",\n \"PKR\": \"Pakistani Rupee\",\n \"PLN\":\ + \ \"Polish Zloty\",\n \"PYG\": \"Paraguayan Guarani\",\n \"QAR\"\ + : \"Qatari Rial\",\n \"RON\": \"Romanian Leu\",\n \"RSD\": \"\ + Serbian Dinar\",\n \"RUB\": \"Russian Ruble\",\n \"RWF\": \"Rwandan\ + \ Franc\",\n \"SAR\": \"Saudi Riyal\",\n \"SBD\": \"Solomon Islands\ + \ Dollar\",\n \"SCR\": \"Seychellois Rupee\",\n \"SDG\": \"Sudanese\ + \ Pound\",\n \"SEK\": \"Swedish Krona\",\n \"SGD\": \"Singapore\ + \ Dollar\",\n \"SHP\": \"Saint Helena Pound\",\n \"SLE\": \"Sierra\ + \ Leonean Leone\",\n \"SLL\": \"Sierra Leonean Leone\",\n \"SOS\"\ + : \"Somali Shilling\",\n \"SRD\": \"Surinamese Dollar\",\n \"\ + STD\": \"S\\u00e3o Tom\\u00e9 and Pr\\u00edncipe Dobra\",\n \"SVC\":\ + \ \"Salvadoran Col\\u00f3n\",\n \"SYP\": \"Syrian Pound\",\n \"\ + SZL\": \"Swazi Lilangeni\",\n \"THB\": \"Thai Baht\",\n \"TJS\"\ + : \"Tajikistani Somoni\",\n \"TMT\": \"Turkmenistani Manat\",\n \ + \ \"TND\": \"Tunisian Dinar\",\n \"TOP\": \"Tongan Pa\\u02bbanga\"\ + ,\n \"TRY\": \"Turkish Lira\",\n \"TTD\": \"Trinidad and Tobago\ + \ Dollar\",\n \"TWD\": \"New Taiwan Dollar\",\n \"TZS\": \"Tanzanian\ + \ Shilling\",\n \"UAH\": \"Ukrainian Hryvnia\",\n \"UGX\": \"\ + Ugandan Shilling\",\n \"USD\": \"United States Dollar\",\n \"\ + UYU\": \"Uruguayan Peso\",\n \"UZS\": \"Uzbekistan Som\",\n \"\ + VEF\": \"Venezuelan Bol\\u00edvar Fuerte\",\n \"VES\": \"Sovereign Bolivar\"\ + ,\n \"VND\": \"Vietnamese Dong\",\n \"VUV\": \"Vanuatu Vatu\"\ + ,\n \"WST\": \"Samoan Tala\",\n \"XAF\": \"CFA Franc BEAC\",\n\ + \ \"XAG\": \"Silver (troy ounce)\",\n \"XAU\": \"Gold (troy ounce)\"\ + ,\n \"XCD\": \"East Caribbean Dollar\",\n \"XDR\": \"Special Drawing\ + \ Rights\",\n \"XOF\": \"CFA Franc BCEAO\",\n \"XPF\": \"CFP Franc\"\ + ,\n \"YER\": \"Yemeni Rial\",\n \"ZAR\": \"South African Rand\"\ + ,\n \"ZMK\": \"Zambian Kwacha (pre-2013)\",\n \"ZMW\": \"Zambian\ + \ Kwacha\",\n \"ZWL\": \"Zimbabwean Dollar\"\n }\n}\n" + content_type: text/plain + method: GET + status: 200 + url: https://api.apilayer.com/fixer/symbols diff --git a/tests/responses/historical_rates_responses.yaml b/tests/responses/historical_rates_responses.yaml new file mode 100644 index 0000000..5558059 --- /dev/null +++ b/tests/responses/historical_rates_responses.yaml @@ -0,0 +1,28 @@ +responses: +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 946943999,\n \"historical\"\ + : true,\n \"base\": \"EUR\",\n \"date\": \"2000-01-03\",\n \"rates\"\ + : {\n \"GBP\": 0.627016\n }\n}\n" + content_type: application/json + method: GET + status: 200 + url: https://api.apilayer.com/fixer/2000-01-03?base=EUR&symbols=GBP +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 946943999,\n \"historical\"\ + : true,\n \"base\": \"USD\",\n \"date\": \"2000-01-03\",\n \"rates\"\ + : {\n \"GBP\": 0.614647\n }\n}\n" + content_type: application/json + method: GET + status: 200 + url: https://api.apilayer.com/fixer/2000-01-03?base=USD&symbols=GBP +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 946943999,\n \"historical\"\ + : true,\n \"base\": \"EUR\",\n \"date\": \"2000-01-03\",\n \"rates\"\ + : {\n \"USD\": 1.020124,\n \"GBP\": 0.627016\n }\n}\n" + content_type: application/json + method: GET + status: 200 + url: https://api.apilayer.com/fixer/2000-01-03?base=EUR&symbols=USD,GBP \ No newline at end of file diff --git a/tests/responses/latest_responses.yaml b/tests/responses/latest_responses.yaml new file mode 100644 index 0000000..d374463 --- /dev/null +++ b/tests/responses/latest_responses.yaml @@ -0,0 +1,91 @@ +responses: +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 1683607143,\n \"base\"\ + : \"EUR\",\n \"date\": \"2023-05-09\",\n \"rates\": {\n \"AED\"\ + : 4.037229,\n \"AFN\": 96.533539,\n \"ALL\": 111.169635,\n \ + \ \"AMD\": 430.343643,\n \"ANG\": 1.99181,\n \"AOA\": 557.912528,\n\ + \ \"ARS\": 250.257079,\n \"AUD\": 1.620117,\n \"AWG\":\ + \ 1.97887,\n \"AZN\": 1.86856,\n \"BAM\": 1.956453,\n \"\ + BBD\": 2.231496,\n \"BDT\": 118.229463,\n \"BGN\": 1.953675,\n\ + \ \"BHD\": 0.414442,\n \"BIF\": 2302.70003,\n \"BMD\":\ + \ 1.099372,\n \"BND\": 1.463909,\n \"BOB\": 7.636549,\n \ + \ \"BRL\": 5.507745,\n \"BSD\": 1.105194,\n \"BTC\": 3.9823669e-05,\n\ + \ \"BTN\": 90.368264,\n \"BWP\": 14.580198,\n \"BYN\":\ + \ 2.789595,\n \"BYR\": 21547.690339,\n \"BZD\": 2.227694,\n \ + \ \"CAD\": 1.469437,\n \"CDF\": 2249.014453,\n \"CHF\": 0.977804,\n\ + \ \"CLF\": 0.031734,\n \"CLP\": 875.639282,\n \"CNY\":\ + \ 7.609413,\n \"COP\": 4954.31972,\n \"CRC\": 600.508635,\n \ + \ \"CUC\": 1.099372,\n \"CUP\": 29.133357,\n \"CVE\": 110.296816,\n\ + \ \"CZK\": 23.401009,\n \"DJF\": 196.770154,\n \"DKK\"\ + : 7.446981,\n \"DOP\": 60.30095,\n \"DZD\": 148.875895,\n \ + \ \"EGP\": 33.968401,\n \"ERN\": 16.490579,\n \"ETB\": 59.894609,\n\ + \ \"EUR\": 1,\n \"FJD\": 2.413616,\n \"FKP\": 0.870367,\n\ + \ \"GBP\": 0.871264,\n \"GEL\": 2.731922,\n \"GGP\": 0.870367,\n\ + \ \"GHS\": 13.041353,\n \"GIP\": 0.870367,\n \"GMD\": 65.907584,\n\ + \ \"GNF\": 9499.300353,\n \"GTQ\": 8.629881,\n \"GYD\"\ + : 233.748022,\n \"HKD\": 8.627712,\n \"HNL\": 27.157435,\n \ + \ \"HRK\": 7.493465,\n \"HTG\": 163.016637,\n \"HUF\": 372.192132,\n\ + \ \"IDR\": 16217.38541,\n \"ILS\": 3.987334,\n \"IMP\"\ + : 0.870367,\n \"INR\": 90.068794,\n \"IQD\": 1447.816186,\n \ + \ \"IRR\": 46448.46559,\n \"ISK\": 151.097363,\n \"JEP\"\ + : 0.870367,\n \"JMD\": 169.78899,\n \"JOD\": 0.779897,\n \ + \ \"JPY\": 148.40037,\n \"KES\": 149.19527,\n \"KGS\": 95.981737,\n\ + \ \"KHR\": 4555.582751,\n \"KMF\": 491.364208,\n \"KPW\"\ + : 989.447064,\n \"KRW\": 1452.358781,\n \"KWD\": 0.336969,\n \ + \ \"KYD\": 0.921028,\n \"KZT\": 490.404843,\n \"LAK\": 19265.868807,\n\ + \ \"LBP\": 16588.9144,\n \"LKR\": 352.575703,\n \"LRD\"\ + : 182.386923,\n \"LSL\": 20.239802,\n \"LTL\": 3.246159,\n \ + \ \"LVL\": 0.665,\n \"LYD\": 5.242869,\n \"MAD\": 11.020378,\n\ + \ \"MDL\": 19.653007,\n \"MGA\": 4837.724781,\n \"MKD\"\ + : 61.640575,\n \"MMK\": 2320.927474,\n \"MNT\": 3834.385531,\n\ + \ \"MOP\": 8.934085,\n \"MRO\": 392.475599,\n \"MUR\":\ + \ 49.800496,\n \"MVR\": 16.875253,\n \"MWK\": 1134.404446,\n \ + \ \"MXN\": 19.575463,\n \"MYR\": 4.882335,\n \"MZN\": 69.535055,\n\ + \ \"NAD\": 20.239154,\n \"NGN\": 506.267398,\n \"NIO\"\ + : 40.474062,\n \"NOK\": 11.557591,\n \"NPR\": 144.591235,\n \ + \ \"NZD\": 1.733187,\n \"OMR\": 0.42328,\n \"PAB\": 1.105169,\n\ + \ \"PEN\": 4.095167,\n \"PGK\": 3.895353,\n \"PHP\": 61.150913,\n\ + \ \"PKR\": 309.841267,\n \"PLN\": 4.560997,\n \"PYG\":\ + \ 7902.81757,\n \"QAR\": 4.002265,\n \"RON\": 4.922219,\n \ + \ \"RSD\": 117.26451,\n \"RUB\": 85.448687,\n \"RWF\": 1232.339363,\n\ + \ \"SAR\": 4.122982,\n \"SBD\": 9.124047,\n \"SCR\": 14.738562,\n\ + \ \"SDG\": 659.622869,\n \"SEK\": 11.172796,\n \"SGD\"\ + : 1.45608,\n \"SHP\": 1.337661,\n \"SLE\": 24.818436,\n \ + \ \"SLL\": 21712.595818,\n \"SOS\": 626.089441,\n \"SRD\": 41.174742,\n\ + \ \"STD\": 22754.779867,\n \"SVC\": 9.670448,\n \"SYP\"\ + : 2762.215174,\n \"SZL\": 20.256222,\n \"THB\": 37.008134,\n \ + \ \"TJS\": 12.068703,\n \"TMT\": 3.847802,\n \"TND\": 3.333021,\n\ + \ \"TOP\": 2.574511,\n \"TRY\": 21.447218,\n \"TTD\": 7.464661,\n\ + \ \"TWD\": 33.714991,\n \"TZS\": 2589.020803,\n \"UAH\"\ + : 40.821654,\n \"UGX\": 4116.373992,\n \"USD\": 1.099372,\n \ + \ \"UYU\": 43.065354,\n \"UZS\": 12625.214132,\n \"VEF\"\ + : 2748542.489672,\n \"VES\": 27.513312,\n \"VND\": 25785.76923,\n\ + \ \"VUV\": 129.803339,\n \"WST\": 2.975682,\n \"XAF\":\ + \ 656.19095,\n \"XAG\": 0.042873,\n \"XAU\": 0.000543,\n \ + \ \"XCD\": 2.971108,\n \"XDR\": 0.817792,\n \"XOF\": 656.233965,\n\ + \ \"XPF\": 119.557084,\n \"YER\": 275.227757,\n \"ZAR\"\ + : 20.166995,\n \"ZMK\": 9895.667336,\n \"ZMW\": 19.976122,\n \ + \ \"ZWL\": 353.997321\n }\n}\n" + content_type: text/plain + method: GET + status: 200 + url: https://api.apilayer.com/fixer/latest?base=EUR +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 1683809883,\n \"base\"\ + : \"EUR\",\n \"date\": \"2023-05-11\",\n \"rates\": {\n \"USD\"\ + : 1.093434\n }\n}\n" + content_type: text/plain + method: GET + status: 200 + url: https://api.apilayer.com/fixer/latest?base=EUR&symbols=USD +- response: + auto_calculate_content_length: false + body: "{\n \"success\": true,\n \"timestamp\": 1683607024,\n \"base\"\ + : \"EUR\",\n \"date\": \"2023-05-09\",\n \"rates\": {\n \"USD\"\ + : 1.099239,\n \"GBP\": 0.871224\n }\n}\n" + content_type: text/plain + method: GET + status: 200 + url: https://api.apilayer.com/fixer/latest?base=EUR&symbols=USD,GBP diff --git a/tests/test_available_currencies.py b/tests/test_available_currencies.py new file mode 100644 index 0000000..3a9fd98 --- /dev/null +++ b/tests/test_available_currencies.py @@ -0,0 +1,39 @@ +import responses +import pytest +from pathlib import Path + +from fixerio.client import Fixerio +from fixerio.exceptions import FixerioException + +BASE_URL = 'https://api.apilayer.com/fixer/' +RESPONSES_DIR = Path(__file__).parent / 'responses' + + +class TestAvailableCurrencies: + def setup_class(cls): + cls.access_key = 'test-access-key' + cls.url = BASE_URL + 'symbols' + cls.response_file = \ + RESPONSES_DIR / "available_currencies_responses.yaml" + + @responses.activate + def test_returns_available_currencies(self): + responses._add_from_file(self.response_file) + client = Fixerio(self.access_key) + response = client.available_currencies() + + assert response['success'] is True + assert response['symbols']['AED'] == 'United Arab Emirates Dirham' + assert response['symbols']['CRC'] == 'Costa Rican Colón' + + @responses.activate + def test_raises_exception_if_bad_request(self): + responses.add(responses.GET, + self.url, + body="{'success': false}", + status=400, + content_type='application/json') + + with pytest.raises(FixerioException): + client = Fixerio(self.access_key) + client.available_currencies() diff --git a/tests/test_base.py b/tests/test_base.py deleted file mode 100644 index 77405cf..0000000 --- a/tests/test_base.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import unicode_literals - -import unittest - -from fixerio.client import Fixerio - - -class FixerioInitTestCase(unittest.TestCase): - def test_raises_if_access_key_is_not_passed(self): - with self.assertRaises(TypeError): - Fixerio() - - def test_sets_access_key(self): - access_key = 'test-access-key' - - client = Fixerio(access_key) - - self.assertEqual(client.access_key, access_key) - - def test_sets_none_symbols_attribute_if_it_is_not_passed(self): - client = Fixerio('test-access-key') - - self.assertIsNone(client.symbols) - - def test_sets_symbols_attribute(self): - symbols = ['USD', 'GBP'] - - client = Fixerio('test-access-key', symbols=symbols) - - self.assertEqual(client.symbols, symbols) diff --git a/tests/test_historical_rates.py b/tests/test_historical_rates.py index 2114224..6003101 100644 --- a/tests/test_historical_rates.py +++ b/tests/test_historical_rates.py @@ -1,156 +1,113 @@ -from __future__ import unicode_literals - -import json -import unittest from datetime import date -try: - from urllib.parse import urljoin # noqa: F401 - from urllib.parse import urlencode # noqa: F401 -except ImportError: # For Python 2 - from urlparse import urljoin # noqa: F401 - from urllib import urlencode # noqa: F401 - import responses +import pytest +from pathlib import Path from fixerio.client import Fixerio from fixerio.exceptions import FixerioException -BASE_URL = 'http://data.fixer.io/api' + +BASE_URL = 'https://api.apilayer.com/fixer/' +RESPONSES_DIR = Path(__file__).parent / 'responses' -class FixerioHistoricalRatesTestCase(unittest.TestCase): - def setUp(self): - self.access_key = 'test-access-key' - self.date = date(2000, 1, 3) - self.path = '/{0}'.format(self.date.isoformat()) - query = urlencode({'access_key': self.access_key}) - self.url = BASE_URL + self.path + '?' + query +class TestHistoricalRates: + def setup_class(cls): + cls.access_key = 'test-access-key' + cls.date = date(2000, 1, 3) + cls.path = '{0}'.format(cls.date.isoformat()) + cls.url = BASE_URL + cls.path + cls.response_file = RESPONSES_DIR / "historical_rates_responses.yaml" @responses.activate - def test_returns_historical_rates(self): - expected_response = {'base': 'EUR', - 'date': '2000-01-03', - 'rates': {'GBP': 0.6246}} - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') + def test_returns_historical_eur_as_default_base(self): + responses._add_from_file(file_path=self.response_file) client = Fixerio(self.access_key) - response = client.historical_rates(date=self.date.isoformat()) - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - self.assertEqual(request.url, self.url) - self.assertIsNone(request.body) + response = client.historical_rates(self.date, symbols=['GBP']) - client = Fixerio(self.access_key) - response = client.historical_rates(date=self.date) - - self.assertDictEqual(response, expected_response) - request = responses.calls[1].request - self.assertEqual(request.method, 'GET') - self.assertEqual(request.url, self.url) - self.assertIsNone(request.body) + assert response['success'] is True + assert response['base'] == 'EUR' @responses.activate - def test_raises_exception_if_bad_request(self): - responses.add(responses.GET, - self.url, - body="{'success': false}", - status=400, - content_type='text/json') + def test_returns_historical_eur_to_gbp_rate(self): + responses._add_from_file(file_path=self.response_file) - with self.assertRaises(FixerioException)as ex: - client = Fixerio(self.access_key) - client.historical_rates(date=self.date) + client = Fixerio(self.access_key, base='EUR') + for date_variant in [self.date, self.date.isoformat()]: + response = client.historical_rates(date_variant, symbols=['GBP']) - expected_message = (('400 Client Error: Bad Request for url: ' - '{0}').format(self.url)) - self.assertEqual(str(ex.exception), expected_message) + assert response['success'] is True + assert response['base'] == 'EUR' + assert response['date'] == '2000-01-03' + assert response['rates']['GBP'] == 0.627016 + request = responses.calls[0].request + assert request.method == 'GET' + assert request.headers['apikey'] == self.access_key + assert request.body is None -class FixerioHistoricalRatesSymbolsTestCase(unittest.TestCase): - def setUp(self): - self.access_key = 'test-access-key' - self.date = date(2000, 1, 3) - self.path = '/{0}'.format(self.date.isoformat()) - query = urlencode({'access_key': self.access_key}) - self.url = BASE_URL + self.path + '?' + query + @responses.activate + def test_returns_historical_usd_to_gbp_rate(self): + responses._add_from_file(file_path=self.response_file) + + client = Fixerio(self.access_key, base='USD') + response = client.historical_rates(self.date, symbols=['GBP']) + + assert response['success'] is True + assert response['base'] == 'USD' + assert response['date'] == '2000-01-03' + assert response['rates']['GBP'] == 0.614647 @responses.activate def test_returns_historical_rates_for_symbols_passed_in_constructor(self): + responses._add_from_file(file_path=self.response_file) symbols = ['USD', 'GBP'] - expected_response = {"base": "EUR", - "date": "2000-01-03", - "rates": {"GBP": 0.6246, "USD": 1.009}} - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') client = Fixerio(self.access_key, symbols=symbols) - response = client.historical_rates(date=self.date) - - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + response = client.historical_rates(self.date) + + assert response['success'] is True + assert response['date'] == '2000-01-03' + assert response['rates']['GBP'] == 0.627016 + assert response['rates']['USD'] == 1.020124 @responses.activate def test_returns_historical_rates_for_symbols_passed_in_method(self): + responses._add_from_file(file_path=self.response_file) symbols = ['USD', 'GBP'] - expected_response = {"base": "EUR", - "date": "2000-01-03", - "rates": {"GBP": 0.6246, "USD": 1.009}} - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') client = Fixerio(self.access_key) - response = client.historical_rates(date=self.date, symbols=symbols) + response = client.historical_rates(self.date, symbols=symbols) - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + assert response['success'] is True + assert response['date'] == '2000-01-03' + assert response['rates']['USD'] == 1.020124 + assert response['rates']['GBP'] == 0.627016 @responses.activate - def test_returns_historical_rates_for_symbols_passed_if_both(self): + def test_returns_hist_rates_for_symbols_passed_in_method_if_both(self): + responses._add_from_file(file_path=self.response_file) + symbols = ['USD', 'GBP'] other_symbols = ['JPY', 'EUR'] - expected_response = {"base": "EUR", - "date": "2000-01-03", - "rates": {"GBP": 0.6246, "USD": 1.009}} - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') client = Fixerio(self.access_key, symbols=other_symbols) response = client.historical_rates(date=self.date, symbols=symbols) - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + assert response['rates']['USD'] == 1.020124 + assert response['rates']['GBP'] == 0.627016 + assert response['rates'].get('JPY') is None + assert response['rates'].get('EUR') is None + + @responses.activate + def test_raises_exception_if_bad_request(self): + responses.add(responses.GET, + self.url, + body="{'success': false}", + status=400, + content_type='text/json') + with pytest.raises(FixerioException): + client = Fixerio(self.access_key) + client.historical_rates(date=self.date) diff --git a/tests/test_latest.py b/tests/test_latest.py index 06087cc..606dd56 100644 --- a/tests/test_latest.py +++ b/tests/test_latest.py @@ -1,47 +1,33 @@ -from __future__ import unicode_literals - -import json -import unittest - -try: - from urllib.parse import urljoin # noqa: F401 - from urllib.parse import urlencode # noqa: F401 -except ImportError: # For Python 2 - from urlparse import urljoin # noqa: F401 - from urllib import urlencode # noqa: F401 - import responses +import pytest +from pathlib import Path from fixerio.client import Fixerio from fixerio.exceptions import FixerioException -BASE_URL = 'http://data.fixer.io/api/' +BASE_URL = 'https://api.apilayer.com/fixer/' +RESPONSES_DIR = Path(__file__).parent / 'responses' -class FixerioLatestTestCase(unittest.TestCase): - def setUp(self): - self.access_key = 'test-access-key' - self.path = 'latest' - query = urlencode({'access_key': self.access_key}) - self.url = BASE_URL + self.path + '?' + query +class TestLatest: + def setup_class(cls): + cls.access_key = 'test-access-key' + cls.date = '2023-05-09' + cls.url = BASE_URL + 'latest' + cls.response_file = RESPONSES_DIR / "latest_responses.yaml" @responses.activate def test_returns_latest_rates(self): - expected_response = {'base': 'EUR', 'date': '2016-04-29', - 'rates': {'GBP': 0.78025}} - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') - + responses._add_from_file(file_path=self.response_file) client = Fixerio(self.access_key) response = client.latest() - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - self.assertEqual(request.url, self.url) - self.assertIsNone(request.body) + assert response['date'] == self.date + assert response['base'] == 'EUR' + rates = response['rates'] + assert rates['SEK'] == 11.172796 + assert rates['NOK'] == 11.557591 + assert rates['JPY'] == 148.40037 @responses.activate def test_raises_exception_if_bad_request(self): @@ -49,102 +35,52 @@ def test_raises_exception_if_bad_request(self): self.url, body="{'success': false}", status=400, - content_type='text/json') + content_type='application/json') - with self.assertRaises(FixerioException)as ex: + with pytest.raises(FixerioException): client = Fixerio(self.access_key) client.latest() - expected_message = (('400 Client Error: Bad Request for url: ' - '{0}').format(self.url)) - self.assertEqual(str(ex.exception), expected_message) - + @responses.activate + def test_returns_latest_rate_for_single_symbol_passed_as_string(self): + symbol = 'USD' + responses._add_from_file(file_path=self.response_file) + client = Fixerio(self.access_key, symbols=symbol) + response = client.latest() -class FixerioLatestSymbolsTestCase(unittest.TestCase): - def setUp(self): - self.access_key = 'test-access-key' - self.path = 'latest' - query = urlencode({'access_key': self.access_key}) - self.url = BASE_URL + self.path + '?' + query + assert response['rates']['USD'] == 1.093434 @responses.activate def test_returns_latest_rates_for_symbols_passed_in_constructor(self): symbols = ['USD', 'GBP'] - expected_response = { - "base": "EUR", - "date": "2016-05-19", - "rates": {"GBP": 0.76585, "USD": 1.1197} - } - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') + responses._add_from_file(file_path=self.response_file) client = Fixerio(self.access_key, symbols=symbols) response = client.latest() - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + assert response['rates']['USD'] == 1.099239 + assert response['rates']['GBP'] == 0.871224 @responses.activate def test_returns_latest_rates_for_symbols_passed_in_method(self): symbols = ['USD', 'GBP'] - expected_response = { - "base": "EUR", - "date": "2016-05-19", - "rates": {"GBP": 0.76585, "USD": 1.1197} - } - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') + responses._add_from_file(file_path=self.response_file) client = Fixerio(self.access_key) response = client.latest(symbols=symbols) - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + assert response['rates']['USD'] == 1.099239 + assert response['rates']['GBP'] == 0.871224 @responses.activate def test_returns_latest_rates_for_symbols_passed_in_method_if_both(self): symbols = ['USD', 'GBP'] other_symbols = ['JPY', 'EUR'] - expected_response = { - "base": "EUR", - "date": "2016-05-19", - "rates": {"GBP": 0.76585, "USD": 1.1197} - } - responses.add(responses.GET, - self.url, - body=json.dumps(expected_response), - content_type='application/json') + responses._add_from_file(file_path=self.response_file) client = Fixerio(self.access_key, symbols=other_symbols) response = client.latest(symbols=symbols) - self.assertDictEqual(response, expected_response) - request = responses.calls[0].request - self.assertEqual(request.method, 'GET') - symbols_str = ','.join(symbols) - params = urlencode( - {'access_key': self.access_key, 'symbols': symbols_str}) - expected_path = '{url}?{params}'.format(url=self.path, params=params) - expected_url = BASE_URL + expected_path - self.assertEqual(request.url, expected_url) - self.assertIsNone(request.body) + assert response['rates']['USD'] == 1.099239 + assert response['rates']['GBP'] == 0.871224 + assert response['rates'].get('JPY') is None