caixa-core 0.1.6

Manifest types, layout invariants, and version contract for the caixa tatara-lisp package system.
Documentation
//! Integration tests: caixa.lisp source → typed `Caixa` via tatara-lisp derive.

use caixa_core::{Caixa, CaixaKind, DepSource, LayoutInvariants, StandardLayout};
use pretty_assertions::assert_eq;
use std::path::{Path, PathBuf};

#[test]
fn parse_minimal_caixa() {
    let src = r#"
(defcaixa
  :nome "pangea-tatara-aws"
  :versao "0.1.0"
  :kind Biblioteca
  :edicao "2026"
  :bibliotecas ("lib/pangea-tatara-aws.lisp"))
"#;
    let c = Caixa::from_lisp(src).expect("parse minimal");
    assert_eq!(c.nome, "pangea-tatara-aws");
    assert_eq!(c.versao, "0.1.0");
    assert_eq!(c.kind, CaixaKind::Biblioteca);
    assert_eq!(c.edicao.as_deref(), Some("2026"));
    assert_eq!(
        c.bibliotecas,
        vec!["lib/pangea-tatara-aws.lisp".to_string()]
    );
    assert!(c.deps.is_empty());
    assert!(c.deps_dev.is_empty());
    assert!(c.autores.is_empty());
}

#[test]
fn parse_full_caixa_with_deps() {
    let src = r#"
(defcaixa
  :nome "demo"
  :versao "0.1.0"
  :kind Biblioteca
  :descricao "A demo caixa"
  :repositorio "github:pleme-io/demo"
  :licenca "MIT"
  :autores ("alice" "bob")
  :etiquetas ("demo" "example")
  :deps ((:nome "caixa-teia" :versao "^0.1")
         (:nome "tatara-lisp" :versao "*"
          :fonte (:tipo git :repo "github:pleme-io/tatara" :tag "v0.9.0")))
  :deps-dev ((:nome "tatara-check" :versao "*")))
"#;
    let c = Caixa::from_lisp(src).expect("parse full");
    assert_eq!(c.descricao.as_deref(), Some("A demo caixa"));
    assert_eq!(c.repositorio.as_deref(), Some("github:pleme-io/demo"));
    assert_eq!(c.licenca.as_deref(), Some("MIT"));
    assert_eq!(c.autores, vec!["alice".to_string(), "bob".to_string()]);
    assert_eq!(c.etiquetas, vec!["demo".to_string(), "example".to_string()]);

    assert_eq!(c.deps.len(), 2);
    assert_eq!(c.deps[0].nome, "caixa-teia");
    assert_eq!(c.deps[0].versao, "^0.1");
    assert!(c.deps[0].fonte.is_none());

    assert_eq!(c.deps[1].nome, "tatara-lisp");
    match &c.deps[1].fonte {
        Some(DepSource::Git { repo, tag, .. }) => {
            assert_eq!(repo, "github:pleme-io/tatara");
            assert_eq!(tag.as_deref(), Some("v0.9.0"));
        }
        other => panic!("expected Git source, got {other:?}"),
    }

    assert_eq!(c.deps_dev.len(), 1);
    assert_eq!(c.deps_dev[0].nome, "tatara-check");
}

#[test]
fn missing_required_errors() {
    let src = r#"(defcaixa :nome "x")"#;
    assert!(Caixa::from_lisp(src).is_err());
}

#[test]
fn wrong_head_errors() {
    let src = r#"(defsomething :nome "x" :versao "0.1.0" :kind Biblioteca)"#;
    assert!(Caixa::from_lisp(src).is_err());
}

#[test]
fn binario_kind_parses() {
    let src = r#"
(defcaixa
  :nome "feira"
  :versao "0.1.0"
  :kind Binario
  :exe ("exe/feira"))
"#;
    let c = Caixa::from_lisp(src).expect("parse binario");
    assert_eq!(c.kind, CaixaKind::Binario);
    assert_eq!(c.exe, vec!["exe/feira".to_string()]);
}

#[test]
fn servico_kind_parses() {
    let src = r#"
(defcaixa
  :nome "watcher"
  :versao "0.1.0"
  :kind Servico
  :servicos ("servicos/watcher.lisp"))
"#;
    let c = Caixa::from_lisp(src).expect("parse servico");
    assert_eq!(c.kind, CaixaKind::Servico);
    assert_eq!(c.servicos, vec!["servicos/watcher.lisp".to_string()]);
}

#[test]
fn layout_validates_parsed_biblioteca() {
    let src = r#"
(defcaixa
  :nome "demo"
  :versao "0.1.0"
  :kind Biblioteca
  :bibliotecas ("lib/demo.lisp"))
"#;
    let c = Caixa::from_lisp(src).unwrap();
    let root = PathBuf::from("/virtual");
    let manifest = root.join("caixa.lisp");
    let lib = root.join("lib/demo.lisp");
    let layout = StandardLayout::new().with_path_exists(move |p| p == manifest || p == lib);
    layout.verify(&c, &root).expect("layout passes");
}

#[test]
fn layout_rejects_binario_without_exe_entry() {
    let src = r#"
(defcaixa
  :nome "demo-bin"
  :versao "0.1.0"
  :kind Binario)
"#;
    let c = Caixa::from_lisp(src).unwrap();
    let layout = StandardLayout::new().with_path_exists(|_| true);
    let err = layout.verify(&c, Path::new("/virtual")).unwrap_err();
    assert!(format!("{err}").contains("no :exe entries"));
}