Пример #1
0
def test_file_store_expired_token() -> None:
    """Test loading expired token from credential store."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        # Prepare initial store
        expired_token = get_jwt(timedelta(seconds=-900))

        old_filename = f"{tmpdirname}/.credentials/renault-api.json"
        old_credential_store = FileCredentialStore(old_filename)
        test_key = "key"
        test_value = Credential("value")
        test_jwt_key = "gigya_jwt"
        test_jwt_value = Credential(expired_token)  # bypass JWTCredential
        old_credential_store[test_key] = test_value
        old_credential_store[test_jwt_key] = test_jwt_value

        assert test_key in old_credential_store
        assert old_credential_store.get(test_key) == test_value
        assert old_credential_store[test_key] == test_value
        assert test_jwt_key in old_credential_store
        assert old_credential_store.get(test_jwt_key) == test_jwt_value
        assert old_credential_store[test_jwt_key] == test_jwt_value

        # Copy the data into new file
        new_filename = f"{tmpdirname}/.credentials/renault-api-copy.json"
        copyfile(old_filename, new_filename)
        new_credential_store = FileCredentialStore(new_filename)

        # Check that the data is in the new store
        assert test_key in new_credential_store
        assert new_credential_store.get(test_key) == test_value
        assert new_credential_store[test_key] == test_value

        # Except the JWT token which was rejected on load
        assert test_jwt_key not in new_credential_store
Пример #2
0
def get_logged_in_credential_store() -> CredentialStore:
    """Get credential store initialised with Gigya credentials."""
    credential_store = CredentialStore()
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(get_jwt())
    return credential_store
Пример #3
0
def test_clear_keys() -> None:
    """Test clearance of specified keys from credential store."""
    credential_store = CredentialStore()
    test_key = "test"
    test_permanent_key = "locale"

    # Try to get value from empty store
    assert test_key not in credential_store
    assert test_permanent_key not in credential_store

    # Set value
    test_value = Credential("test_value")
    test_pemanent_value = Credential("test_locale")
    credential_store[test_key] = test_value
    credential_store[test_permanent_key] = test_pemanent_value

    # Try to get values from filled store
    assert test_key in credential_store
    assert credential_store[test_key] == test_value
    assert test_permanent_key in credential_store
    assert credential_store[test_permanent_key] == test_pemanent_value

    # Clear the store
    credential_store.clear_keys([test_permanent_key])

    # Try to get values from filled store
    assert test_key in credential_store
    assert test_permanent_key not in credential_store
Пример #4
0
def test_http_get(mocked_responses: aioresponses,
                  cli_runner: CliRunner) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[CONF_ACCOUNT_ID] = Credential(TEST_ACCOUNT_ID)
    credential_store[CONF_VIN] = Credential(TEST_VIN)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_get_charging_settings(mocked_responses, "single")

    endpoint = ("/commerce/v1/accounts/{account_id}"
                "/kamereon/kca/car-adapter"
                "/v1/cars/{vin}/charging-settings")
    result = cli_runner.invoke(
        __main__.main,
        f"http get {endpoint}",
    )
    assert result.exit_code == 0, result.exception

    expected_output = (
        "{'data': {'type': 'Car', 'id': 'VF1AAAAA555777999', 'attributes': {"
        "'mode': 'scheduled', 'schedules': ["
        "{'id': 1, 'activated': True, "
        "'monday': {'startTime': 'T12:00Z', 'duration': 15}, "
        "'tuesday': {'startTime': 'T04:30Z', 'duration': 420}, "
        "'wednesday': {'startTime': 'T22:30Z', 'duration': 420}, "
        "'thursday': {'startTime': 'T22:00Z', 'duration': 420}, "
        "'friday': {'startTime': 'T12:15Z', 'duration': 15}, "
        "'saturday': {'startTime': 'T12:30Z', 'duration': 30}, "
        "'sunday': {'startTime': 'T12:45Z', 'duration': 45}}"
        "]}}}\n")
    assert expected_output == result.output
Пример #5
0
def test_http_post_file(tmpdir: Any, mocked_responses: aioresponses,
                        cli_runner: CliRunner) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[CONF_ACCOUNT_ID] = Credential(TEST_ACCOUNT_ID)
    credential_store[CONF_VIN] = Credential(TEST_VIN)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    url = fixtures.inject_set_charge_schedule(mocked_responses, "schedules")

    endpoint = ("/commerce/v1/accounts/{account_id}"
                "/kamereon/kca/car-adapter"
                "/v2/cars/{vin}/actions/charge-schedule")
    body = {
        "data": {
            "type": "ChargeSchedule",
            "attributes": {
                "schedules": []
            }
        }
    }

    json_file = tmpdir.mkdir("json").join("sample.json")
    json_file.write(json.dumps(body))

    result = cli_runner.invoke(
        __main__.main,
        f"http post-file {endpoint} '{json_file}'",
    )
    assert result.exit_code == 0, result.exception

    expected_output = (
        "{'data': {'type': 'ChargeSchedule', 'id': 'guid', "
        "'attributes': {'schedules': ["
        "{'id': 1, 'activated': True, "
        "'tuesday': {'startTime': 'T04:30Z', 'duration': 420}, "
        "'wednesday': {'startTime': 'T22:30Z', 'duration': 420}, "
        "'thursday': {'startTime': 'T22:00Z', 'duration': 420}, "
        "'friday': {'startTime': 'T23:30Z', 'duration': 480}, "
        "'saturday': {'startTime': 'T18:30Z', 'duration': 120}, "
        "'sunday': {'startTime': 'T12:45Z', 'duration': 45}}]}}}\n")
    assert expected_output == result.output

    expected_json = {
        "data": {
            "type": "ChargeSchedule",
            "attributes": {
                "schedules": []
            }
        }
    }

    request: RequestCall = mocked_responses.requests[("POST", URL(url))][0]
    assert expected_json == request.kwargs["json"]
Пример #6
0
def initialise_credential_store(
    include_account_id: Optional[bool] = None,
    include_vin: Optional[bool] = None,
) -> None:
    """Initialise CLI credential store."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(get_jwt())
    if include_account_id:
        credential_store[CONF_ACCOUNT_ID] = Credential(TEST_ACCOUNT_ID)
    if include_vin:
        credential_store[CONF_VIN] = Credential(TEST_VIN)
