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