Skip to content

Commit b534aa6

Browse files
authored
Merge pull request #79 from FullStackWithLawrence/next
incremental improvements to CI console logging and unit tests
2 parents ffe2ac2 + 29654e1 commit b534aa6

10 files changed

Lines changed: 93 additions & 44 deletions

File tree

.github/workflows/tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ jobs:
3030
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
3131
aws-region: ${{ secrets.AWS_REGION }}
3232

33+
- name: Get IAM user info
34+
run: |
35+
aws sts get-caller-identity
36+
3337
- name: Run Python tests
3438
uses: ./.github/actions/tests/python
3539
with:

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ repos:
1313
args: ["--ignore-words=codespell.txt"]
1414
exclude: 'codespell.txt|\.svg$'
1515
- repo: https://github.com/psf/black
16-
rev: 23.12.0
16+
rev: 23.12.1
1717
hooks:
1818
- id: black
1919
- repo: https://github.com/PyCQA/flake8

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ codespell==2.2.6
1919

2020
# project dependencies
2121
# ------------
22-
boto3==1.34.7
22+
boto3==1.34.2
23+
botocore==1.34.6
2324
python-dotenv==1.0.0
2425
pydantic==2.5.3
2526
pydantic-settings==2.1.0
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# -*- coding: utf-8 -*-
22
# DO NOT EDIT.
33
# Managed via automated CI/CD in .github/workflows/semanticVersionBump.yml.
4-
__version__ = "0.2.11-next.1"
4+
__version__ = "0.2.12-next.1"

terraform/python/rekognition_api/conf.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ def to_dict(cls):
123123
return {
124124
key: value
125125
for key, value in Services.__dict__.items()
126-
if not key.startswith("__") and not callable(key) and key != "to_dict"
126+
if not key.startswith("__")
127+
and not callable(key)
128+
and key not in ["enabled", "raise_error_on_disabled", "to_dict", "enabled_services"]
127129
}
128130

129131
@classmethod
@@ -134,7 +136,7 @@ def enabled_services(cls) -> List[str]:
134136
for key in dir(cls)
135137
if not key.startswith("__")
136138
and not callable(getattr(cls, key))
137-
and key != "to_dict"
139+
and key not in ["enabled", "raise_error_on_disabled", "to_dict", "enabled_services"]
138140
and getattr(cls, key)[1] is True
139141
]
140142

@@ -219,27 +221,46 @@ class Settings(BaseSettings):
219221
_dump: dict = None
220222
_initialized: bool = False
221223

222-
# pylint: disable=too-many-branches
223-
def __init__(self, **data: Any):
224+
# pylint: disable=too-many-branches,too-many-statements
225+
def __init__(self, **data: Any): # noqa: C901
224226
super().__init__(**data)
225227
if not Services.enabled(Services.AWS_CLI):
226228
self._initialized = True
227229
return
228230

229231
if bool(os.environ.get("AWS_DEPLOYED", False)):
230232
# If we're running inside AWS Lambda, then we don't need to set the AWS credentials.
233+
logger.info("running inside AWS Lambda")
231234
self._aws_access_key_id_source: str = "overridden by IAM role-based security"
232235
self._aws_secret_access_key_source: str = "overridden by IAM role-based security"
233236
self._aws_session = boto3.Session()
234237
self._initialized = True
235238

236239
if not self.initialized and bool(os.environ.get("GITHUB_ACTIONS", False)):
240+
console_handler = logging.StreamHandler()
241+
console_handler.setLevel(logging.DEBUG)
242+
logger.addHandler(console_handler)
243+
logger.setLevel(logging.DEBUG)
244+
245+
logger.info("running inside GitHub Actions")
246+
aws_access_key_id = os.environ.get("AWS_ACCESS_KEY_ID", None)
247+
aws_secret_access_key = os.environ.get("AWS_SECRET_ACCESS_KEY", None)
248+
if not aws_access_key_id or not aws_secret_access_key and not self.aws_profile:
249+
raise RekognitionConfigurationError(
250+
"required environment variable(s) AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY not set"
251+
)
252+
region_name = os.environ.get("AWS_REGION", None)
253+
if not region_name and not self.aws_profile:
254+
raise RekognitionConfigurationError("required environment variable AWS_REGION not set")
237255
try:
238256
self._aws_session = boto3.Session(
239-
region_name=os.environ.get("AWS_REGION", "us-east-1"),
240-
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", None),
241-
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", None),
257+
region_name=region_name,
258+
aws_access_key_id=aws_access_key_id,
259+
aws_secret_access_key=aws_secret_access_key,
242260
)
261+
self._initialized = True
262+
self._aws_access_key_id_source = "environ"
263+
self._aws_secret_access_key_source = "environ"
243264
except ProfileNotFound:
244265
logger.warning("aws_profile %s not found", self.aws_profile)
245266

