|
|
|
|
|
""" |
|
|
Unit Tests for Lambda Function |
|
|
""" |
|
|
|
|
|
import pytest |
|
|
import json |
|
|
import os |
|
|
import sys |
|
|
from pathlib import Path |
|
|
from unittest.mock import Mock, patch, MagicMock |
|
|
|
|
|
|
|
|
project_root = Path(__file__).parent.parent.parent |
|
|
sys.path.append(str(project_root)) |
|
|
|
|
|
class TestLambdaFunction: |
|
|
"""Unit tests for Lambda function""" |
|
|
|
|
|
@pytest.fixture |
|
|
def mock_event(self): |
|
|
"""Mock event for testing""" |
|
|
return { |
|
|
'indicators': ['GDP', 'UNRATE'], |
|
|
'start_date': '2024-01-01', |
|
|
'end_date': '2024-01-31', |
|
|
'options': { |
|
|
'visualizations': True, |
|
|
'correlation': True, |
|
|
'forecasting': False, |
|
|
'statistics': True |
|
|
} |
|
|
} |
|
|
|
|
|
@pytest.fixture |
|
|
def mock_context(self): |
|
|
"""Mock context for testing""" |
|
|
context = Mock() |
|
|
context.function_name = 'fred-ml-processor' |
|
|
context.function_version = '$LATEST' |
|
|
context.invoked_function_arn = 'arn:aws:lambda:us-west-2:123456789012:function:fred-ml-processor' |
|
|
context.memory_limit_in_mb = 512 |
|
|
context.remaining_time_in_millis = 300000 |
|
|
context.log_group_name = '/aws/lambda/fred-ml-processor' |
|
|
context.log_stream_name = '2024/01/01/[$LATEST]123456789012' |
|
|
return context |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
@patch('lambda.lambda_function.boto3.client') |
|
|
def test_lambda_handler_success(self, mock_boto3_client, mock_os_environ, mock_event, mock_context): |
|
|
"""Test successful Lambda function execution""" |
|
|
|
|
|
mock_os_environ.side_effect = lambda key, default=None: { |
|
|
'FRED_API_KEY': 'test-api-key', |
|
|
'S3_BUCKET': 'fredmlv1' |
|
|
}.get(key, default) |
|
|
|
|
|
|
|
|
mock_s3_client = Mock() |
|
|
mock_lambda_client = Mock() |
|
|
mock_boto3_client.side_effect = [mock_s3_client, mock_lambda_client] |
|
|
|
|
|
|
|
|
with patch('lambda.lambda_function.requests.get') as mock_requests: |
|
|
mock_response = Mock() |
|
|
mock_response.status_code = 200 |
|
|
mock_response.json.return_value = { |
|
|
'observations': [ |
|
|
{'date': '2024-01-01', 'value': '100.0'}, |
|
|
{'date': '2024-01-02', 'value': '101.0'} |
|
|
] |
|
|
} |
|
|
mock_requests.return_value = mock_response |
|
|
|
|
|
|
|
|
sys.path.append(str(project_root / 'lambda')) |
|
|
from lambda_function import lambda_handler |
|
|
|
|
|
response = lambda_handler(mock_event, mock_context) |
|
|
|
|
|
|
|
|
assert response['statusCode'] == 200 |
|
|
assert 'body' in response |
|
|
|
|
|
response_body = json.loads(response['body']) |
|
|
assert response_body['status'] == 'success' |
|
|
assert 'report_id' in response_body |
|
|
assert 'report_key' in response_body |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
def test_lambda_handler_missing_api_key(self, mock_os_environ, mock_event, mock_context): |
|
|
"""Test Lambda function with missing API key""" |
|
|
|
|
|
mock_os_environ.return_value = None |
|
|
|
|
|
sys.path.append(str(project_root / 'lambda')) |
|
|
from lambda_function import lambda_handler |
|
|
|
|
|
response = lambda_handler(mock_event, mock_context) |
|
|
|
|
|
|
|
|
assert response['statusCode'] == 500 |
|
|
response_body = json.loads(response['body']) |
|
|
assert response_body['status'] == 'error' |
|
|
|
|
|
def test_lambda_handler_invalid_event(self, mock_context): |
|
|
"""Test Lambda function with invalid event""" |
|
|
invalid_event = {} |
|
|
|
|
|
sys.path.append(str(project_root / 'lambda')) |
|
|
from lambda_function import lambda_handler |
|
|
|
|
|
response = lambda_handler(invalid_event, mock_context) |
|
|
|
|
|
|
|
|
assert response['statusCode'] == 200 or response['statusCode'] == 500 |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
@patch('lambda.lambda_function.boto3.client') |
|
|
def test_fred_data_fetching(self, mock_boto3_client, mock_os_environ): |
|
|
"""Test FRED data fetching functionality""" |
|
|
|
|
|
mock_os_environ.side_effect = lambda key, default=None: { |
|
|
'FRED_API_KEY': 'test-api-key', |
|
|
'S3_BUCKET': 'fredmlv1' |
|
|
}.get(key, default) |
|
|
|
|
|
mock_s3_client = Mock() |
|
|
mock_lambda_client = Mock() |
|
|
mock_boto3_client.side_effect = [mock_s3_client, mock_lambda_client] |
|
|
|
|
|
sys.path.append(str(project_root / 'lambda')) |
|
|
from lambda_function import get_fred_data |
|
|
|
|
|
|
|
|
with patch('lambda.lambda_function.requests.get') as mock_requests: |
|
|
mock_response = Mock() |
|
|
mock_response.status_code = 200 |
|
|
mock_response.json.return_value = { |
|
|
'observations': [ |
|
|
{'date': '2024-01-01', 'value': '100.0'}, |
|
|
{'date': '2024-01-02', 'value': '101.0'} |
|
|
] |
|
|
} |
|
|
mock_requests.return_value = mock_response |
|
|
|
|
|
result = get_fred_data('GDP', '2024-01-01', '2024-01-31') |
|
|
|
|
|
assert result is not None |
|
|
assert len(result) > 0 |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
@patch('lambda.lambda_function.boto3.client') |
|
|
def test_dataframe_creation(self, mock_boto3_client, mock_os_environ): |
|
|
"""Test DataFrame creation from series data""" |
|
|
|
|
|
mock_os_environ.side_effect = lambda key, default=None: { |
|
|
'FRED_API_KEY': 'test-api-key', |
|
|
'S3_BUCKET': 'fredmlv1' |
|
|
}.get(key, default) |
|
|
|
|
|
mock_s3_client = Mock() |
|
|
mock_lambda_client = Mock() |
|
|
mock_boto3_client.side_effect = [mock_s3_client, mock_lambda_client] |
|
|
|
|
|
from lambda.lambda_function import create_dataframe |
|
|
import pandas as pd |
|
|
|
|
|
|
|
|
series_data = { |
|
|
'GDP': pd.Series([100.0, 101.0], index=pd.to_datetime(['2024-01-01', '2024-01-02'])), |
|
|
'UNRATE': pd.Series([3.5, 3.6], index=pd.to_datetime(['2024-01-01', '2024-01-02'])) |
|
|
} |
|
|
|
|
|
df = create_dataframe(series_data) |
|
|
|
|
|
assert not df.empty |
|
|
assert 'GDP' in df.columns |
|
|
assert 'UNRATE' in df.columns |
|
|
assert len(df) == 2 |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
@patch('lambda.lambda_function.boto3.client') |
|
|
def test_statistics_generation(self, mock_boto3_client, mock_os_environ): |
|
|
"""Test statistics generation""" |
|
|
|
|
|
mock_os_environ.side_effect = lambda key, default=None: { |
|
|
'FRED_API_KEY': 'test-api-key', |
|
|
'S3_BUCKET': 'fredmlv1' |
|
|
}.get(key, default) |
|
|
|
|
|
mock_s3_client = Mock() |
|
|
mock_lambda_client = Mock() |
|
|
mock_boto3_client.side_effect = [mock_s3_client, mock_lambda_client] |
|
|
|
|
|
from lambda.lambda_function import generate_statistics |
|
|
import pandas as pd |
|
|
|
|
|
|
|
|
df = pd.DataFrame({ |
|
|
'GDP': [100.0, 101.0, 102.0], |
|
|
'UNRATE': [3.5, 3.6, 3.7] |
|
|
}) |
|
|
|
|
|
stats = generate_statistics(df) |
|
|
|
|
|
assert 'GDP' in stats |
|
|
assert 'UNRATE' in stats |
|
|
assert 'mean' in stats['GDP'] |
|
|
assert 'std' in stats['GDP'] |
|
|
assert 'min' in stats['GDP'] |
|
|
assert 'max' in stats['GDP'] |
|
|
|
|
|
@patch('lambda.lambda_function.os.environ.get') |
|
|
@patch('lambda.lambda_function.boto3.client') |
|
|
def test_s3_report_storage(self, mock_boto3_client, mock_os_environ): |
|
|
"""Test S3 report storage""" |
|
|
|
|
|
mock_os_environ.side_effect = lambda key, default=None: { |
|
|
'FRED_API_KEY': 'test-api-key', |
|
|
'S3_BUCKET': 'fredmlv1' |
|
|
}.get(key, default) |
|
|
|
|
|
mock_s3_client = Mock() |
|
|
mock_lambda_client = Mock() |
|
|
mock_boto3_client.side_effect = [mock_s3_client, mock_lambda_client] |
|
|
|
|
|
from lambda.lambda_function import save_report_to_s3 |
|
|
|
|
|
|
|
|
report_data = { |
|
|
'report_id': 'test_report_123', |
|
|
'timestamp': '2024-01-01T00:00:00', |
|
|
'indicators': ['GDP'], |
|
|
'data': [] |
|
|
} |
|
|
|
|
|
result = save_report_to_s3(report_data, 'fredmlv1', 'test_report_123') |
|
|
|
|
|
|
|
|
mock_s3_client.put_object.assert_called_once() |
|
|
call_args = mock_s3_client.put_object.call_args |
|
|
assert call_args[1]['Bucket'] == 'fredmlv1' |
|
|
assert 'test_report_123' in call_args[1]['Key'] |
|
|
assert call_args[1]['ContentType'] == 'application/json' |