Пример #7
0
def test_vehicle_status_no_prompt(mocked_responses: aioresponses,
                                  cli_runner: CliRunner) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_vehicle_status(mocked_responses)

    result = cli_runner.invoke(
        __main__.main, f"--account {TEST_ACCOUNT_ID} --vin {TEST_VIN} status")
    assert result.exit_code == 0, result.exception

    assert EXPECTED_BATTERY_STATUS == result.output
Пример #8
0
async def get_locale(websession: aiohttp.ClientSession,
                     ctx_data: Dict[str, Any]) -> str:
    """Prompt the user for locale."""
    credential_store: CredentialStore = ctx_data["credential_store"]
    locale = credential_store.get_value(CONF_LOCALE)
    if locale:
        return locale

    default_locale = getdefaultlocale()[0]
    while True:
        locale = click.prompt("Please select a locale", default=default_locale)
        if locale:  # pragma: no branch
            try:
                await get_api_keys(locale, websession=websession)
            except RenaultException as exc:  # pragma: no cover
                click.echo(f"Locale `{locale}` is unknown: {exc}", err=True)
            else:
                if click.confirm(
                        "Do you want to save the locale to the credential store?",
                        default=False,
                ):
                    credential_store[CONF_LOCALE] = Credential(locale)
                # Add blank new line
                click.echo("")
                return locale
Пример #9
0
def test_simple_credential() -> None:
    """Test get/set with simple credential."""
    credential_store = CredentialStore()
    test_key = "test"

    # Try to get value from empty store
    assert test_key not in credential_store
    assert not credential_store.get(test_key)
    with pytest.raises(KeyError):
        credential_store[test_key]

    # Set value
    test_value = Credential("test_value")
    credential_store[test_key] = test_value

    # Try to get values from filled store
    assert test_key in credential_store
    assert credential_store.get(test_key) == test_value
    assert credential_store[test_key] == test_value

    # Delete value
    del credential_store[test_key]

    # Try to get values from filled store
    assert test_key not in credential_store
    assert credential_store.get(test_key) is None
    assert credential_store.get_value(test_key) is None