@@ -249,6 +270,7 @@ def __init__(self, **data: Any):
249270
else:
250271
self._aws_access_key_id_source = "environ"
251272
self._aws_secret_access_key_source = "environ"
273+
252274
self._initialized = True
253275

254276
if not self.initialized:
@@ -351,6 +373,10 @@ def __init__(self, **data: Any):
351373
pre=True,
352374
getter=lambda v: empty_str_to_int_default(v, SettingsDefaults.AWS_REKOGNITION_FACE_DETECT_THRESHOLD),
353375
)
376+
init_info: Optional[str] = Field(
377+
None,
378+
env="INIT_INFO",
379+
)
354380

355381
@property
356382
def initialized(self):
@@ -384,12 +410,15 @@ def aws_secret_access_key_source(self):
384410
@property
385411
def aws_auth(self) -> dict:
386412
"""AWS authentication"""
387-
return {
413+
retval = {
388414
"aws_profile": self.aws_profile,
389415
"aws_access_key_id_source": self.aws_access_key_id_source,
390416
"aws_secret_access_key_source": self.aws_secret_access_key_source,
391417
"aws_region": self.aws_region,
392418
}
419+
if self.init_info:
420+
retval["init_info"] = self.init_info
421+
return retval
393422

394423
@property
395424
def aws_session(self):

terraform/python/rekognition_api/const.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,6 @@
2424

2525
logger = logging.getLogger(__name__)
2626

27-
# def read_file_from_github(repo_owner, repo_name, file_path):
28-
# """For prod. Read a file from a GitHub repository."""
29-
# base_url = "https://raw.githubusercontent.com"
30-
# url = f"{base_url}/{repo_owner}/{repo_name}/main/{file_path}"
31-
# response = requests.get(url, timeout=5)
32-
# if response.status_code == 200:
33-
# return response.text
34-
# return None
35-
3627
try:
3728
with open(TERRAFORM_TFVARS, "r", encoding="utf-8") as f:
3829
TFVARS = hcl2.load(f)

terraform/python/rekognition_api/tests/test_aws.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,21 @@ class TestAWSInfrastructture(unittest.TestCase):
3232
# Get the working directory of this script
3333
here = os.path.dirname(os.path.abspath(__file__))
3434

35-
def env_path(self, filename):
36-
"""Return the path to the .env file."""
37-
return os.path.join(self.here, filename)
38-
3935
def setUp(self):
4036
"""Set up test fixtures."""
37+
self.saved_env = dict(os.environ)
4138
env_path = self.env_path(".env")
4239
load_dotenv(env_path)
4340

41+
def tearDown(self):
42+
# Restore environment variables
43+
os.environ.clear()
44+
os.environ.update(self.saved_env)
45+
46+
def env_path(self, filename):
47+
"""Return the path to the .env file."""
48+
return os.path.join(self.here, filename)
49+
4450
def test_rekognition_collection_exists(self):
4551
"""Test that the Rekognition collection exists."""
4652
if not Services.enabled(Services.AWS_REKOGNITION):

terraform/python/rekognition_api/tests/test_configuration.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ class TestConfiguration(unittest.TestCase):
3131
env_vars = dict(os.environ)
3232

3333
def setUp(self):
34-
"""Set up test fixtures."""
34+
# Save current environment variables
35+
self.saved_env = dict(os.environ)
36+
37+
def tearDown(self):
38+
# Restore environment variables
39+
os.environ.clear()
40+
os.environ.update(self.saved_env)
3541

3642
def env_path(self, filename):
3743
"""Return the path to the .env file."""
@@ -40,7 +46,7 @@ def env_path(self, filename):
4046
def test_conf_defaults(self):
4147
"""Test that settings == SettingsDefaults when no .env is in use."""
4248
os.environ.clear()
43-
mock_settings = Settings()
49+
mock_settings = Settings(init_info="test_conf_defaults()")
4450
os.environ.update(self.env_vars)
4551

4652
self.assertEqual(mock_settings.aws_region, SettingsDefaults.AWS_REGION)
@@ -70,7 +76,7 @@ def test_env_illegal_nulls(self):
7076
self.assertTrue(loaded)
7177

7278
with self.assertRaises(PydanticValidationError):
73-
Settings()
79+
Settings(init_info="test_env_illegal_nulls()")
7480

7581
os.environ.update(self.env_vars)
7682

@@ -81,7 +87,7 @@ def test_env_nulls(self):
8187
loaded = load_dotenv(env_path)
8288
self.assertTrue(loaded)
8389

84-
mock_settings = Settings()
90+
mock_settings = Settings(init_info="test_env_nulls()")
8591
os.environ.update(self.env_vars)
8692

8793
self.assertEqual(mock_settings.aws_region, SettingsDefaults.AWS_REGION)
@@ -103,7 +109,7 @@ def test_env_overrides(self):
103109
loaded = load_dotenv(env_path)
104110
self.assertTrue(loaded)
105111

106-
mock_settings = Settings()
112+
mock_settings = Settings(init_info="test_env_overrides()")
107113
os.environ.update(self.env_vars)
108114

109115
self.assertEqual(mock_settings.aws_region, "us-west-1")
@@ -122,7 +128,7 @@ def test_env_overrides(self):
122128
def test_aws_credentials_with_profile(self):
123129
"""Test that key and secret are unset when using profile."""
124130

125-
mock_settings = Settings()
131+
mock_settings = Settings(init_info="test_aws_credentials_with_profile()")
126132
self.assertEqual(mock_settings.aws_access_key_id_source, "aws_profile")
127133
self.assertEqual(mock_settings.aws_secret_access_key_source, "aws_profile")
128134

@@ -132,7 +138,7 @@ def test_aws_credentials_with_profile(self):
132138
def test_aws_credentials_without_profile(self):
133139
"""Test that key and secret are set by environment variable when provided."""
134140

135-
mock_settings = Settings()
141+
mock_settings = Settings(init_info="test_aws_credentials_without_profile()")
136142
# pylint: disable=no-member
137143
self.assertEqual(mock_settings.aws_access_key_id.get_secret_value(), "TEST_KEY")
138144
# pylint: disable=no-member
@@ -148,7 +154,7 @@ def test_aws_credentials_without_profile(self):
148154
def test_aws_credentials_noinfo(self):
149155
"""Test that key and secret remain unset when no profile nor environment variables are provided."""
150156
os.environ.clear()
151-
mock_settings = Settings()
157+
mock_settings = Settings(init_info="test_aws_credentials_noinfo()")
152158
os.environ.update(self.env_vars)
153159
aws_profile = TFVARS.get("aws_profile", None)
154160
self.assertEqual(mock_settings.aws_profile, aws_profile)
@@ -169,21 +175,21 @@ def test_invalid_aws_region_code(self):
169175
"""Test that Pydantic raises a validation error for environment variable with non-existent aws region code."""
170176

171177
with self.assertRaises(RekognitionValueError):
172-
Settings()
178+
Settings(init_info="test_invalid_aws_region_code()")
173179

174180
@patch.dict(os.environ, {"AWS_REKOGNITION_FACE_DETECT_MAX_FACES_COUNT": "-1"})
175181
def test_invalid_max_faces_count(self):
176182
"""Test that Pydantic raises a validation error for environment variable w negative integer values."""
177183

178184
with self.assertRaises(PydanticValidationError):
179-
Settings()
185+
Settings(init_info="test_invalid_max_faces_count()")
180186

181187
@patch.dict(os.environ, {"AWS_REKOGNITION_FACE_DETECT_THRESHOLD": "-1"})
182188
def test_invalid_threshold(self):
183189
"""Test that Pydantic raises a validation error for environment variable w negative integer values."""
184190

185191
with self.assertRaises(PydanticValidationError):
186-
Settings()
192+
Settings(init_info="test_invalid_threshold()")
187193

188194
def test_configure_with_class_constructor(self):
189195
"""test that we can set values with the class constructor"""
@@ -197,6 +203,7 @@ def test_configure_with_class_constructor(self):
197203
aws_rekognition_face_detect_quality_filter="TEST_AUTO",
198204
aws_rekognition_face_detect_threshold=102,
199205
debug_mode=True,
206+
init_info="test_configure_with_class_constructor()",
200207
)
201208

