"""Unit tests for the dynamo_cli Lambda handler.""" import boto3 import pytest from moto import mock_aws TABLE_NAME = 'offboarding-automation-snapshots' REGION = 'us-east-1' def _make_table() -> None: """Create the DynamoDB table for tests.""" dynamodb = boto3.resource('dynamodb', region_name=REGION) dynamodb.create_table( TableName=TABLE_NAME, KeySchema=[{'AttributeName': 'ticket_id', 'KeyType': 'HASH'}], AttributeDefinitions=[{'AttributeName': 'ticket_id', 'AttributeType': 'S'}], BillingMode='PAY_PER_REQUEST', ) def _make_put_event(dry_run: bool = False) -> dict: return { 'action': 'put-snapshot', 'ticket_id': 'SYS-123', 'email': 'john.doe@company.com', 'full_name': 'John Doe', 'last_working_day': '2026-04-30', 'auth0_matches': [], 'terraform_hits': [], 'dry_run': dry_run, } class TestHandlerRouting: """Tests for the handler action dispatcher.""" def test_unknown_action_raises(self) -> None: """Unknown action raises ValueError.""" from dynamo_client.app import handler with pytest.raises(ValueError, match='Unknown action'): handler({'action': 'nonexistent'}, None) def test_missing_action_raises(self) -> None: """Missing action key raises ValueError.""" from dynamo_client.app import handler with pytest.raises(ValueError, match='Unknown action'): handler({}, None) @mock_aws class TestPutSnapshot: """Tests for the put-snapshot action.""" def setup_method(self, method: object = None) -> None: """Reset module-level singletons before each test.""" import dynamo_client.app as app_module app_module._config = None app_module._client = None def test_stores_snapshot(self) -> None: """put-snapshot stores the item and returns stored=True.""" _make_table() from dynamo_client.app import handler result = handler(_make_put_event(), None) assert result['stored'] is True assert result['ticket_id'] == 'SYS-123' assert result['dry_run'] is False def test_dry_run_does_not_store(self) -> None: """put-snapshot with dry_run=True returns stored=False without writing.""" _make_table() from dynamo_client.app import handler result = handler(_make_put_event(dry_run=True), None) assert result['stored'] is False assert result['dry_run'] is True # Verify nothing was written item = ( boto3.resource('dynamodb', region_name=REGION) .Table(TABLE_NAME) .get_item(Key={'ticket_id': 'SYS-123'}) .get('Item') ) assert item is None @mock_aws class TestGetSnapshot: """Tests for the get-snapshot action.""" def setup_method(self, method: object = None) -> None: """Reset module-level singletons before each test.""" import dynamo_client.app as app_module app_module._config = None app_module._client = None def test_returns_snapshot_when_found(self) -> None: """get-snapshot returns found=True with snapshot when item exists.""" _make_table() from dynamo_client.app import handler handler(_make_put_event(), None) result = handler({'action': 'get-snapshot', 'ticket_id': 'SYS-123'}, None) assert result['found'] is True assert result['snapshot']['email'] == 'john.doe@company.com' def test_returns_not_found(self) -> None: """get-snapshot returns found=False when item does not exist.""" _make_table() from dynamo_client.app import handler result = handler({'action': 'get-snapshot', 'ticket_id': 'SYS-MISSING'}, None) assert result['found'] is False assert result['snapshot'] is None @mock_aws class TestDeleteSnapshot: """Tests for the delete-snapshot action.""" def setup_method(self, method: object = None) -> None: """Reset module-level singletons before each test.""" import dynamo_client.app as app_module app_module._config = None app_module._client = None def test_deletes_snapshot(self) -> None: """delete-snapshot removes the item and returns deleted=True.""" _make_table() from dynamo_client.app import handler handler(_make_put_event(), None) result = handler( {'action': 'delete-snapshot', 'ticket_id': 'SYS-123', 'dry_run': False}, None, ) assert result['deleted'] is True assert result['dry_run'] is False get_result = handler({'action': 'get-snapshot', 'ticket_id': 'SYS-123'}, None) assert get_result['found'] is False def test_dry_run_does_not_delete(self) -> None: """delete-snapshot with dry_run=True leaves the item intact.""" _make_table() from dynamo_client.app import handler handler(_make_put_event(), None) result = handler( {'action': 'delete-snapshot', 'ticket_id': 'SYS-123', 'dry_run': True}, None ) assert result['deleted'] is False assert result['dry_run'] is True get_result = handler({'action': 'get-snapshot', 'ticket_id': 'SYS-123'}, None) assert get_result['found'] is True