#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use super::*;
mod size_estimation {
use super::*;
#[test]
fn test_estimate_alpine_base_image() {
let dockerfile = "FROM alpine:latest\nRUN echo hello";
let estimate = estimate_size(dockerfile);
assert_eq!(estimate.base_image, "alpine:latest");
assert!(estimate.base_image_size > 0);
assert!(estimate.base_image_size < 20_000_000); }
#[test]
fn test_estimate_ubuntu_base_image() {
let dockerfile = "FROM ubuntu:22.04\nRUN apt-get update";
let estimate = estimate_size(dockerfile);
assert_eq!(estimate.base_image, "ubuntu:22.04");
assert!(estimate.base_image_size > 50_000_000); }
#[test]
fn test_estimate_jupyter_base_image() {
let dockerfile = "FROM jupyter/scipy-notebook:latest\nUSER jovyan";
let estimate = estimate_size(dockerfile);
assert_eq!(estimate.base_image, "jupyter/scipy-notebook:latest");
assert!(estimate.base_image_size >= 3_000_000_000); }
#[test]
fn test_estimate_nvidia_cuda_image() {
let dockerfile = "FROM nvidia/cuda:12.0-devel-ubuntu22.04\nRUN echo hi";
let estimate = estimate_size(dockerfile);
assert_eq!(estimate.base_image, "nvidia/cuda:12.0-devel-ubuntu22.04");
assert!(estimate.base_image_size >= 8_000_000_000); }
#[test]
fn test_estimate_unknown_base_image_warns() {
let dockerfile = "FROM unknown-image:v1.0\nRUN echo hello";
let estimate = estimate_size(dockerfile);
assert!(!estimate.warnings.is_empty());
assert!(estimate.warnings[0].contains("Unknown base image"));
}
#[test]
fn test_estimate_apt_install_packages() {
let dockerfile = r#"
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y build-essential cmake git
"#;
let estimate = estimate_size(dockerfile);
let run_layer = estimate
.layer_estimates
.iter()
.find(|l| l.instruction == "RUN")
.expect("Should have RUN layer");
assert!(run_layer.estimated_size > 0);
assert!(run_layer.notes.is_some());
}
#[test]
fn test_estimate_pip_install_packages() {
let dockerfile = r#"
FROM python:3.11-slim
RUN pip install numpy pandas scipy matplotlib
"#;
let estimate = estimate_size(dockerfile);
let run_layer = estimate
.layer_estimates
.iter()
.find(|l| l.instruction == "RUN")
.expect("Should have RUN layer");
assert!(run_layer.estimated_size > 200_000_000); }
#[test]
fn test_estimate_total_size() {
let dockerfile = r#"
FROM python:3.11-slim
RUN apt-get update && apt-get install -y build-essential
RUN pip install numpy pandas
COPY . /app
"#;
let estimate = estimate_size(dockerfile);
let sum: u64 = estimate
.layer_estimates
.iter()
.map(|l| l.estimated_size)
.sum();
assert_eq!(estimate.total_estimated, sum);
}
#[test]
fn test_estimate_copy_unknown_size() {
let dockerfile = r#"
FROM alpine:latest
COPY ./data /app/data
"#;
let estimate = estimate_size(dockerfile);
let copy_layer = estimate
.layer_estimates
.iter()
.find(|l| l.instruction == "COPY")
.expect("Should have COPY layer");
assert_eq!(copy_layer.estimated_size, 0);
assert!(copy_layer
.notes
.as_ref()
.is_some_and(|n| n.contains("build context")));
}
#[test]
fn test_estimate_metadata_layers() {
let dockerfile = r#"
FROM alpine:latest
ENV FOO=bar
WORKDIR /app
EXPOSE 8080
USER nobody
HEALTHCHECK --interval=30s CMD curl -f http://localhost/
"#;
let estimate = estimate_size(dockerfile);
for layer in &estimate.layer_estimates {
if ["ENV", "WORKDIR", "EXPOSE", "USER", "HEALTHCHECK"]
.contains(&layer.instruction.as_str())
{
assert_eq!(layer.estimated_size, 0);
}
}
}
}
mod bloat_detection {
use super::*;
#[test]
fn test_detect_apt_cache_not_cleaned() {
let dockerfile = r#"
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3
"#;
let estimate = estimate_size(dockerfile);
assert!(!estimate.bloat_patterns.is_empty());
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE001");
assert!(bloat.is_some());
assert!(bloat.unwrap().description.contains("apt cache"));
}
#[test]
fn test_no_bloat_with_cleanup() {
let dockerfile = r#"
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3 && rm -rf /var/lib/apt/lists/*
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE001");
assert!(bloat.is_none());
}
#[test]
fn test_detect_missing_no_install_recommends() {
let dockerfile = r#"
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3 && rm -rf /var/lib/apt/lists/*
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE002");
assert!(bloat.is_some());
assert!(bloat
.unwrap()
.description
.contains("--no-install-recommends"));
}
#[test]
fn test_no_bloat_with_no_install_recommends() {
let dockerfile = r#"
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y --no-install-recommends python3 && rm -rf /var/lib/apt/lists/*
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE002");
assert!(bloat.is_none());
}
#[test]
fn test_detect_pip_without_no_cache_dir() {
let dockerfile = r#"
FROM python:3.11-slim
RUN pip install numpy pandas
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE003");
assert!(bloat.is_some());
assert!(bloat.unwrap().description.contains("--no-cache-dir"));
}
#[test]
fn test_no_bloat_with_no_cache_dir() {
let dockerfile = r#"
FROM python:3.11-slim
RUN pip install --no-cache-dir numpy pandas
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE003");
assert!(bloat.is_none());
}
#[test]
fn test_detect_npm_dev_dependencies() {
let dockerfile = r#"
FROM node:18
RUN npm install
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE004");
assert!(bloat.is_some());
}
#[test]
fn test_no_bloat_with_npm_production() {
let dockerfile = r#"
FROM node:18
RUN npm ci --only=production
"#;
let estimate = estimate_size(dockerfile);
let bloat = estimate.bloat_patterns.iter().find(|b| b.code == "SIZE004");
assert!(bloat.is_none());
}
}
mod platform_profiles {
use super::*;
#[test]
fn test_coursera_profile_limits() {
let profile = PlatformProfile::Coursera;
assert_eq!(profile.max_size_bytes(), 10 * 1024 * 1024 * 1024); assert_eq!(profile.max_memory_bytes(), 4 * 1024 * 1024 * 1024); assert_eq!(profile.max_startup_ms(), 60_000); }
#[test]
fn test_standard_profile_no_limits() {
let profile = PlatformProfile::Standard;
assert_eq!(profile.max_size_bytes(), u64::MAX);
assert_eq!(profile.max_memory_bytes(), u64::MAX);
assert_eq!(profile.max_startup_ms(), u64::MAX);
}
#[test]
fn test_lint_result_size_exceeds_coursera() {
let dockerfile = r#"FROM nvidia/cuda:12.0-devel-ubuntu22.04
RUN apt-get update && apt-get install -y build-essential cmake
RUN pip install tensorflow torch transformers"#;
let estimate = estimate_size(dockerfile);
let result = size_estimate_to_lint_result(&estimate, PlatformProfile::Coursera, false);
let has_size_warning = result
.diagnostics
.iter()
.any(|d| d.code == "SIZE-WARNING" || d.code == "SIZE-LIMIT");
let threshold = (10u64 * 1024 * 1024 * 1024) as f64 * 0.80;
if estimate.total_estimated as f64 > threshold {
assert!(
has_size_warning,
"Expected size warning for estimate of {} bytes",
estimate.total_estimated
);
}
}
#[test]
fn test_lint_result_size_within_coursera() {
let dockerfile = "FROM alpine:latest\nRUN echo hi";
let estimate = estimate_size(dockerfile);
let result = size_estimate_to_lint_result(&estimate, PlatformProfile::Coursera, false);
let has_size_limit = result.diagnostics.iter().any(|d| d.code == "SIZE-LIMIT");
assert!(!has_size_limit);
}
#[test]
fn test_lint_result_strict_mode() {
let dockerfile = "FROM nvidia/cuda:12.0-devel-ubuntu22.04\nRUN echo hi";
let estimate = estimate_size(dockerfile);
let result = size_estimate_to_lint_result(&estimate, PlatformProfile::Coursera, true);
let has_error = result
.diagnostics
.iter()
.any(|d| d.code == "SIZE-LIMIT" && d.severity == Severity::Error);
if estimate.total_estimated > PlatformProfile::Coursera.max_size_bytes() {
assert!(has_error);
}
}
}
mod formatting {
use super::*;
#[test]
fn test_format_human_readable() {
let dockerfile = r#"
FROM python:3.11-slim
RUN pip install numpy
COPY . /app
"#;
let estimate = estimate_size(dockerfile);
let output = format_size_estimate(&estimate, false);
assert!(output.contains("Image Size Analysis"));
assert!(output.contains("Base image:"));
assert!(output.contains("Estimated total:"));
}
#[test]
fn test_format_verbose() {
let dockerfile = r#"
FROM python:3.11-slim
RUN pip install numpy
COPY . /app
"#;
let estimate = estimate_size(dockerfile);
let output = format_size_estimate(&estimate, true);
assert!(output.contains("Layer Breakdown:"));
assert!(output.contains("[1]")); }
#[test]
fn test_format_json() {
let dockerfile = "FROM alpine:latest\nRUN echo hi";
let estimate = estimate_size(dockerfile);
let json = format_size_estimate_json(&estimate);
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Should be valid JSON");
assert!(parsed["base_image"].is_string());
assert!(parsed["total_estimated_bytes"].is_u64());
assert!(parsed["layers"].is_array());
}
include!("docker_profiler_tests_tests_lookup_base.rs");
}