From ba1ead3958107fa7969fa7b5bfa2854177d5abc7 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Wed, 18 Nov 2020 22:27:19 +0100 Subject: [PATCH] Add models for Kamereon (#37) * Add models for RenaultClient * Update fixtures * Use marshmallow_dataclass * Update fixtures * Update fixtures * Update fixtures * Update fixtures * Add KamereonVehicleDataResponse * Add tests for vehicle actions * Add tests for vehicle errors * Fix tests * Add KamereonException * Fix tests * Update parsing of Kamereon errors * Add raw_data to Kamereon models * Fix pre-commit * Fix method descriptions * Remove attributes property on KamereonVehicleData model --- src/renault_api/exceptions.py | 16 ++ src/renault_api/model/__init__.py | 18 +- src/renault_api/model/kamereon.py | 117 ++++++++++++ tests/fixtures/kamereon/account_vehicles.txt | 1 - .../kamereon/errors/invalid_upstream.txt | 1 - .../kamereon/errors/not_supported.txt | 1 - .../fixtures/kamereon/errors/quota_limit.txt | 1 - tests/fixtures/kamereon/person.json | 68 +++++++ tests/fixtures/kamereon/persons_detail.txt | 1 - .../kamereon/vehicle/battery-status.1.txt | 1 - .../kamereon/vehicle/battery-status.2.txt | 1 - .../kamereon/vehicle/charge-history.txt | 1 - tests/fixtures/kamereon/vehicle/charges.txt | 1 - tests/fixtures/kamereon/vehicle/cockpit.txt | 1 - .../fixtures/kamereon/vehicle/hvac-status.txt | 1 - .../charge-mode.schedule_mode.json | 7 + .../charge-schedule.schedules.json | 7 + .../vehicle_action/charging-start.start.json | 7 + .../vehicle_action/hvac-start.cancel.json | 7 + .../vehicle_action/hvac-start.start.json | 7 + .../charge-mode.schedule_mode.txt | 1 - .../charge-schedule.schedules.txt | 1 - .../vehicle_actions/charging-start.start.txt | 1 - .../vehicle_actions/hvac-start.cancel.txt | 1 - .../hvac-start.start.invalid_date.txt | 1 - .../vehicle_actions/hvac-start.start.txt | 1 - .../vehicle_data/battery-status.1.json | 15 ++ .../vehicle_data/battery-status.2.json | 16 ++ .../kamereon/vehicle_data/charge-history.json | 16 ++ .../kamereon/vehicle_data/charges.json | 21 +++ .../kamereon/vehicle_data/cockpit.json | 7 + .../kamereon/vehicle_data/hvac-status.json | 7 + .../kamereon/vehicle_error/invalid_date.json | 16 ++ .../vehicle_error/invalid_upstream.json | 16 ++ .../kamereon/vehicle_error/not_supported.json | 16 ++ .../kamereon/vehicle_error/quota_limit.json | 16 ++ tests/fixtures/kamereon/vehicles/zoe.1.json | 138 ++++++++++++++ tests/model/test_kamereon.py | 178 ++++++++++++++++++ 38 files changed, 715 insertions(+), 18 deletions(-) create mode 100644 src/renault_api/model/kamereon.py delete mode 100644 tests/fixtures/kamereon/account_vehicles.txt delete mode 100644 tests/fixtures/kamereon/errors/invalid_upstream.txt delete mode 100644 tests/fixtures/kamereon/errors/not_supported.txt delete mode 100644 tests/fixtures/kamereon/errors/quota_limit.txt create mode 100644 tests/fixtures/kamereon/person.json delete mode 100644 tests/fixtures/kamereon/persons_detail.txt delete mode 100644 tests/fixtures/kamereon/vehicle/battery-status.1.txt delete mode 100644 tests/fixtures/kamereon/vehicle/battery-status.2.txt delete mode 100644 tests/fixtures/kamereon/vehicle/charge-history.txt delete mode 100644 tests/fixtures/kamereon/vehicle/charges.txt delete mode 100644 tests/fixtures/kamereon/vehicle/cockpit.txt delete mode 100644 tests/fixtures/kamereon/vehicle/hvac-status.txt create mode 100644 tests/fixtures/kamereon/vehicle_action/charge-mode.schedule_mode.json create mode 100644 tests/fixtures/kamereon/vehicle_action/charge-schedule.schedules.json create mode 100644 tests/fixtures/kamereon/vehicle_action/charging-start.start.json create mode 100644 tests/fixtures/kamereon/vehicle_action/hvac-start.cancel.json create mode 100644 tests/fixtures/kamereon/vehicle_action/hvac-start.start.json delete mode 100644 tests/fixtures/kamereon/vehicle_actions/charge-mode.schedule_mode.txt delete mode 100644 tests/fixtures/kamereon/vehicle_actions/charge-schedule.schedules.txt delete mode 100644 tests/fixtures/kamereon/vehicle_actions/charging-start.start.txt delete mode 100644 tests/fixtures/kamereon/vehicle_actions/hvac-start.cancel.txt delete mode 100644 tests/fixtures/kamereon/vehicle_actions/hvac-start.start.invalid_date.txt delete mode 100644 tests/fixtures/kamereon/vehicle_actions/hvac-start.start.txt create mode 100644 tests/fixtures/kamereon/vehicle_data/battery-status.1.json create mode 100644 tests/fixtures/kamereon/vehicle_data/battery-status.2.json create mode 100644 tests/fixtures/kamereon/vehicle_data/charge-history.json create mode 100644 tests/fixtures/kamereon/vehicle_data/charges.json create mode 100644 tests/fixtures/kamereon/vehicle_data/cockpit.json create mode 100644 tests/fixtures/kamereon/vehicle_data/hvac-status.json create mode 100644 tests/fixtures/kamereon/vehicle_error/invalid_date.json create mode 100644 tests/fixtures/kamereon/vehicle_error/invalid_upstream.json create mode 100644 tests/fixtures/kamereon/vehicle_error/not_supported.json create mode 100644 tests/fixtures/kamereon/vehicle_error/quota_limit.json create mode 100644 tests/fixtures/kamereon/vehicles/zoe.1.json create mode 100644 tests/model/test_kamereon.py diff --git a/src/renault_api/exceptions.py b/src/renault_api/exceptions.py index 7a343c96..35c56ba2 100644 --- a/src/renault_api/exceptions.py +++ b/src/renault_api/exceptions.py @@ -22,3 +22,19 @@ def __init__(self, error_code: int, error_details: Optional[str]): """Initialise GigyaResponseException.""" self.error_code = error_code self.error_details = error_details + + +# Kamereon exceptions +class KamereonException(RenaultException): + """Base exception for Kamereon errors.""" + + pass + + +class KamereonResponseException(KamereonException): + """Kamereon returned a parsable errors.""" + + def __init__(self, error_code: Optional[str], error_details: Optional[str]): + """Initialise KamereonResponseException.""" + self.error_code = error_code + self.error_details = error_details diff --git a/src/renault_api/model/__init__.py b/src/renault_api/model/__init__.py index b3ead963..c1e97048 100644 --- a/src/renault_api/model/__init__.py +++ b/src/renault_api/model/__init__.py @@ -1,11 +1,27 @@ """Models for Renault API.""" +from dataclasses import dataclass +from typing import Any +from typing import Dict + import marshmallow +@dataclass +class BaseModel: + """Base model for Gigya and Kamereon models to include raw_data.""" + + raw_data: Dict[str, Any] + + class BaseSchema(marshmallow.Schema): - """Base schema for Gigya models to exclude unknown fields.""" + """Base schema for Gigya and Kamereon models to exclude unknown fields.""" class Meta: """Force unknown fields to 'exclude'.""" unknown = marshmallow.EXCLUDE + + @marshmallow.pre_load + def get_raw_data(self, data, **kwargs): # type: ignore + """Ensure raw_data is added to the data set.""" + return {"raw_data": data, **data} diff --git a/src/renault_api/model/kamereon.py b/src/renault_api/model/kamereon.py new file mode 100644 index 00000000..85f20a85 --- /dev/null +++ b/src/renault_api/model/kamereon.py @@ -0,0 +1,117 @@ +"""Kamereon models.""" +import json +from dataclasses import dataclass +from typing import List +from typing import Optional + +import marshmallow_dataclass + +from . import BaseModel +from . import BaseSchema +from renault_api.exceptions import KamereonResponseException + + +@dataclass +class KamereonResponseError(BaseModel): + """Kamereon response error.""" + + errorCode: Optional[str] # noqa: N815 + errorMessage: Optional[str] # noqa: N815 + + def raise_for_error_code(self) -> None: + """Raise exception from response error.""" + raise KamereonResponseException(self.errorCode, self.get_error_details()) + + def get_error_details(self) -> Optional[str]: + """Extract the error details sometimes hidden inside nested JSON.""" + try: + error_details = json.loads(self.errorMessage or "{}") + except json.JSONDecodeError: + return self.errorMessage + + error_descriptions = [] + for inner_error in error_details.get("errors", []): + error_description = " ".join( + filter( + None, + [ + inner_error.get("title"), + inner_error.get("source", {}).get("pointer"), + inner_error.get("detail"), + ], + ) + ) + error_descriptions.append(error_description) + + return ", ".join(error_descriptions) or self.errorMessage + + +@dataclass +class KamereonResponse(BaseModel): + """Kamereon response.""" + + errors: Optional[List[KamereonResponseError]] + + def raise_for_error_code(self) -> None: + """Raise exception if errors found in the response.""" + for error in self.errors or []: + # Until we have a sample for multiple errors, just raise on first one + error.raise_for_error_code() + + +@dataclass +class KamereonPersonAccount(BaseModel): + """Kamereon account data.""" + + accountId: Optional[str] # noqa: N815 + accountType: Optional[str] # noqa: N815 + accountStatus: Optional[str] # noqa: N815 + + +@dataclass +class KamereonPersonResponse(KamereonResponse): + """Kamereon response to GET on /persons/{gigya_person_id}.""" + + accounts: List[KamereonPersonAccount] + + +@dataclass +class KamereonVehiclesLink(BaseModel): + """Kamereon account data.""" + + vin: Optional[str] + + +@dataclass +class KamereonVehiclesResponse(KamereonResponse): + """Kamereon response to GET on /accounts/{account_id}/vehicles.""" + + accountId: Optional[str] # noqa: N815 + country: Optional[str] + vehicleLinks: List[KamereonVehiclesLink] # noqa: N815 + + +@dataclass +class KamereonVehicleData(BaseModel): + """Kamereon account data.""" + + type: Optional[str] + id: Optional[str] + + +@dataclass +class KamereonVehicleDataResponse(KamereonResponse): + """Kamereon response to GET/POST on .../cars/{vin}/{type}.""" + + data: Optional[KamereonVehicleData] + + +KamereonPersonResponseSchema = marshmallow_dataclass.class_schema( + KamereonPersonResponse, base_schema=BaseSchema +)() +KamereonVehiclesResponseSchema = marshmallow_dataclass.class_schema( + KamereonVehiclesResponse, base_schema=BaseSchema +)() +KamereonVehicleDataResponseSchema = marshmallow_dataclass.class_schema( + KamereonVehicleDataResponse, base_schema=BaseSchema +)() diff --git a/tests/fixtures/kamereon/account_vehicles.txt b/tests/fixtures/kamereon/account_vehicles.txt deleted file mode 100644 index d2f3bcec..00000000 --- a/tests/fixtures/kamereon/account_vehicles.txt +++ /dev/null @@ -1 +0,0 @@ -{'accountId': 'account-id-1', 'country': 'FR', 'vehicleLinks': [{'brand': 'RENAULT', 'vin': 'VF1AAAAA555777999', 'status': 'ACTIVE', 'linkType': 'OWNER', 'garageBrand': 'RENAULT', 'annualMileage': 16000, 'mileage': 26464, 'startDate': '2017-08-07', 'createdDate': '2019-05-23T21:38:16.409008Z', 'lastModifiedDate': '2020-11-17T08:41:40.497400Z', 'ownershipStartDate': '2017-08-01', 'cancellationReason': {}, 'connectedDriver': {'role': 'MAIN_DRIVER', 'createdDate': '2019-06-17T09:49:06.880627Z', 'lastModifiedDate': '2019-06-17T09:49:06.880627Z'}, 'vehicleDetails': {'vin': 'VF1AAAAA555777999', 'registrationDate': '2017-08-01', 'firstRegistrationDate': '2017-08-01', 'engineType': '5AQ', 'engineRatio': '601', 'modelSCR': 'ZOE', 'deliveryCountry': {'code': 'FR', 'label': 'FRANCE'}, 'family': {'code': 'X10', 'label': 'FAMILLE X10', 'group': '007'}, 'tcu': {'code': 'TCU0G2', 'label': 'TCU VER 0 GEN 2', 'group': 'E70'}, 'navigationAssistanceLevel': {'code': 'NAV3G5', 'label': 'LEVEL 3 TYPE 5 NAVIGATION', 'group': '408'}, 'battery': {'code': 'BT4AR1', 'label': 'BATTERIE BT4AR1', 'group': '968'}, 'radioType': {'code': 'RAD37A', 'label': 'RADIO 37A', 'group': '425'}, 'registrationCountry': {'code': 'FR'}, 'brand': {'label': 'RENAULT'}, 'model': {'code': 'X101VE', 'label': 'ZOE', 'group': '971'}, 'gearbox': {'code': 'BVEL', 'label': 'BOITE A VARIATEUR ELECTRIQUE', 'group': '427'}, 'version': {'code': 'INT MB 10R'}, 'energy': {'code': 'ELEC', 'label': 'ELECTRIQUE', 'group': '019'}, 'registrationNumber': 'registrationNumber', 'vcd': 'SYTINC/SKTPOU/SAND41/FDIU1/SSESM/MAPSUP/SSCALL/SAND88/SAND90/SQKDRO/SDIFPA/FACBA2/PRLEX1/SSRCAR/CABDO2/TCU0G2/SWALBO/EVTEC1/STANDA/X10/B10/EA2/MB/ELEC/DG/TEMP/TR4X2/RV/ABS/CAREG/LAC/VT003/CPE/RET03/SPROJA/RALU16/CEAVRH/AIRBA1/SERIE/DRA/DRAP08/HARM02/ATAR/TERQG/SFBANA/KM/DPRPN/AVREPL/SSDECA/ASRESP/RDAR02/ALEVA/CACBL2/SOP02C/CTHAB2/TRNOR/LVAVIP/LVAREL/SASURV/KTGREP/SGSCHA/APL03/ALOUCC/CMAR3P/NAV3G5/RAD37A/BVEL/AUTAUG/RNORM/ISOFIX/EQPEUR/HRGM01/SDPCLV/TLFRAN/SPRODI/SAN613/SSAPEX/GENEV1/ELC1/SANCML/PE2012/PHAS1/SAN913/045KWH/BT4AR1/VEC153/X101VE/NBT017/5AQ', 'assets': [{'assetType': 'PICTURE', 'renditions': [{'resolutionType': 'ONE_MYRENAULT_LARGE', 'url': 'https://3dv2.renault.com/ImageFromBookmark?configuration=SKTPOU%2FPRLEX1%2FSTANDA%2FB10%2FEA2%2FDG%2FVT003%2FRET03%2FRALU16%2FDRAP08%2FHARM02%2FTERQG%2FRDAR02%2FALEVA%2FSOP02C%2FTRNOR%2FLVAVIP%2FLVAREL%2FNAV3G5%2FRAD37A%2FSDPCLV%2FTLFRAN%2FGENEV1%2FSAN913%2FBT4AR1%2FNBT017&databaseId=1d514feb-93a6-4b45-8785-e11d2a6f1864&bookmarkSet=RSITE&bookmark=EXT_34_DESSUS&profile=HELIOS_OWNERSERVICES_LARGE'}, {'resolutionType': 'ONE_MYRENAULT_SMALL', 'url': 'https://3dv2.renault.com/ImageFromBookmark?configuration=SKTPOU%2FPRLEX1%2FSTANDA%2FB10%2FEA2%2FDG%2FVT003%2FRET03%2FRALU16%2FDRAP08%2FHARM02%2FTERQG%2FRDAR02%2FALEVA%2FSOP02C%2FTRNOR%2FLVAVIP%2FLVAREL%2FNAV3G5%2FRAD37A%2FSDPCLV%2FTLFRAN%2FGENEV1%2FSAN913%2FBT4AR1%2FNBT017&databaseId=1d514feb-93a6-4b45-8785-e11d2a6f1864&bookmarkSet=RSITE&bookmark=EXT_34_DESSUS&profile=HELIOS_OWNERSERVICES_SMALL_V2'}]}, {'assetType': 'PDF', 'assetRole': 'GUIDE', 'title': 'PDF Guide', 'description': '', 'renditions': [{'url': 'https://cdn.group.renault.com/ren/gb/myr/assets/x101ve/manual.pdf.asset.pdf/1558704861676.pdf'}]}, {'assetType': 'URL', 'assetRole': 'GUIDE', 'title': 'e-guide', 'description': '', 'renditions': [{'url': 'http://gb.e-guide.renault.com/eng/Zoe'}]}, {'assetType': 'VIDEO', 'assetRole': 'CAR', 'title': '10 Fundamentals about getting the best out of your electric vehicle', 'description': '', 'renditions': [{'url': '39r6QEKcOM4'}]}, {'assetType': 'VIDEO', 'assetRole': 'CAR', 'title': 'Automatic Climate Control', 'description': '', 'renditions': [{'url': 'Va2FnZFo_GE'}]}, {'assetType': 'URL', 'assetRole': 'CAR', 'title': 'More videos', 'description': '', 'renditions': [{'url': 'https://www.youtube.com/watch?v=wfpCMkK1rKI'}]}, {'assetType': 'VIDEO', 'assetRole': 'CAR', 'title': 'Charging the battery', 'description': '', 'renditions': [{'url': 'RaEad8DjUJs'}]}, {'assetType': 'VIDEO', 'assetRole': 'CAR', 'title': 'Charging the battery at a station with a flap', 'description': '', 'renditions': [{'url': 'zJfd7fJWtr0'}]}], 'yearsOfMaintenance': 12, 'connectivityTechnology': 'RLINK1', 'easyConnectStore': False, 'electrical': True, 'rlinkStore': False, 'deliveryDate': '2017-08-11', 'retrievedFromDhs': False, 'engineEnergyType': 'ELEC', 'radioCode': '0738'}}]} diff --git a/tests/fixtures/kamereon/errors/invalid_upstream.txt b/tests/fixtures/kamereon/errors/invalid_upstream.txt deleted file mode 100644 index a69f452f..00000000 --- a/tests/fixtures/kamereon/errors/invalid_upstream.txt +++ /dev/null @@ -1 +0,0 @@ -{"type":"TECHNICAL","messages":[{"code":"err.tech.500","message":"{\"errors\":[{\"status\":\"Internal Server Error\",\"code\":\"500\",\"title\":\"Invalid response from the upstream server (The request sent to the GDC is erroneous) ; 502 Bad Gateway\"}]}"}],"errors":[{"errorCode":"err.tech.500","errorMessage":"{\"errors\":[{\"status\":\"Internal Server Error\",\"code\":\"500\",\"title\":\"Invalid response from the upstream server (The request sent to the GDC is erroneous) ; 502 Bad Gateway\"}]}"}],"error_reference":"TECHNICAL"} diff --git a/tests/fixtures/kamereon/errors/not_supported.txt b/tests/fixtures/kamereon/errors/not_supported.txt deleted file mode 100644 index 1ee19e5e..00000000 --- a/tests/fixtures/kamereon/errors/not_supported.txt +++ /dev/null @@ -1 +0,0 @@ -{"type":"TECHNICAL","messages":[{"code":"err.tech.501","message":"{\"errors\":[{\"status\":\"501\",\"code\":\"error.internal\",\"detail\":\"This feature is not technically supported by this gateway\"}]}"}],"errors":[{"errorCode":"err.tech.501","errorMessage":"{\"errors\":[{\"status\":\"501\",\"code\":\"error.internal\",\"detail\":\"This feature is not technically supported by this gateway\"}]}"}],"error_reference":"TECHNICAL"} diff --git a/tests/fixtures/kamereon/errors/quota_limit.txt b/tests/fixtures/kamereon/errors/quota_limit.txt deleted file mode 100644 index 8cbb3c66..00000000 --- a/tests/fixtures/kamereon/errors/quota_limit.txt +++ /dev/null @@ -1 +0,0 @@ -{"type":"FUNCTIONAL","messages":[{"code":"err.func.wired.overloaded","message":"You have reached your quota limit"}],"errors":[{"errorCode":"err.func.wired.overloaded","errorMessage":"You have reached your quota limit","errorType":"functional"}]} diff --git a/tests/fixtures/kamereon/person.json b/tests/fixtures/kamereon/person.json new file mode 100644 index 00000000..db6cbfcb --- /dev/null +++ b/tests/fixtures/kamereon/person.json @@ -0,0 +1,68 @@ +{ + "personId": "person-id-1", + "type": "I", + "country": "FR", + "civility": "1", + "firstName": "firstName", + "lastName": "lastName", + "idp": { + "idpId": "idpId", + "idpType": "GIGYA", + "idpStatus": "ACTIVE", + "login": "email@email.com", + "loginType": "EMAIL", + "lastLoginDate": "2020-11-16T11:43:52Z", + "termsConditionAcceptance": true, + "termsConditionLastAcceptanceDate": "2019-04-29T20:39:37.153608Z" + }, + "emails": [ + { + "emailType": "MAIN", + "emailValue": "email@email.com", + "validityFlag": true, + "createdDate": "2019-04-29T20:39:37.162651Z", + "lastModifiedDate": "2019-04-29T20:39:37.162654Z", + "functionalCreationDate": "2019-04-29T20:39:37.162651Z", + "functionalModificationDate": "2019-04-29T20:39:37.162654Z" + } + ], + "phones": [ + { + "phoneType": "MOBILE", + "phoneValue": "phoneValue", + "areaCode": "33", + "createdDate": "2019-06-17T09:47:14.745555Z", + "lastModifiedDate": "2019-06-17T09:47:14.745730Z", + "functionalCreationDate": "2019-06-17T09:47:14.745555Z", + "functionalModificationDate": "2019-06-17T09:47:14.745730Z" + } + ], + "identities": [], + "myrRequest": false, + "accounts": [ + { + "accountId": "account-id-1", + "accountType": "MYRENAULT", + "accountStatus": "ACTIVE", + "country": "FR", + "personId": "person-id-1", + "relationType": "OWNER" + }, + { + "accountId": "account-id-2", + "externalId": "externalId", + "accountType": "SFDC", + "accountStatus": "ACTIVE", + "country": "FR", + "personId": "person-id-1", + "relationType": "OWNER" + } + ], + "partyId": "partyId", + "mdmId": "mdmId", + "createdDate": "2019-04-29T20:39:37.163635Z", + "lastModifiedDate": "2020-11-16T11:43:57.658639Z", + "functionalCreationDate": "2019-04-29T20:39:37.162578Z", + "functionalModificationDate": "2020-11-16T11:29:48.631544Z", + "locale": "fr-FR" +} diff --git a/tests/fixtures/kamereon/persons_detail.txt b/tests/fixtures/kamereon/persons_detail.txt deleted file mode 100644 index 774c372d..00000000 --- a/tests/fixtures/kamereon/persons_detail.txt +++ /dev/null @@ -1 +0,0 @@ -{'personId': 'person-id-1', 'type': 'I', 'country': 'FR', 'civility': '1', 'firstName': 'firstName', 'lastName': 'lastName', 'idp': {'idpId': 'idpId', 'idpType': 'GIGYA', 'idpStatus': 'ACTIVE', 'login': 'email@email.com', 'loginType': 'EMAIL', 'lastLoginDate': '2020-11-16T11:43:52Z', 'termsConditionAcceptance': True, 'termsConditionLastAcceptanceDate': '2019-04-29T20:39:37.153608Z'}, 'emails': [{'emailType': 'MAIN', 'emailValue': 'email@email.com', 'validityFlag': True, 'createdDate': '2019-04-29T20:39:37.162651Z', 'lastModifiedDate': '2019-04-29T20:39:37.162654Z', 'functionalCreationDate': '2019-04-29T20:39:37.162651Z', 'functionalModificationDate': '2019-04-29T20:39:37.162654Z'}], 'phones': [{'phoneType': 'MOBILE', 'phoneValue': 'phoneValue', 'areaCode': '33', 'createdDate': '2019-06-17T09:47:14.745555Z', 'lastModifiedDate': '2019-06-17T09:47:14.745730Z', 'functionalCreationDate': '2019-06-17T09:47:14.745555Z', 'functionalModificationDate': '2019-06-17T09:47:14.745730Z'}], 'identities': [], 'myrRequest': False, 'accounts': [{'accountId': 'account-id-1', 'accountType': 'MYRENAULT', 'accountStatus': 'ACTIVE', 'country': 'FR', 'personId': 'person-id-1', 'relationType': 'OWNER'}, {'accountId': 'account-id-2', 'externalId': 'externalId', 'accountType': 'SFDC', 'accountStatus': 'ACTIVE', 'country': 'FR', 'personId': 'person-id-1', 'relationType': 'OWNER'}], 'partyId': 'partyId', 'mdmId': 'mdmId', 'createdDate': '2019-04-29T20:39:37.163635Z', 'lastModifiedDate': '2020-11-16T11:43:57.658639Z', 'functionalCreationDate': '2019-04-29T20:39:37.162578Z', 'functionalModificationDate': '2020-11-16T11:29:48.631544Z', 'locale': 'fr-FR'} diff --git a/tests/fixtures/kamereon/vehicle/battery-status.1.txt b/tests/fixtures/kamereon/vehicle/battery-status.1.txt deleted file mode 100644 index 8ee13f5d..00000000 --- a/tests/fixtures/kamereon/vehicle/battery-status.1.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"timestamp":"2020-11-17T09:06:48+01:00","batteryLevel":50,"batteryAutonomy":128,"batteryCapacity":0,"batteryAvailableEnergy":0,"plugStatus":0,"chargingStatus":-1.0}}} diff --git a/tests/fixtures/kamereon/vehicle/battery-status.2.txt b/tests/fixtures/kamereon/vehicle/battery-status.2.txt deleted file mode 100644 index 1c2bb8d2..00000000 --- a/tests/fixtures/kamereon/vehicle/battery-status.2.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"timestamp":"2020-11-09T07:41:49+01:00","batteryLevel":34,"batteryTemperature":10,"batteryAutonomy":88,"batteryCapacity":0,"batteryAvailableEnergy":0,"plugStatus":0,"chargingStatus":-1.0}}} diff --git a/tests/fixtures/kamereon/vehicle/charge-history.txt b/tests/fixtures/kamereon/vehicle/charge-history.txt deleted file mode 100644 index 4517c14d..00000000 --- a/tests/fixtures/kamereon/vehicle/charge-history.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"chargeSummaries":[{"month":"202011","totalChargesNumber":1,"totalChargesDuration":479,"totalChargesErrors":0}]}}} diff --git a/tests/fixtures/kamereon/vehicle/charges.txt b/tests/fixtures/kamereon/vehicle/charges.txt deleted file mode 100644 index c54d4283..00000000 --- a/tests/fixtures/kamereon/vehicle/charges.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"charges":[{"chargeStartDate":"2020-11-11T00:31:03Z","chargeEndDate":"2020-11-11T08:30:17Z","chargeDuration":479,"chargeStartBatteryLevel":15,"chargeEndBatteryLevel":74,"chargeBatteryLevelRecovered":59,"chargePower":"slow","chargeStartInstantaneousPower":3100,"chargeEndStatus":"ok"}]}}} diff --git a/tests/fixtures/kamereon/vehicle/cockpit.txt b/tests/fixtures/kamereon/vehicle/cockpit.txt deleted file mode 100644 index 29beb341..00000000 --- a/tests/fixtures/kamereon/vehicle/cockpit.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"totalMileage":49114.27}}} diff --git a/tests/fixtures/kamereon/vehicle/hvac-status.txt b/tests/fixtures/kamereon/vehicle/hvac-status.txt deleted file mode 100644 index 2b325f4e..00000000 --- a/tests/fixtures/kamereon/vehicle/hvac-status.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"Car","id":"VF1AAAAA555777999","attributes":{"externalTemperature":8.0,"hvacStatus":"off"}}} diff --git a/tests/fixtures/kamereon/vehicle_action/charge-mode.schedule_mode.json b/tests/fixtures/kamereon/vehicle_action/charge-mode.schedule_mode.json new file mode 100644 index 00000000..faef8120 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_action/charge-mode.schedule_mode.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "ChargeMode", + "id": "guid", + "attributes": { "action": "schedule_mode" } + } +} diff --git a/tests/fixtures/kamereon/vehicle_action/charge-schedule.schedules.json b/tests/fixtures/kamereon/vehicle_action/charge-schedule.schedules.json new file mode 100644 index 00000000..5ab9988f --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_action/charge-schedule.schedules.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "ChargeSchedule", + "id": "guid", + "attributes": { "schedules": [] } + } +} diff --git a/tests/fixtures/kamereon/vehicle_action/charging-start.start.json b/tests/fixtures/kamereon/vehicle_action/charging-start.start.json new file mode 100644 index 00000000..3adb7051 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_action/charging-start.start.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "ChargingStart", + "id": "guid", + "attributes": { "action": "start" } + } +} diff --git a/tests/fixtures/kamereon/vehicle_action/hvac-start.cancel.json b/tests/fixtures/kamereon/vehicle_action/hvac-start.cancel.json new file mode 100644 index 00000000..df7a94cb --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_action/hvac-start.cancel.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "HvacStart", + "id": "guid", + "attributes": { "action": "cancel" } + } +} diff --git a/tests/fixtures/kamereon/vehicle_action/hvac-start.start.json b/tests/fixtures/kamereon/vehicle_action/hvac-start.start.json new file mode 100644 index 00000000..7aca3269 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_action/hvac-start.start.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "HvacStart", + "id": "guid", + "attributes": { "action": "start", "targetTemperature": 21.0 } + } +} diff --git a/tests/fixtures/kamereon/vehicle_actions/charge-mode.schedule_mode.txt b/tests/fixtures/kamereon/vehicle_actions/charge-mode.schedule_mode.txt deleted file mode 100644 index e57c153f..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/charge-mode.schedule_mode.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"ChargeMode","id":"guid","attributes":{"action":"schedule_mode"}}} diff --git a/tests/fixtures/kamereon/vehicle_actions/charge-schedule.schedules.txt b/tests/fixtures/kamereon/vehicle_actions/charge-schedule.schedules.txt deleted file mode 100644 index 9a744f1d..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/charge-schedule.schedules.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"ChargeSchedule","id":"guid","attributes":{"schedules":[]}}} diff --git a/tests/fixtures/kamereon/vehicle_actions/charging-start.start.txt b/tests/fixtures/kamereon/vehicle_actions/charging-start.start.txt deleted file mode 100644 index bb63864a..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/charging-start.start.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"ChargingStart","id":"guid","attributes":{"action":"start"}}} diff --git a/tests/fixtures/kamereon/vehicle_actions/hvac-start.cancel.txt b/tests/fixtures/kamereon/vehicle_actions/hvac-start.cancel.txt deleted file mode 100644 index b7e7233a..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/hvac-start.cancel.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"HvacStart","id":"guid","attributes":{"action":"cancel"}}} diff --git a/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.invalid_date.txt b/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.invalid_date.txt deleted file mode 100644 index 8296018e..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.invalid_date.txt +++ /dev/null @@ -1 +0,0 @@ -{"type":"FUNCTIONAL","messages":[{"code":"err.func.400","message":"{\"errors\":[{\"status\":\"400\",\"code\":\"Future\",\"detail\":\"must be a future date\",\"source\":{\"pointer\":\"/data/attributes/startDateTime\"}}]}"}],"errors":[{"errorCode":"err.func.400","errorMessage":"{\"errors\":[{\"status\":\"400\",\"code\":\"Future\",\"detail\":\"must be a future date\",\"source\":{\"pointer\":\"/data/attributes/startDateTime\"}}]}"}],"error_reference":"FUNCTIONAL"} diff --git a/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.txt b/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.txt deleted file mode 100644 index a54db330..00000000 --- a/tests/fixtures/kamereon/vehicle_actions/hvac-start.start.txt +++ /dev/null @@ -1 +0,0 @@ -{"data":{"type":"HvacStart","id":"guid","attributes":{"action":"start","targetTemperature":21.0}}} diff --git a/tests/fixtures/kamereon/vehicle_data/battery-status.1.json b/tests/fixtures/kamereon/vehicle_data/battery-status.1.json new file mode 100644 index 00000000..750d0081 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/battery-status.1.json @@ -0,0 +1,15 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { + "timestamp": "2020-11-17T09:06:48+01:00", + "batteryLevel": 50, + "batteryAutonomy": 128, + "batteryCapacity": 0, + "batteryAvailableEnergy": 0, + "plugStatus": 0, + "chargingStatus": -1.0 + } + } +} diff --git a/tests/fixtures/kamereon/vehicle_data/battery-status.2.json b/tests/fixtures/kamereon/vehicle_data/battery-status.2.json new file mode 100644 index 00000000..aaf9c7a6 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/battery-status.2.json @@ -0,0 +1,16 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { + "timestamp": "2020-11-09T07:41:49+01:00", + "batteryLevel": 34, + "batteryTemperature": 10, + "batteryAutonomy": 88, + "batteryCapacity": 0, + "batteryAvailableEnergy": 0, + "plugStatus": 0, + "chargingStatus": -1.0 + } + } +} diff --git a/tests/fixtures/kamereon/vehicle_data/charge-history.json b/tests/fixtures/kamereon/vehicle_data/charge-history.json new file mode 100644 index 00000000..0a4fbd67 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/charge-history.json @@ -0,0 +1,16 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { + "chargeSummaries": [ + { + "month": "202011", + "totalChargesNumber": 1, + "totalChargesDuration": 479, + "totalChargesErrors": 0 + } + ] + } + } +} diff --git a/tests/fixtures/kamereon/vehicle_data/charges.json b/tests/fixtures/kamereon/vehicle_data/charges.json new file mode 100644 index 00000000..fc2c9a3c --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/charges.json @@ -0,0 +1,21 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { + "charges": [ + { + "chargeStartDate": "2020-11-11T00:31:03Z", + "chargeEndDate": "2020-11-11T08:30:17Z", + "chargeDuration": 479, + "chargeStartBatteryLevel": 15, + "chargeEndBatteryLevel": 74, + "chargeBatteryLevelRecovered": 59, + "chargePower": "slow", + "chargeStartInstantaneousPower": 3100, + "chargeEndStatus": "ok" + } + ] + } + } +} diff --git a/tests/fixtures/kamereon/vehicle_data/cockpit.json b/tests/fixtures/kamereon/vehicle_data/cockpit.json new file mode 100644 index 00000000..1112ee52 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/cockpit.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { "totalMileage": 49114.27 } + } +} diff --git a/tests/fixtures/kamereon/vehicle_data/hvac-status.json b/tests/fixtures/kamereon/vehicle_data/hvac-status.json new file mode 100644 index 00000000..f48cbae6 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_data/hvac-status.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "Car", + "id": "VF1AAAAA555777999", + "attributes": { "externalTemperature": 8.0, "hvacStatus": "off" } + } +} diff --git a/tests/fixtures/kamereon/vehicle_error/invalid_date.json b/tests/fixtures/kamereon/vehicle_error/invalid_date.json new file mode 100644 index 00000000..71466f63 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_error/invalid_date.json @@ -0,0 +1,16 @@ +{ + "type": "FUNCTIONAL", + "messages": [ + { + "code": "err.func.400", + "message": "{\"errors\":[{\"status\":\"400\",\"code\":\"Future\",\"detail\":\"must be a future date\",\"source\":{\"pointer\":\"/data/attributes/startDateTime\"}}]}" + } + ], + "errors": [ + { + "errorCode": "err.func.400", + "errorMessage": "{\"errors\":[{\"status\":\"400\",\"code\":\"Future\",\"detail\":\"must be a future date\",\"source\":{\"pointer\":\"/data/attributes/startDateTime\"}}]}" + } + ], + "error_reference": "FUNCTIONAL" +} diff --git a/tests/fixtures/kamereon/vehicle_error/invalid_upstream.json b/tests/fixtures/kamereon/vehicle_error/invalid_upstream.json new file mode 100644 index 00000000..03e85b4b --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_error/invalid_upstream.json @@ -0,0 +1,16 @@ +{ + "type": "TECHNICAL", + "messages": [ + { + "code": "err.tech.500", + "message": "{\"errors\":[{\"status\":\"Internal Server Error\",\"code\":\"500\",\"title\":\"Invalid response from the upstream server (The request sent to the GDC is erroneous) ; 502 Bad Gateway\"}]}" + } + ], + "errors": [ + { + "errorCode": "err.tech.500", + "errorMessage": "{\"errors\":[{\"status\":\"Internal Server Error\",\"code\":\"500\",\"title\":\"Invalid response from the upstream server (The request sent to the GDC is erroneous) ; 502 Bad Gateway\"}]}" + } + ], + "error_reference": "TECHNICAL" +} diff --git a/tests/fixtures/kamereon/vehicle_error/not_supported.json b/tests/fixtures/kamereon/vehicle_error/not_supported.json new file mode 100644 index 00000000..094adce5 --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_error/not_supported.json @@ -0,0 +1,16 @@ +{ + "type": "TECHNICAL", + "messages": [ + { + "code": "err.tech.501", + "message": "{\"errors\":[{\"status\":\"501\",\"code\":\"error.internal\",\"detail\":\"This feature is not technically supported by this gateway\"}]}" + } + ], + "errors": [ + { + "errorCode": "err.tech.501", + "errorMessage": "{\"errors\":[{\"status\":\"501\",\"code\":\"error.internal\",\"detail\":\"This feature is not technically supported by this gateway\"}]}" + } + ], + "error_reference": "TECHNICAL" +} diff --git a/tests/fixtures/kamereon/vehicle_error/quota_limit.json b/tests/fixtures/kamereon/vehicle_error/quota_limit.json new file mode 100644 index 00000000..7de7a4ef --- /dev/null +++ b/tests/fixtures/kamereon/vehicle_error/quota_limit.json @@ -0,0 +1,16 @@ +{ + "type": "FUNCTIONAL", + "messages": [ + { + "code": "err.func.wired.overloaded", + "message": "You have reached your quota limit" + } + ], + "errors": [ + { + "errorCode": "err.func.wired.overloaded", + "errorMessage": "You have reached your quota limit", + "errorType": "functional" + } + ] +} diff --git a/tests/fixtures/kamereon/vehicles/zoe.1.json b/tests/fixtures/kamereon/vehicles/zoe.1.json new file mode 100644 index 00000000..e0f964ca --- /dev/null +++ b/tests/fixtures/kamereon/vehicles/zoe.1.json @@ -0,0 +1,138 @@ +{ + "accountId": "account-id-1", + "country": "FR", + "vehicleLinks": [ + { + "brand": "RENAULT", + "vin": "VF1AAAAA555777999", + "status": "ACTIVE", + "linkType": "OWNER", + "garageBrand": "RENAULT", + "annualMileage": 16000, + "mileage": 26464, + "startDate": "2017-08-07", + "createdDate": "2019-05-23T21:38:16.409008Z", + "lastModifiedDate": "2020-11-17T08:41:40.497400Z", + "ownershipStartDate": "2017-08-01", + "cancellationReason": {}, + "connectedDriver": { + "role": "MAIN_DRIVER", + "createdDate": "2019-06-17T09:49:06.880627Z", + "lastModifiedDate": "2019-06-17T09:49:06.880627Z" + }, + "vehicleDetails": { + "vin": "VF1AAAAA555777999", + "registrationDate": "2017-08-01", + "firstRegistrationDate": "2017-08-01", + "engineType": "5AQ", + "engineRatio": "601", + "modelSCR": "ZOE", + "deliveryCountry": { "code": "FR", "label": "FRANCE" }, + "family": { "code": "X10", "label": "FAMILLE X10", "group": "007" }, + "tcu": { "code": "TCU0G2", "label": "TCU VER 0 GEN 2", "group": "E70" }, + "navigationAssistanceLevel": { + "code": "NAV3G5", + "label": "LEVEL 3 TYPE 5 NAVIGATION", + "group": "408" + }, + "battery": { + "code": "BT4AR1", + "label": "BATTERIE BT4AR1", + "group": "968" + }, + "radioType": { "code": "RAD37A", "label": "RADIO 37A", "group": "425" }, + "registrationCountry": { "code": "FR" }, + "brand": { "label": "RENAULT" }, + "model": { "code": "X101VE", "label": "ZOE", "group": "971" }, + "gearbox": { + "code": "BVEL", + "label": "BOITE A VARIATEUR ELECTRIQUE", + "group": "427" + }, + "version": { "code": "INT MB 10R" }, + "energy": { "code": "ELEC", "label": "ELECTRIQUE", "group": "019" }, + "registrationNumber": "REG-NUMBER", + "vcd": "SYTINC/SKTPOU/SAND41/FDIU1/SSESM/MAPSUP/SSCALL/SAND88/SAND90/SQKDRO/SDIFPA/FACBA2/PRLEX1/SSRCAR/CABDO2/TCU0G2/SWALBO/EVTEC1/STANDA/X10/B10/EA2/MB/ELEC/DG/TEMP/TR4X2/RV/ABS/CAREG/LAC/VT003/CPE/RET03/SPROJA/RALU16/CEAVRH/AIRBA1/SERIE/DRA/DRAP08/HARM02/ATAR/TERQG/SFBANA/KM/DPRPN/AVREPL/SSDECA/ASRESP/RDAR02/ALEVA/CACBL2/SOP02C/CTHAB2/TRNOR/LVAVIP/LVAREL/SASURV/KTGREP/SGSCHA/APL03/ALOUCC/CMAR3P/NAV3G5/RAD37A/BVEL/AUTAUG/RNORM/ISOFIX/EQPEUR/HRGM01/SDPCLV/TLFRAN/SPRODI/SAN613/SSAPEX/GENEV1/ELC1/SANCML/PE2012/PHAS1/SAN913/045KWH/BT4AR1/VEC153/X101VE/NBT017/5AQ", + "assets": [ + { + "assetType": "PICTURE", + "renditions": [ + { + "resolutionType": "ONE_MYRENAULT_LARGE", + "url": "https://3dv2.renault.com/ImageFromBookmark?configuration=SKTPOU%2FPRLEX1%2FSTANDA%2FB10%2FEA2%2FDG%2FVT003%2FRET03%2FRALU16%2FDRAP08%2FHARM02%2FTERQG%2FRDAR02%2FALEVA%2FSOP02C%2FTRNOR%2FLVAVIP%2FLVAREL%2FNAV3G5%2FRAD37A%2FSDPCLV%2FTLFRAN%2FGENEV1%2FSAN913%2FBT4AR1%2FNBT017&databaseId=1d514feb-93a6-4b45-8785-e11d2a6f1864&bookmarkSet=RSITE&bookmark=EXT_34_DESSUS&profile=HELIOS_OWNERSERVICES_LARGE" + }, + { + "resolutionType": "ONE_MYRENAULT_SMALL", + "url": "https://3dv2.renault.com/ImageFromBookmark?configuration=SKTPOU%2FPRLEX1%2FSTANDA%2FB10%2FEA2%2FDG%2FVT003%2FRET03%2FRALU16%2FDRAP08%2FHARM02%2FTERQG%2FRDAR02%2FALEVA%2FSOP02C%2FTRNOR%2FLVAVIP%2FLVAREL%2FNAV3G5%2FRAD37A%2FSDPCLV%2FTLFRAN%2FGENEV1%2FSAN913%2FBT4AR1%2FNBT017&databaseId=1d514feb-93a6-4b45-8785-e11d2a6f1864&bookmarkSet=RSITE&bookmark=EXT_34_DESSUS&profile=HELIOS_OWNERSERVICES_SMALL_V2" + } + ] + }, + { + "assetType": "PDF", + "assetRole": "GUIDE", + "title": "PDF Guide", + "description": "", + "renditions": [ + { + "url": "https://cdn.group.renault.com/ren/gb/myr/assets/x101ve/manual.pdf.asset.pdf/1558704861676.pdf" + } + ] + }, + { + "assetType": "URL", + "assetRole": "GUIDE", + "title": "e-guide", + "description": "", + "renditions": [{ "url": "http://gb.e-guide.renault.com/eng/Zoe" }] + }, + { + "assetType": "VIDEO", + "assetRole": "CAR", + "title": "10 Fundamentals about getting the best out of your electric vehicle", + "description": "", + "renditions": [{ "url": "39r6QEKcOM4" }] + }, + { + "assetType": "VIDEO", + "assetRole": "CAR", + "title": "Automatic Climate Control", + "description": "", + "renditions": [{ "url": "Va2FnZFo_GE" }] + }, + { + "assetType": "URL", + "assetRole": "CAR", + "title": "More videos", + "description": "", + "renditions": [ + { "url": "https://www.youtube.com/watch?v=wfpCMkK1rKI" } + ] + }, + { + "assetType": "VIDEO", + "assetRole": "CAR", + "title": "Charging the battery", + "description": "", + "renditions": [{ "url": "RaEad8DjUJs" }] + }, + { + "assetType": "VIDEO", + "assetRole": "CAR", + "title": "Charging the battery at a station with a flap", + "description": "", + "renditions": [{ "url": "zJfd7fJWtr0" }] + } + ], + "yearsOfMaintenance": 12, + "connectivityTechnology": "RLINK1", + "easyConnectStore": false, + "electrical": true, + "rlinkStore": false, + "deliveryDate": "2017-08-11", + "retrievedFromDhs": false, + "engineEnergyType": "ELEC", + "radioCode": "0738" + } + } + ] +} diff --git a/tests/model/test_kamereon.py b/tests/model/test_kamereon.py new file mode 100644 index 00000000..5401a2dc --- /dev/null +++ b/tests/model/test_kamereon.py @@ -0,0 +1,178 @@ +"""Tests for RenaultClient.""" +import os +from typing import Any +from typing import Type + +import pytest +from marshmallow.schema import Schema + +from renault_api.exceptions import KamereonResponseException +from renault_api.model.kamereon import KamereonPersonResponse +from renault_api.model.kamereon import KamereonPersonResponseSchema +from renault_api.model.kamereon import KamereonVehicleDataResponse +from renault_api.model.kamereon import KamereonVehicleDataResponseSchema +from renault_api.model.kamereon import KamereonVehiclesResponse +from renault_api.model.kamereon import KamereonVehiclesResponseSchema + + +def get_response_content(path: str, schema: Type[Schema]) -> Any: + """Read fixture text file as string.""" + with open(f"tests/fixtures/kamereon/{path}", "r") as file: + content = file.read() + return schema.loads(content) + + +def test_person_response() -> None: + """Test person details response.""" + response: KamereonPersonResponse = get_response_content( + "person.json", KamereonPersonResponseSchema + ) + response.raise_for_error_code() + assert response.accounts[0].accountId == "account-id-1" + assert response.accounts[0].accountType == "MYRENAULT" + assert response.accounts[0].accountStatus == "ACTIVE" + + assert response.accounts[1].accountId == "account-id-2" + assert response.accounts[1].accountType == "SFDC" + assert response.accounts[1].accountStatus == "ACTIVE" + + +def test_vehicles_response() -> None: + """Test vehicles list response.""" + directory = "tests/fixtures/kamereon/vehicles" + for file in os.listdir(directory): + filename = os.fsdecode(file) + response: KamereonVehiclesResponse = get_response_content( + f"vehicles/{filename}", KamereonVehiclesResponseSchema + ) + response.raise_for_error_code() + # Ensure the account id is hidden + assert response.accountId.startswith("account-id") + for vehicle_link in response.vehicleLinks: + # Ensure the VIN is hidden + assert vehicle_link.vin.startswith("VF1AAAAA555777") + vehicle_details = vehicle_link.raw_data["vehicleDetails"] + assert vehicle_details["vin"].startswith("VF1AAAAA555777") + assert vehicle_details["registrationNumber"].startswith("REG-NUMBER") + + +def test_vehicle_data_response() -> None: + """Test vehicle data response.""" + directory = "tests/fixtures/kamereon/vehicle_data" + for file in os.listdir(directory): + filename = os.fsdecode(file) + response: KamereonVehicleDataResponse = get_response_content( + f"vehicle_data/{filename}", KamereonVehicleDataResponseSchema + ) + response.raise_for_error_code() + # Ensure the VIN is hidden + assert response.data.id.startswith("VF1AAAAA555777") + + +def test_vehicle_data_response_attributes() -> None: + """Test vehicle data response attributes.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_data/battery-status.1.json", KamereonVehicleDataResponseSchema + ) + response.raise_for_error_code() + assert response.data.raw_data["attributes"] == { + "timestamp": "2020-11-17T09:06:48+01:00", + "batteryLevel": 50, + "batteryAutonomy": 128, + "batteryCapacity": 0, + "batteryAvailableEnergy": 0, + "plugStatus": 0, + "chargingStatus": -1.0, + } + + +def test_vehicle_action_response() -> None: + """Test vehicle action response.""" + directory = "tests/fixtures/kamereon/vehicle_action" + for file in os.listdir(directory): + filename = os.fsdecode(file) + response: KamereonVehicleDataResponse = get_response_content( + f"vehicle_action/{filename}", KamereonVehicleDataResponseSchema + ) + response.raise_for_error_code() + # Ensure the guid is hidden + assert response.data.id == "guid" + + +def test_vehicle_action_response_attributes() -> None: + """Test vehicle action response attributes.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_action/hvac-start.start.json", KamereonVehicleDataResponseSchema + ) + response.raise_for_error_code() + assert response.data.raw_data["attributes"] == { + "action": "start", + "targetTemperature": 21.0, + } + + +def test_vehicle_error_response() -> None: + """Test vehicle error response.""" + directory = "tests/fixtures/kamereon/vehicle_error" + for file in os.listdir(directory): + filename = os.fsdecode(file) + response: KamereonVehicleDataResponse = get_response_content( + f"vehicle_error/{filename}", KamereonVehicleDataResponseSchema + ) + with pytest.raises(KamereonResponseException): + response.raise_for_error_code() + assert response.errors is not None + + +def test_vehicle_error_quota_limit() -> None: + """Test vehicle quota_limit response.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_error/quota_limit.json", KamereonVehicleDataResponseSchema + ) + with pytest.raises(KamereonResponseException) as excinfo: + response.raise_for_error_code() + assert excinfo.value.error_code == "err.func.wired.overloaded" + assert excinfo.value.error_details == "You have reached your quota limit" + + +def test_vehicle_error_invalid_date() -> None: + """Test vehicle invalid_date response.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_error/invalid_date.json", KamereonVehicleDataResponseSchema + ) + with pytest.raises(KamereonResponseException) as excinfo: + response.raise_for_error_code() + assert excinfo.value.error_code == "err.func.400" + assert ( + excinfo.value.error_details + == "/data/attributes/startDateTime must be a future date" + ) + + +def test_vehicle_error_invalid_upstream() -> None: + """Test vehicle invalid_upstream response.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_error/invalid_upstream.json", KamereonVehicleDataResponseSchema + ) + with pytest.raises(KamereonResponseException) as excinfo: + response.raise_for_error_code() + assert excinfo.value.error_code == "err.tech.500" + assert ( + excinfo.value.error_details + == "Invalid response from the upstream server (The request sent to the GDC" + " is erroneous) ; 502 Bad Gateway" + ) + + +def test_vehicle_error_not_supported() -> None: + """Test vehicle not_supported response.""" + response: KamereonVehicleDataResponse = get_response_content( + "vehicle_error/not_supported.json", KamereonVehicleDataResponseSchema + ) + with pytest.raises(KamereonResponseException) as excinfo: + response.raise_for_error_code() + assert excinfo.value.error_code == "err.tech.501" + assert ( + excinfo.value.error_details + == "This feature is not technically supported by this gateway" + )