Пример #10
0
def test_file_store() -> None:
    """Test file credential store."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        # Prepare initial store
        old_filename = f"{tmpdirname}/.credentials/renault-api.json"
        old_credential_store = FileCredentialStore(old_filename)
        test_key = "key"
        test_value = Credential("value")
        test_jwt_key = "gigya_jwt"
        test_jwt_value = JWTCredential(get_jwt())
        old_credential_store[test_key] = test_value
        old_credential_store[test_jwt_key] = test_jwt_value

        assert test_key in old_credential_store
        assert old_credential_store.get(test_key) == test_value
        assert old_credential_store[test_key] == test_value
        assert test_jwt_key in old_credential_store
        assert old_credential_store.get(test_jwt_key) == test_jwt_value
        assert old_credential_store[test_jwt_key] == test_jwt_value

        # Copy the data into new file
        new_filename = f"{tmpdirname}/.credentials/renault-api-copy.json"
        copyfile(old_filename, new_filename)
        new_credential_store = FileCredentialStore(new_filename)

        # Check that the data is in the new store
        assert test_key in new_credential_store
        assert new_credential_store.get(test_key) == test_value
        assert new_credential_store[test_key] == test_value
        assert test_jwt_key in new_credential_store
        assert new_credential_store.get(test_jwt_key) == test_jwt_value
        assert new_credential_store[test_jwt_key] == test_jwt_value
Пример #11
0
def test_list_vehicles_no_prompt(mocked_responses: aioresponses,
                                 cli_runner: CliRunner) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_get_vehicles(mocked_responses, "zoe_40.1.json")

    result = cli_runner.invoke(__main__.main,
                               f"--account {TEST_ACCOUNT_ID} vehicles")
    assert result.exit_code == 0, result.exception

    expected_output = ("Registration    Brand    Model    VIN\n"
                       "--------------  -------  -------  -----------------\n"
                       "REG-NUMBER      RENAULT  ZOE      VF1AAAAA555777999\n")
    assert expected_output == result.output
Пример #12
0
def test_invalid_credential() -> None:
    """Test set with invalid types."""
    credential_store = CredentialStore()

    test_key = "test"
    with pytest.raises(TypeError):
        credential_store[test_key] = test_key  # type:ignore

    test_value = Credential("test_value")
    with pytest.raises(TypeError):
        credential_store[test_value] = test_value  # type:ignore
Пример #13
0
def test_vehicle_status(mocked_responses: aioresponses, cli_runner: CliRunner,
                        filename: str) -> None:
    """It exits with a status code of zero."""
    filename = os.path.basename(filename)
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[CONF_ACCOUNT_ID] = Credential(TEST_ACCOUNT_ID)
    credential_store[CONF_VIN] = Credential(TEST_VIN)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_get_vehicle_details(mocked_responses, filename)
    fixtures.inject_get_vehicle_contracts(mocked_responses, "fr_FR.2.json")
    fixtures.inject_vehicle_status(mocked_responses)

    result = cli_runner.invoke(__main__.main, "status")
    assert result.exit_code == 0, result.exception

    assert EXPECTED_STATUS[filename] == result.output
Пример #14
0
async def set_options(
    websession: aiohttp.ClientSession,
    ctx_data: Dict[str, Any],
    locale: Optional[str],
    account: Optional[str],
    vin: Optional[str],
) -> None:
    """Set configuration keys."""
    credential_store: CredentialStore = ctx_data["credential_store"]
    if locale:
        # Ensure API keys are available
        api_keys = await get_api_keys(locale, websession=websession)

        credential_store[CONF_LOCALE] = Credential(locale)
        for k, v in api_keys.items():
            credential_store[k] = Credential(v)

    if account:
        credential_store[CONF_ACCOUNT_ID] = Credential(account)
    if vin:
        credential_store[CONF_VIN] = Credential(vin)
Пример #15
0
def test_vehicle_contracts(mocked_responses: aioresponses,
                           cli_runner: CliRunner) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[CONF_ACCOUNT_ID] = Credential(TEST_ACCOUNT_ID)
    credential_store[CONF_VIN] = Credential(TEST_VIN)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_get_vehicle_contracts(mocked_responses, "fr_FR.1.json")

    result = cli_runner.invoke(__main__.main, "contracts")
    assert result.exit_code == 0, result.exception

    expected_output = (
        "Type                            Code                  "
        "Description                       Start       End         Status\n"
        "------------------------------  --------------------  "
        "--------------------------------  ----------  ----------  "
        "------------------\n"
        "WARRANTY_MAINTENANCE_CONTRACTS  40                    "
        "CONTRAT LOSANGE                   2018-04-04  2022-04-03  Actif\n"
        "CONNECTED_SERVICES              ZECONNECTP            "
        "My Z.E. Connect en série 36 mois  2018-08-23  2021-08-23  Actif\n"
        "CONNECTED_SERVICES              GBA                   "
        "Battery Services                  2018-03-23              "
        "Echec d’activation\n"
        "WARRANTY                        ManufacturerWarranty  "
        "Garantie fabricant                            2020-04-03  Expiré\n"
        "WARRANTY                        PaintingWarranty      "
        "Garantie peinture                             2021-04-03  Actif\n"
        "WARRANTY                        CorrosionWarranty     "
        "Garantie corrosion                            2030-04-03  Actif\n"
        "WARRANTY                        GMPeWarranty          "
        "Garantie GMPe                                 2020-04-03  Expiré\n"
        "WARRANTY                        AssistanceWarranty    "
        "Garantie assistance                           2020-04-03  Expiré\n")
    assert expected_output == result.output
Пример #16
0
def test_list_accounts_no_prompt(
    mocked_responses: aioresponses, cli_runner: CliRunner
) -> None:
    """It exits with a status code of zero."""
    credential_store = FileCredentialStore(os.path.expanduser(CREDENTIAL_PATH))
    credential_store[CONF_LOCALE] = Credential(TEST_LOCALE)
    credential_store[GIGYA_LOGIN_TOKEN] = Credential(TEST_LOGIN_TOKEN)
    credential_store[GIGYA_PERSON_ID] = Credential(TEST_PERSON_ID)
    credential_store[GIGYA_JWT] = JWTCredential(fixtures.get_jwt())

    fixtures.inject_get_person(mocked_responses)

    result = cli_runner.invoke(__main__.main, "accounts")
    assert result.exit_code == 0, result.exception

    expected_output = (
        "Type       ID\n"
        "---------  ------------\n"
        "MYRENAULT  account-id-1\n"
        "SFDC       account-id-2\n"
    )
    assert expected_output == result.output
Пример #17
0
 def _read(self) -> None:
     """Read data from store location."""
     if not os.path.exists(self._store_location):
         return
     with open(self._store_location) as json_file:
         data = json.load(json_file)
         for key, value in data.items():
             if key == "gigya_jwt":
                 try:
                     self[key] = JWTCredential(value)
                 except jwt.ExpiredSignatureError:  # pragma: no cover
                     pass
             else:
                 self[key] = Credential(value)
Пример #18
0
async def _get_account_id(ctx_data: Dict[str, Any], client: RenaultClient) -> str:
    """Prompt the user for account."""
    # First, check context data
    if "account" in ctx_data:
        return str(ctx_data["account"])

    # Second, check credential store
    credential_store: CredentialStore = ctx_data["credential_store"]

    account_id = credential_store.get_value(renault_settings.CONF_ACCOUNT_ID)
    if account_id:
        return account_id

    # Third, prompt the user
    response = await client.get_person()
    if not response.accounts:  # pragma: no cover
        raise RenaultException("No account found.")

    prompt, default = await _get_account_prompt(response.accounts, client)

    while True:
        i = int(
            click.prompt(
                prompt,
                default=default,
                type=click.IntRange(min=1, max=len(response.accounts)),
            )
        )
        try:
            account_id = str(response.accounts[i - 1].accountId)
        except (KeyError, IndexError) as exc:  # pragma: no cover
            click.echo(f"Invalid option: {exc}.", err=True)
        else:
            if click.confirm(  # pragma: no branch
                "Do you want to save the account ID to the credential store?",
                default=False,
            ):
                credential_store[renault_settings.CONF_ACCOUNT_ID] = Credential(
                    account_id
                )
            # Add blank new line
            click.echo("")
            return account_id
Пример #19
0
async def _get_vin(ctx_data: Dict[str, Any], account: RenaultAccount) -> str:
    """Prompt the user for vin."""
    # First, check context data
    if "vin" in ctx_data:
        return str(ctx_data["vin"])

    # Second, check credential store
    credential_store: CredentialStore = ctx_data["credential_store"]

    vin = credential_store.get_value(renault_settings.CONF_VIN)
    if vin:
        return vin

    # Third, prompt the user
    response = await account.get_vehicles()
    if not response.vehicleLinks:  # pragma: no cover
        raise RenaultException("No vehicle found.")

    prompt, default = await _get_vehicle_prompt(response.vehicleLinks, account)

    while True:
        i = int(
            click.prompt(
                prompt,
                default=default,
                type=click.IntRange(min=1, max=len(response.vehicleLinks)),
            )
        )
        try:
            vin = str(response.vehicleLinks[i - 1].vin)
        except (KeyError, IndexError) as exc:  # pragma: no cover
            click.echo(f"Invalid option: {exc}.", err=True)
        else:
            if click.confirm(  # pragma: no branch
                "Do you want to save the VIN to the credential store?",
                default=False,
            ):
                credential_store[renault_settings.CONF_VIN] = Credential(vin)
            click.echo("")
            return vin
Пример #20
0
def test_simple_credential() -> None:
    """Test for Credential class."""
    credential = Credential(TEST_VALUE)

    assert credential.value == TEST_VALUE
    assert not credential.has_expired()