v1.1.0
This commit is contained in:
parent
19c8d10645
commit
6eca8fcc46
|
@ -1,5 +1,12 @@
|
|||
## Keep It Secret Changelog
|
||||
|
||||
#### v1.1.0 (2024-01-18)
|
||||
|
||||
* `Secrets.resolve_dependency()` API for resolving field dependencies.
|
||||
* AWS Secrets Manager client management moved from `AWSSecretsManagerField` to
|
||||
`AWSSecrets`.
|
||||
* Docs tweaked.
|
||||
|
||||
#### v1.0.0 (2024-01-04)
|
||||
|
||||
* Initial public release.
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
project = 'Keep It Secret'
|
||||
copyright = '2023-present Tomek Wójcik'
|
||||
author = 'Tomek Wójcik'
|
||||
version = '1.0.0'
|
||||
version = '1.1.0'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '1.0.0'
|
||||
release = '1.1.0'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
@ -30,5 +30,8 @@ exclude_patterns = []
|
|||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme = 'furo'
|
||||
html_static_path = ['_static']
|
||||
|
||||
# -- Options for autodoc -----------------------------------------------------
|
||||
autodoc_member_order = 'bysource'
|
||||
|
|
|
@ -6,7 +6,7 @@ from .fields import ( # noqa: F401
|
|||
)
|
||||
from .secrets import Secrets # noqa: F401
|
||||
|
||||
__version__ = '1.0.0'
|
||||
__version__ = '1.1.0'
|
||||
|
||||
__all__ = [
|
||||
'AbstractField',
|
||||
|
|
|
@ -57,6 +57,11 @@ class AWSSecrets(Secrets):
|
|||
:type: ``str | None``
|
||||
"""
|
||||
|
||||
def __init__(self, parent: Secrets | None = None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.client: PAWSSecretsManagerClient | None = None
|
||||
|
||||
def as_boto3_client_kwargs(self) -> dict[str, typing.Any]:
|
||||
"""
|
||||
Return representation of the mapped variables for use in
|
||||
|
@ -78,6 +83,15 @@ class AWSSecrets(Secrets):
|
|||
|
||||
return result
|
||||
|
||||
def get_client(self) -> PAWSSecretsManagerClient:
|
||||
if self.client is None:
|
||||
self.client = boto3.client(
|
||||
'secretsmanager',
|
||||
**self.as_boto3_client_kwargs(),
|
||||
)
|
||||
|
||||
return self.client
|
||||
|
||||
|
||||
class AWSSecretsManagerField(Field):
|
||||
"""
|
||||
|
@ -87,7 +101,7 @@ class AWSSecretsManagerField(Field):
|
|||
:param secret_id: ID of the secret to fetch.
|
||||
:param default: Default value. Defaults to ``None``.
|
||||
:param decoder: A callable to decode the fetched value. Defaults to
|
||||
``json.loads``.
|
||||
:py:func:`json.loads`.
|
||||
"""
|
||||
def __init__(self,
|
||||
secret_id: str,
|
||||
|
@ -100,8 +114,6 @@ class AWSSecretsManagerField(Field):
|
|||
self.default = default
|
||||
self.decoder = decoder
|
||||
|
||||
self.client: PAWSSecretsManagerClient | None = None
|
||||
|
||||
@classmethod
|
||||
def new(cls: type[AWSSecretsManagerField], # type: ignore[override]
|
||||
secret_id: str,
|
||||
|
@ -110,33 +122,25 @@ class AWSSecretsManagerField(Field):
|
|||
**field_options) -> AWSSecretsManagerField:
|
||||
return cls(secret_id, default=default, decoder=decoder, **field_options)
|
||||
|
||||
def get_client(self, secrets: Secrets) -> PAWSSecretsManagerClient:
|
||||
if self.client is None:
|
||||
aws_secrets = typing.cast(AWSSecrets, secrets.aws) # type: ignore[attr-defined]
|
||||
|
||||
self.client = boto3.client(
|
||||
'secretsmanager',
|
||||
**aws_secrets.as_boto3_client_kwargs(),
|
||||
)
|
||||
|
||||
return self.client
|
||||
|
||||
def get_value(self, secrets: Secrets) -> typing.Any:
|
||||
"""
|
||||
Retrieve, decode and return the secret specified by *secret_id*.
|
||||
|
||||
Depends on :py:class:`AWSSecrets` to be declared in ``secrets.aws``
|
||||
field.
|
||||
Depends on :py:class:`AWSSecrets` to be declared in ``aws`` field on
|
||||
``secrets`` or one of its parents.
|
||||
|
||||
:raises DependencyMissing: Signal that ``secrets.aws`` field is
|
||||
missing.
|
||||
:raises RequiredValueMissing: Signal the field's value is required but
|
||||
*secret_id* is not present in the Secrets Manager.
|
||||
"""
|
||||
if hasattr(secrets, 'aws') is False:
|
||||
aws_secrets: AWSSecrets = secrets.resolve_dependency(
|
||||
'aws', include_parents=True,
|
||||
)
|
||||
if aws_secrets is secrets.UNRESOLVED_DEPENDENCY:
|
||||
raise self.DependencyMissing('aws')
|
||||
|
||||
client = self.get_client(secrets)
|
||||
client = aws_secrets.get_client()
|
||||
|
||||
try:
|
||||
secret = client.get_secret_value(SecretId=self.secret_id)
|
||||
|
|
|
@ -95,6 +95,10 @@ class Secrets(metaclass=SecretsBase):
|
|||
|
||||
:param parent: The parent :py:class:`Secrets` subclass or ``None``.
|
||||
"""
|
||||
|
||||
#: Sentinel for unresolved dependency.
|
||||
UNRESOLVED_DEPENDENCY: list[typing.Any] = []
|
||||
|
||||
__secrets_fields__: TFields
|
||||
|
||||
def __init__(self, parent: Secrets | None = None):
|
||||
|
@ -104,3 +108,24 @@ class Secrets(metaclass=SecretsBase):
|
|||
|
||||
for field_name, field in self.__secrets_fields__.items():
|
||||
self.__secrets_data__[field_name] = getattr(self, field_name)
|
||||
|
||||
def resolve_dependency(self,
|
||||
name: str,
|
||||
*,
|
||||
include_parents: bool = True) -> typing.Any:
|
||||
"""
|
||||
Resolve a dependency field identified by *name* and return its value.
|
||||
returns :py:attr:`keep_it_secret.Secrets.UNRESOLVED_DEPENDENCY` if
|
||||
the value can't be resolved.
|
||||
|
||||
:param include_parents: Recursively include parents, if any.
|
||||
"""
|
||||
if name in self.__secrets_fields__:
|
||||
return getattr(self, name)
|
||||
|
||||
if include_parents is True and self.__secrets_parent__ is not None:
|
||||
return self.__secrets_parent__.resolve_dependency(
|
||||
name, include_parents=include_parents,
|
||||
)
|
||||
|
||||
return self.UNRESOLVED_DEPENDENCY
|
||||
|
|
83
poetry.lock
generated
83
poetry.lock
generated
|
@ -43,6 +43,27 @@ files = [
|
|||
[package.extras]
|
||||
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "beautifulsoup4"
|
||||
version = "4.12.3"
|
||||
description = "Screen-scraping library"
|
||||
optional = false
|
||||
python-versions = ">=3.6.0"
|
||||
files = [
|
||||
{file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
|
||||
{file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = ">1.2"
|
||||
|
||||
[package.extras]
|
||||
cchardet = ["cchardet"]
|
||||
chardet = ["chardet"]
|
||||
charset-normalizer = ["charset-normalizer"]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.34.8"
|
||||
|
@ -391,6 +412,23 @@ files = [
|
|||
[package.dependencies]
|
||||
flake8 = ">=2"
|
||||
|
||||
[[package]]
|
||||
name = "furo"
|
||||
version = "2023.9.10"
|
||||
description = "A clean customisable Sphinx documentation theme."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"},
|
||||
{file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
beautifulsoup4 = "*"
|
||||
pygments = ">=2.7"
|
||||
sphinx = ">=6.0,<8.0"
|
||||
sphinx-basic-ng = "*"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
|
@ -1337,6 +1375,17 @@ files = [
|
|||
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soupsieve"
|
||||
version = "2.5"
|
||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
|
||||
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx"
|
||||
version = "7.2.6"
|
||||
|
@ -1372,23 +1421,21 @@ lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy
|
|||
test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinx-rtd-theme"
|
||||
version = "2.0.0"
|
||||
description = "Read the Docs theme for Sphinx"
|
||||
name = "sphinx-basic-ng"
|
||||
version = "1.0.0b2"
|
||||
description = "A modern skeleton for Sphinx themes."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"},
|
||||
{file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"},
|
||||
{file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"},
|
||||
{file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
docutils = "<0.21"
|
||||
sphinx = ">=5,<8"
|
||||
sphinxcontrib-jquery = ">=4,<5"
|
||||
sphinx = ">=4.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"]
|
||||
docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-applehelp"
|
||||
|
@ -1444,20 +1491,6 @@ Sphinx = ">=5"
|
|||
lint = ["docutils-stubs", "flake8", "mypy"]
|
||||
test = ["html5lib", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-jquery"
|
||||
version = "4.1"
|
||||
description = "Extension to include jQuery on newer Sphinx releases"
|
||||
optional = false
|
||||
python-versions = ">=2.7"
|
||||
files = [
|
||||
{file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"},
|
||||
{file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Sphinx = ">=1.8"
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-jsmath"
|
||||
version = "1.0.1"
|
||||
|
@ -1663,4 +1696,4 @@ aws = ["boto3"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "10bea99532b925eac76461f71e685103a771c5549deeaff65f9a96df9e585341"
|
||||
content-hash = "e0cabda70067d490af2f9c9e9251384fb43454c545da6a071e25b1b74cc9f951"
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
[tool.poetry]
|
||||
name = "keep-it-secret"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
description = "Keep It Secret by BTHLabs"
|
||||
authors = ["Tomek Wójcik <contact@bthlabs.pl>"]
|
||||
maintainers = ["BTHLabs <contact@bthlabs.pl>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
homepage = "https://projects.bthlabs.pl/keep-it-secret/"
|
||||
repository = "https://git.bthlabs.pl/tomekwojcik/keep-it-secret/"
|
||||
documentation = "https://projects.bthlabs.pl/keep-it-secret/"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
|
@ -14,6 +18,7 @@ boto3 = {version = ">=1.34.0", optional = true}
|
|||
boto3 = "1.34.8"
|
||||
flake8 = "6.1.0"
|
||||
flake8-commas = "2.1.0"
|
||||
furo = "2023.9.10"
|
||||
invoke = "1.7.3"
|
||||
ipdb = "0.13.13"
|
||||
ipython = "8.19.0"
|
||||
|
@ -23,7 +28,6 @@ pytest = "7.4.3"
|
|||
pytest-env = "1.1.3"
|
||||
pytest-mock = "3.12.0"
|
||||
sphinx = "7.2.6"
|
||||
sphinx-rtd-theme = "2.0.0"
|
||||
twine = "4.0.2"
|
||||
|
||||
[tool.poetry.extras]
|
||||
|
|
12
tests/ext/aws/conftest.py
Normal file
12
tests/ext/aws/conftest.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from .fixtures import TestingAWSSecrets
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def testing_aws_secrets() -> TestingAWSSecrets:
|
||||
return TestingAWSSecrets()
|
10
tests/ext/aws/fixtures.py
Normal file
10
tests/ext/aws/fixtures.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
from keep_it_secret.ext.aws import AWSSecrets
|
||||
from keep_it_secret.fields import SecretsField
|
||||
from keep_it_secret.secrets import Secrets
|
||||
|
||||
|
||||
class TestingAWSSecrets(Secrets):
|
||||
aws = SecretsField.new(AWSSecrets)
|
|
@ -5,8 +5,31 @@ from __future__ import annotations
|
|||
import os
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from keep_it_secret.ext import aws
|
||||
|
||||
from .fixtures import TestingAWSSecrets
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_boto3_client(mocker: MockerFixture) -> mock.Mock:
|
||||
return mocker.patch.object(aws.boto3, 'client')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def aws_secrets_manager_client() -> mock.Mock:
|
||||
return mock.Mock()
|
||||
|
||||
|
||||
def test_init():
|
||||
# When
|
||||
result = aws.AWSSecrets()
|
||||
|
||||
# Then
|
||||
result.client is None
|
||||
|
||||
|
||||
@mock.patch.dict(
|
||||
os.environ,
|
||||
|
@ -43,3 +66,40 @@ def test_as_boto3_client_kwargs_empty():
|
|||
|
||||
# Then
|
||||
assert result == {}
|
||||
|
||||
|
||||
def test_get_client_cache_miss(mock_boto3_client: mock.Mock,
|
||||
aws_secrets_manager_client: mock.Mock,
|
||||
testing_aws_secrets: TestingAWSSecrets):
|
||||
# Given
|
||||
mock_boto3_client.return_value = aws_secrets_manager_client
|
||||
|
||||
field = aws.AWSSecrets()
|
||||
|
||||
# When
|
||||
result = field.get_client()
|
||||
|
||||
# Then
|
||||
assert result == aws_secrets_manager_client
|
||||
|
||||
assert field.client == aws_secrets_manager_client
|
||||
|
||||
mock_boto3_client.assert_called_once_with(
|
||||
'secretsmanager', **testing_aws_secrets.aws.as_boto3_client_kwargs(),
|
||||
)
|
||||
|
||||
|
||||
def test_get_client_cache_hit(mock_boto3_client: mock.Mock,
|
||||
aws_secrets_manager_client: mock.Mock,
|
||||
testing_aws_secrets: TestingAWSSecrets):
|
||||
# Given
|
||||
field = aws.AWSSecrets()
|
||||
field.client = aws_secrets_manager_client
|
||||
|
||||
# When
|
||||
result = field.get_client()
|
||||
|
||||
# Then
|
||||
assert result == aws_secrets_manager_client
|
||||
|
||||
mock_boto3_client.assert_not_called()
|
||||
|
|
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||
|
||||
import datetime
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import boto3
|
||||
import moto
|
||||
|
@ -15,25 +14,6 @@ from keep_it_secret.ext import aws
|
|||
from keep_it_secret.secrets import Secrets
|
||||
|
||||
|
||||
class TestingAWSSecrets(Secrets):
|
||||
aws = aws.AWSSecrets()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def aws_secrets_manager_client() -> mock.Mock:
|
||||
return mock.Mock(spec=['get_secret_value'])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_boto3_client(mocker: MockerFixture) -> mock.Mock:
|
||||
return mocker.patch.object(aws.boto3, 'client')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def testing_aws_secrets() -> TestingAWSSecrets:
|
||||
return TestingAWSSecrets()
|
||||
|
||||
|
||||
def test_init():
|
||||
# When
|
||||
result = aws.AWSSecretsManagerField('keep_it_secret/tests/spam')
|
||||
|
@ -42,7 +22,6 @@ def test_init():
|
|||
assert result.secret_id == 'keep_it_secret/tests/spam'
|
||||
assert result.default is None
|
||||
assert result.decoder == json.loads
|
||||
assert result.client is None
|
||||
|
||||
|
||||
def test_init_with_default():
|
||||
|
@ -107,53 +86,11 @@ def test_new(mocker: MockerFixture):
|
|||
)
|
||||
|
||||
|
||||
def test_get_client_cache_miss(mock_boto3_client: mock.Mock,
|
||||
aws_secrets_manager_client: mock.Mock,
|
||||
testing_aws_secrets: TestingAWSSecrets):
|
||||
# Given
|
||||
mock_boto3_client.return_value = aws_secrets_manager_client
|
||||
|
||||
field = aws.AWSSecretsManagerField('keep_it_secret/tests/spam')
|
||||
|
||||
# When
|
||||
result = field.get_client(testing_aws_secrets)
|
||||
|
||||
# Then
|
||||
assert result == aws_secrets_manager_client
|
||||
|
||||
assert field.client == aws_secrets_manager_client
|
||||
|
||||
mock_boto3_client.assert_called_once_with(
|
||||
'secretsmanager', **testing_aws_secrets.aws.as_boto3_client_kwargs(),
|
||||
)
|
||||
|
||||
|
||||
def test_get_client_cache_hit(mock_boto3_client: mock.Mock,
|
||||
aws_secrets_manager_client: mock.Mock,
|
||||
testing_aws_secrets: TestingAWSSecrets):
|
||||
# Given
|
||||
field = aws.AWSSecretsManagerField('keep_it_secret/tests/spam')
|
||||
field.client = aws_secrets_manager_client
|
||||
|
||||
# When
|
||||
result = field.get_client(testing_aws_secrets)
|
||||
|
||||
# Then
|
||||
assert result == aws_secrets_manager_client
|
||||
|
||||
mock_boto3_client.assert_not_called()
|
||||
|
||||
|
||||
def test_get_value_aws_dependency_missing(mocker: MockerFixture,
|
||||
aws_secrets_manager_client: mock.Mock,
|
||||
testing_secrets: Secrets):
|
||||
# Given
|
||||
field = aws.AWSSecretsManagerField('keep_it_secret/tests/spam')
|
||||
|
||||
mock_field_get_client = mocker.patch.object(
|
||||
field, 'get_client', return_value=aws_secrets_manager_client,
|
||||
)
|
||||
|
||||
with pytest.raises(field.DependencyMissing) as exception_info:
|
||||
# When
|
||||
_ = field(testing_secrets)
|
||||
|
@ -161,10 +98,6 @@ def test_get_value_aws_dependency_missing(mocker: MockerFixture,
|
|||
# Then
|
||||
assert exception_info.value.args[0] == 'aws'
|
||||
|
||||
mock_field_get_client.assert_not_called()
|
||||
|
||||
aws_secrets_manager_client.get_secret_value.assert_not_called()
|
||||
|
||||
|
||||
@moto.mock_secretsmanager
|
||||
def test_get_value_required_value_not_found(testing_aws_secrets: Secrets):
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-x
|
||||
# type: ignore
|
||||
from __future__ import annotations
|
||||
|
||||
from keep_it_secret import secrets
|
||||
from keep_it_secret.fields import LiteralField
|
||||
|
||||
from tests.fixtures import TestingSecrets
|
||||
|
||||
|
||||
class ParentSecrets(secrets.Secrets):
|
||||
pass
|
||||
class NestedSecrets(secrets.Secrets):
|
||||
spameggs: str = LiteralField.new('spameggs')
|
||||
|
||||
|
||||
def test_init():
|
||||
|
@ -21,15 +22,12 @@ def test_init():
|
|||
assert result.__secrets_data__ == {'spam': 'spam', 'eggs': 'eggs'}
|
||||
|
||||
|
||||
def test_init_with_parent():
|
||||
# Given
|
||||
parent_secrets = ParentSecrets()
|
||||
|
||||
def test_init_with_parent(testing_secrets: TestingSecrets):
|
||||
# When
|
||||
result = TestingSecrets(parent=parent_secrets)
|
||||
result = NestedSecrets(parent=testing_secrets)
|
||||
|
||||
# Then
|
||||
assert result.__secrets_parent__ is parent_secrets
|
||||
assert result.__secrets_parent__ is testing_secrets
|
||||
|
||||
|
||||
def test_field_property(testing_secrets: TestingSecrets):
|
||||
|
@ -38,3 +36,58 @@ def test_field_property(testing_secrets: TestingSecrets):
|
|||
|
||||
# Then
|
||||
assert result == testing_secrets.__secrets_data__['spam']
|
||||
|
||||
|
||||
def test_resolve_dependency_from_self():
|
||||
# Given
|
||||
secrets = NestedSecrets()
|
||||
|
||||
# When
|
||||
result = secrets.resolve_dependency('spameggs')
|
||||
|
||||
# Then
|
||||
assert result == secrets.spameggs
|
||||
|
||||
|
||||
def test_resolve_dependency_from_parent(testing_secrets: TestingSecrets):
|
||||
# Given
|
||||
secrets = NestedSecrets(parent=testing_secrets)
|
||||
|
||||
# When
|
||||
result = secrets.resolve_dependency('spam')
|
||||
|
||||
# Then
|
||||
assert result == testing_secrets.spam
|
||||
|
||||
|
||||
def test_resolve_dependency_unresolved_from_self():
|
||||
# Given
|
||||
secrets = NestedSecrets()
|
||||
|
||||
# When
|
||||
result = secrets.resolve_dependency('thisisntright')
|
||||
|
||||
# Then
|
||||
assert result is secrets.UNRESOLVED_DEPENDENCY
|
||||
|
||||
|
||||
def test_resolve_dependency_unresolved_with_parent(testing_secrets: TestingSecrets):
|
||||
# Given
|
||||
secrets = NestedSecrets(parent=testing_secrets)
|
||||
|
||||
# When
|
||||
result = secrets.resolve_dependency('thisisntright')
|
||||
|
||||
# Then
|
||||
assert result is secrets.UNRESOLVED_DEPENDENCY
|
||||
|
||||
|
||||
def test_resolve_dependency_unresolved_dont_include_parents(testing_secrets: TestingSecrets):
|
||||
# Given
|
||||
secrets = NestedSecrets(parent=testing_secrets)
|
||||
|
||||
# When
|
||||
result = secrets.resolve_dependency('spam', include_parents=False)
|
||||
|
||||
# Then
|
||||
assert result is secrets.UNRESOLVED_DEPENDENCY
|
||||
|
|
Loading…
Reference in New Issue
Block a user