use super::*;
#[test]
fn test_SERVE_LAM_001_default_config() {
let config = LambdaConfig::default();
assert_eq!(config.memory_mb, 3008);
assert_eq!(config.timeout_secs, 60);
assert_eq!(config.region, "us-east-1");
assert_eq!(config.architecture, LambdaArchitecture::Arm64);
}
#[test]
fn test_SERVE_LAM_001_builder_pattern() {
let config = LambdaConfig::new("my-inference")
.with_model("pacha://llama3:8b-q4")
.with_memory(4096)
.with_timeout(120)
.with_region("eu-west-1")
.with_env("LOG_LEVEL", "debug")
.with_provisioned_concurrency(5);
assert_eq!(config.function_name, "my-inference");
assert_eq!(config.model_uri, "pacha://llama3:8b-q4");
assert_eq!(config.memory_mb, 4096);
assert_eq!(config.timeout_secs, 120);
assert_eq!(config.region, "eu-west-1");
assert_eq!(config.environment.get("LOG_LEVEL"), Some(&"debug".to_string()));
assert_eq!(config.provisioned_concurrency, 5);
}
#[test]
fn test_SERVE_LAM_001_memory_clamping() {
let config = LambdaConfig::new("test").with_memory(50);
assert_eq!(config.memory_mb, 128);
let config = LambdaConfig::new("test").with_memory(50000);
assert_eq!(config.memory_mb, 10240);
}
#[test]
fn test_SERVE_LAM_001_timeout_clamping() {
let config = LambdaConfig::new("test").with_timeout(0);
assert_eq!(config.timeout_secs, 1);
let config = LambdaConfig::new("test").with_timeout(2000);
assert_eq!(config.timeout_secs, 900);
}
#[test]
fn test_SERVE_LAM_001_validation() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0");
assert!(config.validate().is_ok());
let config = LambdaConfig::default();
assert!(matches!(config.validate(), Err(ConfigError::MissingField("function_name"))));
let config = LambdaConfig::new("test");
assert!(matches!(config.validate(), Err(ConfigError::MissingField("model_uri"))));
}
#[test]
fn test_SERVE_LAM_001_cost_estimation() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0").with_memory(3008);
let cost_1k = config.estimate_cost(1000, 500);
let cost_1m = config.estimate_cost(1_000_000, 500);
assert!(cost_1k > 0.0);
assert!(cost_1m > cost_1k);
assert!(cost_1m < 100.0); }
#[test]
fn test_SERVE_LAM_001_provisioned_cost() {
let config_no_prov =
LambdaConfig::new("test").with_model("pacha://model:1.0").with_provisioned_concurrency(0);
let config_with_prov =
LambdaConfig::new("test").with_model("pacha://model:1.0").with_provisioned_concurrency(10);
let cost_no_prov = config_no_prov.estimate_cost(1000, 500);
let cost_with_prov = config_with_prov.estimate_cost(1000, 500);
assert!(cost_with_prov > cost_no_prov);
}
#[test]
fn test_SERVE_LAM_002_runtime_identifiers() {
assert_eq!(LambdaRuntime::Provided.identifier(), "provided.al2023");
assert_eq!(LambdaRuntime::Python312.identifier(), "python3.12");
assert_eq!(LambdaRuntime::Container.identifier(), "container");
}
#[test]
fn test_SERVE_LAM_002_architecture_identifiers() {
assert_eq!(LambdaArchitecture::Arm64.identifier(), "arm64");
assert_eq!(LambdaArchitecture::X86_64.identifier(), "x86_64");
}
#[test]
fn test_SERVE_LAM_003_deployer_creation() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0");
let deployer = LambdaDeployer::new(config);
assert_eq!(deployer.status(), DeploymentStatus::NotDeployed);
assert!(deployer.function_arn().is_none());
}
#[test]
fn test_SERVE_LAM_003_deployer_validation() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0");
let deployer = LambdaDeployer::new(config);
assert!(deployer.validate().is_ok());
let config = LambdaConfig::default();
let deployer = LambdaDeployer::new(config);
assert!(deployer.validate().is_err());
}
#[test]
fn test_SERVE_LAM_003_deployer_estimate() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0").with_memory(3008);
let deployer = LambdaDeployer::new(config);
let estimate = deployer.estimate();
assert!(estimate.package_size_mb > 0);
assert!(estimate.estimated_cold_start_ms > 0);
assert!(estimate.monthly_cost_1m_req > estimate.monthly_cost_1k_req);
}
#[test]
fn test_SERVE_LAM_003_deployer_iac() {
let config = LambdaConfig::new("my-inference")
.with_model("pacha://llama3:8b")
.with_memory(4096)
.with_timeout(120);
let deployer = LambdaDeployer::new(config);
let iac = deployer.generate_iac();
assert!(iac.contains("my-inference"));
assert!(iac.contains("4096"));
assert!(iac.contains("120"));
assert!(iac.contains("pacha://llama3:8b"));
}
#[test]
fn test_SERVE_LAM_003_deployer_status_tracking() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0");
let mut deployer = LambdaDeployer::new(config);
assert_eq!(deployer.status(), DeploymentStatus::NotDeployed);
deployer.set_status(DeploymentStatus::Packaging);
assert_eq!(deployer.status(), DeploymentStatus::Packaging);
deployer.set_function_arn("arn:aws:lambda:us-east-1:123:function:test".to_string());
assert_eq!(deployer.status(), DeploymentStatus::Active);
assert_eq!(deployer.function_arn(), Some("arn:aws:lambda:us-east-1:123:function:test"));
}
#[test]
fn test_SERVE_LAM_004_client_creation() {
let client = LambdaClient::new("arn:aws:lambda:us-east-1:123:function:test", "us-east-1");
assert_eq!(client.function_arn(), "arn:aws:lambda:us-east-1:123:function:test");
assert_eq!(client.region(), "us-east-1");
assert_eq!(client.timeout(), Duration::from_secs(60));
}
#[test]
fn test_SERVE_LAM_004_client_with_timeout() {
let client = LambdaClient::new("test", "us-east-1").with_timeout(Duration::from_secs(120));
assert_eq!(client.timeout(), Duration::from_secs(120));
}
#[test]
fn test_SERVE_LAM_005_inference_request() {
let request = InferenceRequest::new("Hello, world!").with_max_tokens(100).with_temperature(0.7);
assert_eq!(request.input, "Hello, world!");
assert_eq!(request.max_tokens, Some(100));
assert!((request.temperature.unwrap() - 0.7).abs() < f32::EPSILON);
}
#[test]
fn test_SERVE_LAM_005_request_serialization() {
let request = InferenceRequest::new("test").with_max_tokens(50).with_temperature(0.5);
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("test"));
assert!(json.contains("50"));
let deserialized: InferenceRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.input, "test");
assert_eq!(deserialized.max_tokens, Some(50));
}
#[test]
fn test_SERVE_LAM_006_vpc_config() {
let vpc = VpcConfig::new(
vec!["subnet-123".to_string(), "subnet-456".to_string()],
vec!["sg-789".to_string()],
);
assert_eq!(vpc.subnet_ids.len(), 2);
assert_eq!(vpc.security_group_ids.len(), 1);
}
#[test]
fn test_SERVE_LAM_006_config_with_vpc() {
let vpc = VpcConfig::new(vec!["subnet-123".to_string()], vec!["sg-789".to_string()]);
let config = LambdaConfig::new("test").with_model("pacha://model:1.0").with_vpc(vpc);
assert!(config.vpc_config.is_some());
}
#[test]
fn test_SERVE_LAM_006_iac_with_vpc() {
let vpc = VpcConfig::new(vec!["subnet-123".to_string()], vec!["sg-456".to_string()]);
let config = LambdaConfig::new("test").with_model("pacha://model:1.0").with_vpc(vpc);
let deployer = LambdaDeployer::new(config);
let iac = deployer.generate_iac();
assert!(iac.contains("VpcConfig"));
assert!(iac.contains("subnet-123"));
assert!(iac.contains("sg-456"));
}
#[test]
fn test_SERVE_LAM_007_config_error_display() {
let err = ConfigError::MissingField("model_uri");
assert!(err.to_string().contains("model_uri"));
let err = ConfigError::InvalidMemory(50);
assert!(err.to_string().contains("50"));
}
#[test]
fn test_SERVE_LAM_007_deployment_error_display() {
let err = DeploymentError::ModelNotFound("pacha://missing:1.0".to_string());
assert!(err.to_string().contains("missing"));
let err = DeploymentError::PackageTooLarge { size_mb: 500, max_mb: 250 };
assert!(err.to_string().contains("500"));
}
#[test]
fn test_with_storage_clamping() {
let config = LambdaConfig::new("test").with_storage(100);
assert_eq!(config.ephemeral_storage_mb, 512);
let config = LambdaConfig::new("test").with_storage(50000);
assert_eq!(config.ephemeral_storage_mb, 10240);
let config = LambdaConfig::new("test").with_storage(2048);
assert_eq!(config.ephemeral_storage_mb, 2048); }
#[test]
fn test_with_architecture() {
let config = LambdaConfig::new("test").with_architecture(LambdaArchitecture::X86_64);
assert_eq!(config.architecture, LambdaArchitecture::X86_64);
}
#[test]
fn test_with_runtime() {
let config = LambdaConfig::new("test").with_runtime(LambdaRuntime::Container);
assert_eq!(config.runtime, LambdaRuntime::Container);
let config = LambdaConfig::new("test").with_runtime(LambdaRuntime::Python312);
assert_eq!(config.runtime, LambdaRuntime::Python312);
}
#[test]
fn test_deployer_config_accessor() {
let config = LambdaConfig::new("my-function").with_model("pacha://model:1.0").with_memory(5120);
let deployer = LambdaDeployer::new(config);
assert_eq!(deployer.config().function_name, "my-function");
assert_eq!(deployer.config().memory_mb, 5120);
}
#[test]
fn test_estimate_cold_start_python_runtime() {
let config = LambdaConfig::new("test")
.with_model("pacha://model:1.0")
.with_runtime(LambdaRuntime::Python312)
.with_memory(3008);
let deployer = LambdaDeployer::new(config);
let estimate = deployer.estimate();
assert!(estimate.estimated_cold_start_ms >= 2000);
}
#[test]
fn test_estimate_cold_start_container_runtime() {
let config = LambdaConfig::new("test")
.with_model("pacha://model:1.0")
.with_runtime(LambdaRuntime::Container)
.with_memory(3008);
let deployer = LambdaDeployer::new(config);
let estimate = deployer.estimate();
assert!(estimate.estimated_cold_start_ms >= 2500);
}
#[test]
fn test_estimate_cold_start_low_memory() {
let config = LambdaConfig::new("test").with_model("pacha://model:1.0").with_memory(1024);
let deployer = LambdaDeployer::new(config);
let estimate = deployer.estimate();
assert!(estimate.estimated_cold_start_ms > 2000);
}
#[test]
fn test_deployment_status_default() {
let status = DeploymentStatus::default();
assert_eq!(status, DeploymentStatus::NotDeployed);
}
#[test]
fn test_lambda_runtime_default() {
let runtime = LambdaRuntime::default();
assert_eq!(runtime, LambdaRuntime::Provided);
}
#[test]
fn test_lambda_architecture_default() {
let arch = LambdaArchitecture::default();
assert_eq!(arch, LambdaArchitecture::Arm64);
}
#[test]
fn test_config_error_equality() {
let err1 = ConfigError::MissingField("model_uri");
let err2 = ConfigError::MissingField("model_uri");
assert_eq!(err1, err2);
let err3 = ConfigError::InvalidMemory(50);
let err4 = ConfigError::InvalidMemory(50);
assert_eq!(err3, err4);
let err5 = ConfigError::InvalidTimeout(0);
assert_ne!(err3, err5);
}
#[test]
fn test_config_error_timeout_display() {
let err = ConfigError::InvalidTimeout(0);
let display = err.to_string();
assert!(display.contains("0"));
assert!(display.contains("1-900"));
}
#[test]
fn test_deployment_error_aws_error() {
let err = DeploymentError::AwsError("AccessDenied".to_string());
let display = err.to_string();
assert!(display.contains("AWS error"));
assert!(display.contains("AccessDenied"));
}
#[test]
fn test_deployment_error_config() {
let config_err = ConfigError::MissingField("function_name");
let err = DeploymentError::Config(config_err);
let display = err.to_string();
assert!(display.contains("Configuration error"));
assert!(display.contains("function_name"));
}
#[test]
fn test_inference_response_fields() {
let response = InferenceResponse {
output: "Hello!".to_string(),
tokens_generated: 10,
latency_ms: 150,
cold_start: true,
};
assert_eq!(response.output, "Hello!");
assert_eq!(response.tokens_generated, 10);
assert_eq!(response.latency_ms, 150);
assert!(response.cold_start);
}
#[test]
fn test_deployment_estimate_fields() {
let estimate = DeploymentEstimate {
package_size_mb: 500,
estimated_cold_start_ms: 2000,
monthly_cost_1k_req: 0.05,
monthly_cost_100k_req: 5.0,
monthly_cost_1m_req: 50.0,
};
assert_eq!(estimate.package_size_mb, 500);
assert_eq!(estimate.estimated_cold_start_ms, 2000);
assert!(estimate.monthly_cost_1m_req > estimate.monthly_cost_100k_req);
}
#[test]
fn test_inference_request_default_fields() {
let request = InferenceRequest::new("prompt");
assert_eq!(request.input, "prompt");
assert!(request.max_tokens.is_none());
assert!(request.temperature.is_none());
assert!(request.parameters.is_empty());
}
#[test]
fn test_deployment_status_variants() {
assert_eq!(DeploymentStatus::Packaging, DeploymentStatus::Packaging);
assert_eq!(DeploymentStatus::Uploading, DeploymentStatus::Uploading);
assert_eq!(DeploymentStatus::Deploying, DeploymentStatus::Deploying);
assert_eq!(DeploymentStatus::Active, DeploymentStatus::Active);
assert_eq!(DeploymentStatus::Failed, DeploymentStatus::Failed);
}
#[test]
fn test_validation_invalid_memory_low() {
let config = LambdaConfig {
function_name: "test".to_string(),
model_uri: "pacha://model:1.0".to_string(),
memory_mb: 50, ..Default::default()
};
assert!(matches!(config.validate(), Err(ConfigError::InvalidMemory(50))));
}
#[test]
fn test_validation_invalid_memory_high() {
let config = LambdaConfig {
function_name: "test".to_string(),
model_uri: "pacha://model:1.0".to_string(),
memory_mb: 99999,
..Default::default()
};
assert!(matches!(config.validate(), Err(ConfigError::InvalidMemory(99999))));
}
#[test]
fn test_validation_invalid_timeout_zero() {
let config = LambdaConfig {
function_name: "test".to_string(),
model_uri: "pacha://model:1.0".to_string(),
timeout_secs: 0,
..Default::default()
};
assert!(matches!(config.validate(), Err(ConfigError::InvalidTimeout(0))));
}
#[test]
fn test_validation_invalid_timeout_high() {
let config = LambdaConfig {
function_name: "test".to_string(),
model_uri: "pacha://model:1.0".to_string(),
timeout_secs: 1000,
..Default::default()
};
assert!(matches!(config.validate(), Err(ConfigError::InvalidTimeout(1000))));
}