202209
self.assertEqual(mock_settings.aws_region, "eu-west-1")
@@ -212,15 +219,20 @@ def test_configure_neg_int_with_class_constructor(self):
212219
"""test that we cannot set negative int values with the class constructor"""
213220

214221
with self.assertRaises(PydanticValidationError):
215-
Settings(aws_rekognition_face_detect_max_faces_count=-1)
222+
Settings(
223+
aws_rekognition_face_detect_max_faces_count=-1,
224+
init_info="test_configure_neg_int_with_class_constructor()",
225+
)
216226

217227
with self.assertRaises(PydanticValidationError):
218-
Settings(aws_rekognition_face_detect_threshold=-1)
228+
Settings(
229+
aws_rekognition_face_detect_threshold=-1, init_info="test_configure_neg_int_with_class_constructor()"
230+
)
219231

220232
def test_readonly_settings(self):
221233
"""test that we can't set readonly values with the class constructor"""
222234

223-
mock_settings = Settings(aws_region="eu-west-1")
235+
mock_settings = Settings(aws_region="eu-west-1", init_info="test_readonly_settings()")
224236
with self.assertRaises(PydanticValidationError):
225237
mock_settings.aws_region = "us-west-1"
226238

terraform/python/rekognition_api/tests/test_configuration_dump.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,25 @@
2121
class TestConfigurationDump(unittest.TestCase):
2222
"""Test configuration."""
2323

