Skip to content

Commit

Permalink
Write tests for secret module
Browse files Browse the repository at this point in the history
  • Loading branch information
paulfioravanti committed Aug 25, 2024
1 parent aa55f72 commit a24968d
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 3 deletions.
4 changes: 2 additions & 2 deletions plover_1password/secret/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ async def resolve(service_account_token: str, secret_reference: str) -> str:
Resolves a single secret from a secret reference URI.
"""
if not secret_reference:
raise ValueError("Secret reference cannot be blank")
raise ValueError("Secret Reference cannot be blank")

try:
client: Client = await _init_client(service_account_token)
secret: str = await client.secrets.resolve(secret_reference)
except Exception as exc: # pylint: disable=broad-except
error.handle_ffi_error(exc)
raise exc
raise ValueError(str(exc)) from exc

return secret

Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
asyncio_mode=auto
Empty file added test/secret/__init__.py
Empty file.
151 changes: 151 additions & 0 deletions test/secret/test_secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import pytest
from unittest.mock import AsyncMock

from plover_1password import secret


@pytest.fixture()
def mock_client(mocker):
async_mock = AsyncMock()
mocker.patch(
"onepassword.client.Client.authenticate",
return_value=async_mock
)
return async_mock

# REF: https://stackoverflow.com/questions/8294618/define-a-lambda-expression-that-raises-an-exception
def raise_(exc):
raise exc

async def test_blank_secret_reference():
with pytest.raises(
ValueError,
match="Secret Reference cannot be blank"
):
await secret.resolve("service_account_token", "")

async def test_service_account_token_invalid(monkeypatch):
monkeypatch.setattr(
"onepassword.client.Client.authenticate",
lambda *args, **kwargs: raise_(Exception(
"invalid service account token, please make sure you provide a "
"valid service account token as parameter: service account "
"deserialization failed, please create another token"
))
)

with pytest.raises(
ValueError,
match=(
"Service Account Token is invalid. "
"Create another token and restart Plover."
)
):
await secret.resolve("service_account_token", "secret_reference")

async def test_service_account_token_invalid_format(monkeypatch):
monkeypatch.setattr(
"onepassword.client.Client.authenticate",
lambda *args, **kwargs: raise_(Exception(
"invalid user input: encountered the following errors: "
"service account token had invalid format"
))
)

with pytest.raises(
ValueError,
match=(
"Service Account Token has invalid format. "
"Fix token format or create a new one and restart Plover."
)
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_invalid_format(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"secret reference has invalid format - "
"must be \"op://<vault>/<item>/[section/]field\""
)

with pytest.raises(
ValueError,
match=(
"Secret Reference has invalid format. "
"URI must be \"op://<vault>/<item>/\\[section/\\]field\""
)
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_missing_prefix(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"secret reference is not prefixed with \"op://\""
)

with pytest.raises(
ValueError,
match="Secret Reference needs to be prefixed with \"op://\""
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_vault_not_found(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"no vault matched the secret reference query"
)

with pytest.raises(
ValueError,
match="Vault specified in Secret Reference not found."
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_item_not_found(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"no item matched the secret reference query"
)

with pytest.raises(
ValueError,
match="Item specified in Secret Reference not found."
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_section_not_found(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"no section matched the secret reference query"
)

with pytest.raises(
ValueError,
match="Section specified in Secret Reference not found."
):
await secret.resolve("service_account_token", "secret_reference")

async def test_secret_reference_field_not_found(mock_client):
mock_client.secrets.resolve.side_effect = Exception(
"error resolving secret reference: "
"the specified field cannot be found within the item"
)

with pytest.raises(
ValueError,
match="Field specified Secret Reference not found."
):
await secret.resolve("service_account_token", "secret_reference")

async def test_unexpected_exception(mock_client):
mock_client.secrets.resolve.side_effect = Exception("Some exception")

with pytest.raises(ValueError, match="Some exception"):
await secret.resolve("service_account_token", "secret_reference")

async def test_successful_secret_retrieval(mock_client):
mock_client.secrets.resolve.return_value = "secret"
remote_secret = await secret.resolve(
"service_account_token", "secret_reference"
)
assert remote_secret == "secret"
5 changes: 4 additions & 1 deletion test/service_account/test_service_account.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os
import pytest

# NOTE: All the monkeypatching needed for these tests resulted in needing
# to run the tests directly on the token module, rather than the service_account
# interface.
from plover_1password.service_account import token

class MockPopen:
Expand All @@ -12,7 +15,7 @@ def read(self):

# NOTE: It does not seem to be possible to monkeypatch over an existing
# $OP_SERVICE_ACCOUNT_TOKEN env var, so instead change the name of the env
# var the get_token() function attempts to fetch to $TOKEN, and then mock
# var the get_token() function attempts to fetch to be $TOKEN, and then mock
# that value. This will only then work when calling token.get_token(), and
# not service_account.get_token(), so it's not possible to test from the
# service_account interface.
Expand Down

0 comments on commit a24968d

Please sign in to comment.