Skip to main content

alef_e2e/
lib.rs

1//! Fixture-driven e2e test generation for alef.
2//!
3//! This crate generates complete, runnable e2e test projects for all supported
4//! languages from JSON fixture files. Each project is self-contained with
5//! build files, test files, and local package references.
6
7pub mod codegen;
8pub mod config;
9pub mod escape;
10pub mod field_access;
11pub mod fixture;
12pub mod format;
13pub mod scaffold;
14pub mod validate;
15
16use alef_core::backend::GeneratedFile;
17use alef_core::config::AlefConfig;
18use alef_core::config::e2e::DependencyMode;
19use anyhow::{Context, Result};
20use config::E2eConfig;
21use fixture::{group_fixtures, load_fixtures};
22use std::path::Path;
23use tracing::{info, warn};
24use validate::Severity;
25
26/// Generate e2e test projects from fixtures.
27///
28/// Returns the list of generated files. The caller is responsible for writing
29/// them to disk.
30pub fn generate_e2e(
31    alef_config: &AlefConfig,
32    e2e_config: &E2eConfig,
33    languages: Option<&[String]>,
34) -> Result<Vec<GeneratedFile>> {
35    let fixtures_dir = Path::new(&e2e_config.fixtures);
36    let fixtures = load_fixtures(fixtures_dir)
37        .with_context(|| format!("failed to load fixtures from {}", fixtures_dir.display()))?;
38
39    info!("Loaded {} fixture(s) from {}", fixtures.len(), e2e_config.fixtures);
40
41    // Run semantic validation and emit warnings (don't block generation)
42    let diagnostics = validate::validate_fixtures_semantic(&fixtures, e2e_config, &e2e_config.languages);
43    for diag in &diagnostics {
44        match diag.severity {
45            Severity::Error => warn!("{}: {}", diag.file, diag.message),
46            Severity::Warning => warn!("{}: {}", diag.file, diag.message),
47        }
48    }
49
50    let all_groups = group_fixtures(&fixtures);
51
52    // In registry mode with a non-empty category filter, keep only the listed
53    // categories so the generated test apps contain a curated subset.
54    let groups: Vec<_> =
55        if e2e_config.dep_mode == DependencyMode::Registry && !e2e_config.registry.categories.is_empty() {
56            let allowed = &e2e_config.registry.categories;
57            all_groups
58                .into_iter()
59                .filter(|g| allowed.iter().any(|c| c == &g.category))
60                .collect()
61        } else {
62            all_groups
63        };
64
65    let generators = if let Some(langs) = languages {
66        codegen::generators_for(langs)
67    } else if !e2e_config.languages.is_empty() {
68        codegen::generators_for(&e2e_config.languages)
69    } else {
70        codegen::all_generators()
71    };
72
73    let mut all_files = Vec::new();
74    for generator in &generators {
75        let files = generator.generate(&groups, e2e_config, alef_config)?;
76        info!("  [{}] generated {} file(s)", generator.language_name(), files.len());
77        all_files.extend(files);
78    }
79
80    Ok(all_files)
81}