24+
def setUp(self):
25+
# Save current environment variables
26+
self.saved_env = dict(os.environ)
27+
28+
def tearDown(self):
29+
# Restore environment variables
30+
os.environ.clear()
31+
os.environ.update(self.saved_env)
32+
2433
def test_dump(self):
2534
"""Test that dump is a dict."""
2635

27-
mock_settings = Settings(aws_region="us-east-1")
36+
mock_settings = Settings(aws_region="us-east-1", init_info="test_dump()")
2837
self.assertIsInstance(mock_settings.dump, dict)
2938

3039
def test_dump_keys(self):
3140
"""Test that dump contains the expected keys."""
3241

33-
mock_settings = Settings(aws_region="us-east-1")
42+
mock_settings = Settings(aws_region="us-east-1", init_info="test_dump_keys()")
3443

3544
dump = mock_settings.dump
3645
self.assertIn("environment", dump)
@@ -43,7 +52,7 @@ def test_dump_keys(self):
4352
def test_dump_values(self):
4453
"""Test that dump contains the expected values."""
4554

46-
mock_settings = Settings(aws_region="us-east-1")
55+
mock_settings = Settings(aws_region="us-east-1", init_info="test_dump_values()")
4756
environment = mock_settings.dump["environment"]
4857

4958
self.assertEqual(environment["is_using_tfvars_file"], mock_settings.is_using_tfvars_file)

terraform/python/rekognition_api/tests/test_lambda_info.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
from rekognition_api.lambda_info import lambda_handler # noqa: E402
1818

19-
# our stuff
20-
from rekognition_api.tests.test_setup import get_test_file # noqa: E402
21-
2219

2320
class TestLambdaInfo(unittest.TestCase):
2421
"""Test Index Lambda function."""

0 commit comments

Comments
 (0)