"""Unit tests for DynamoClient using moto.""" import time import boto3 from dynamo_client.dynamo_client import DynamoClient from moto import mock_aws from shared.schemas.dynamo import Snapshot TABLE_NAME = 'test-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_snapshot(ticket_id: str = 'SYS-123') -> Snapshot: """Create a minimal Snapshot for testing.""" return Snapshot( ticket_id=ticket_id, email='john.doe@company.com', full_name='John Doe', last_working_day='2026-04-30', checked_at='2026-04-10T12:00:00+00:00', ) @mock_aws class TestPutSnapshot: """Tests for DynamoClient.put_snapshot().""" def test_stores_item_in_table(self) -> None: """put_snapshot writes an item retrievable via get_item.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) snapshot = _make_snapshot() client.put_snapshot(snapshot, ttl_days=7) raw = boto3.resource('dynamodb', region_name=REGION).Table(TABLE_NAME) item = raw.get_item(Key={'ticket_id': 'SYS-123'}).get('Item') assert item is not None assert item['email'] == 'john.doe@company.com' def test_stores_ttl_attribute(self) -> None: """put_snapshot sets a ttl attribute approximately ttl_days from now.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) snapshot = _make_snapshot() before = int(time.time()) client.put_snapshot(snapshot, ttl_days=7) after = int(time.time()) raw = boto3.resource('dynamodb', region_name=REGION).Table(TABLE_NAME) item = raw.get_item(Key={'ticket_id': 'SYS-123'}).get('Item') ttl = int(item['ttl']) expected_low = before + 7 * 86400 expected_high = after + 7 * 86400 assert expected_low <= ttl <= expected_high def test_overwrites_existing_item(self) -> None: """put_snapshot overwrites a previous snapshot for the same ticket.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) snap1 = _make_snapshot() client.put_snapshot(snap1) snap2 = Snapshot( ticket_id='SYS-123', email='updated@company.com', full_name='Updated Name', last_working_day='2026-05-01', checked_at='2026-04-11T09:00:00+00:00', ) client.put_snapshot(snap2) raw = boto3.resource('dynamodb', region_name=REGION).Table(TABLE_NAME) item = raw.get_item(Key={'ticket_id': 'SYS-123'}).get('Item') assert item['email'] == 'updated@company.com' @mock_aws class TestGetSnapshot: """Tests for DynamoClient.get_snapshot().""" def test_returns_snapshot_when_found(self) -> None: """get_snapshot returns a Snapshot with correct fields.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) snapshot = _make_snapshot() client.put_snapshot(snapshot) result = client.get_snapshot('SYS-123') assert result is not None assert result.ticket_id == 'SYS-123' assert result.email == 'john.doe@company.com' assert result.full_name == 'John Doe' def test_returns_none_when_not_found(self) -> None: """get_snapshot returns None for a missing ticket ID.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) result = client.get_snapshot('SYS-MISSING') assert result is None def test_roundtrip_preserves_nested_fields(self) -> None: """auth0_matches, terraform_hits survive a put/get roundtrip.""" from shared.schemas.auth0 import Auth0Match from shared.schemas.common import TerraformHit _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) snapshot = Snapshot( ticket_id='SYS-456', email='a@b.com', full_name='A B', last_working_day='2026-04-30', checked_at='2026-04-10T12:00:00+00:00', auth0_matches=[Auth0Match(tenant='prod-orchard', user_id='auth0|abc')], terraform_hits=[ TerraformHit( path='main.tf', html_url='https://github.com/org/repo/blob/main/main.tf', repository='org/repo', ) ], ) client.put_snapshot(snapshot) result = client.get_snapshot('SYS-456') assert result is not None assert len(result.auth0_matches) == 1 assert result.auth0_matches[0].tenant == 'prod-orchard' assert len(result.terraform_hits) == 1 assert result.terraform_hits[0].repository == 'org/repo' @mock_aws class TestDeleteSnapshot: """Tests for DynamoClient.delete_snapshot().""" def test_removes_item_from_table(self) -> None: """delete_snapshot removes the item so get_snapshot returns None.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) client.put_snapshot(_make_snapshot()) client.delete_snapshot('SYS-123') assert client.get_snapshot('SYS-123') is None def test_idempotent_on_missing_item(self) -> None: """delete_snapshot does not raise when item does not exist.""" _make_table() client = DynamoClient(table_name=TABLE_NAME, region=REGION) # Should not raise client.delete_snapshot('SYS-NONEXISTENT')