use daml_codegen::generator::{ModuleMatcher, RenderMethod};
use daml_codegen::renderer::quote_archive;
use daml_lf::DarFile;
const FIXTURE_DAR: &str = "../daml-lf/test_resources/TestingTypes-3_0_0-sdk_3_4_11-lf_2_1.dar";
fn render_demo_modules() -> String {
let dar = DarFile::from_file(FIXTURE_DAR).expect("read fixture DAR");
let matchers: Vec<&str> = vec![r"^Fuji\.Asset$", r"^Fuji\.Types$", r"^DA\.Internal\.Template$"];
dar.apply(|archive| {
let matcher = ModuleMatcher::new(&matchers).expect("matcher regex");
quote_archive(archive, &matcher, &RenderMethod::Full).to_string()
})
.expect("apply archive")
}
fn assert_contains(rendered: &str, needle: &str, why: &str) {
assert!(
rendered.contains(needle),
"{why}\n needle: `{needle}`\n not found in rendered output (first 400 chars: {})",
rendered.get(..400.min(rendered.len())).unwrap_or(""),
);
}
#[test]
fn template_id_uses_package_name() {
let r = render_demo_modules();
assert_contains(&r, "fn template_id () -> DamlIdentifier", "Asset must expose template_id()");
assert_contains(&r, "DamlIdentifier :: from_package_name", "template_id() must address by package-name");
assert!(!r.contains("from_package_id"), "renderer should not emit any from_package_id call in v2 codegen",);
}
#[test]
fn variant_record_payload_lives_in_synthetic_module() {
let r = render_demo_modules();
assert_contains(&r, "pub mod shape", "synthetic payload sub-module must be emitted");
assert_contains(&r, "pub struct Circle", "Circle payload record must be emitted");
assert_contains(&r, "pub struct Rectangle", "Rectangle payload record must be emitted");
assert_contains(
&r,
"Circle (crate :: testing_types :: fuji :: types :: shape :: Circle)",
"Shape::Circle variant must reference shape::Circle by full path",
);
assert_contains(
&r,
"Rectangle (crate :: testing_types :: fuji :: types :: shape :: Rectangle)",
"Shape::Rectangle variant must reference shape::Rectangle by full path",
);
}
#[test]
fn cross_package_paths_carry_package_name() {
let r = render_demo_modules();
assert_contains(&r, "ghc_stdlib_da_internal_template", "cross-package path must include the target package's name");
assert!(!r.contains("crate :: :: "), "rendered output contains an empty path segment (unresolved package_name)",);
}
#[test]
fn interface_trait_and_impl_emitted() {
let r = render_demo_modules();
assert_contains(&r, "pub trait Holding", "Holding interface must produce a Rust trait");
assert_contains(&r, "fn interface_id () -> DamlIdentifier", "interface trait must expose interface_id()");
assert_contains(
&r,
"impl crate :: testing_types :: fuji :: asset :: Holding for AssetContractId",
"AssetContractId must impl the Holding interface trait",
);
}
#[test]
fn interface_addressed_choice_methods_emitted() {
let r = render_demo_modules();
assert_contains(&r, "pub fn holding_reassign_command", "AssetContractId must expose holding_reassign_command");
assert_contains(
&r,
"pub fn holding_archive_command",
"AssetContractId must expose the inherited holding_archive_command",
);
assert_contains(
&r,
"as crate :: testing_types :: fuji :: asset :: Holding > :: interface_id ()",
"interface-addressed command method must dispatch via interface_id()",
);
}
#[test]
fn module_matcher_filters_out_unmatched_modules() {
let dar = DarFile::from_file(FIXTURE_DAR).expect("read fixture DAR");
let only_asset = dar
.apply(|archive| {
let matcher = ModuleMatcher::new(&[r"^Fuji\.Asset$"]).expect("matcher");
quote_archive(archive, &matcher, &RenderMethod::Full).to_string()
})
.expect("apply");
assert_contains(&only_asset, "pub struct Asset", "Fuji.Asset's Asset must render");
assert_contains(&only_asset, "pub trait Holding", "Fuji.Asset's Holding must render");
assert!(!only_asset.contains("pub enum Shape"), "Fuji.Types::Shape leaked despite matcher filtering it out",);
assert!(!only_asset.contains("pub struct Painted"), "Fuji.Types::Painted leaked despite matcher filtering it out",);
assert!(
!only_asset.contains("pub mod shape"),
"synthetic shape sub-module leaked despite Fuji.Types being filtered out",
);
}
#[test]
fn enum_constructors_round_trip_via_damlvalue() {
let r = render_demo_modules();
assert_contains(&r, "pub enum Color", "Color enum must be emitted");
for ctor in ["Red", "Green", "Blue"] {
assert_contains(
&r,
&format!("Color :: {ctor} => DamlValue :: new_enum"),
&format!("Color::{ctor} must round-trip via DamlValue::new_enum"),
);
assert_contains(
&r,
&format!("\"{ctor}\" => Ok (Color :: {ctor})"),
&format!("deserialize_from must accept the \"{ctor}\" constructor"),
);
}
}
#[test]
fn rendered_archive_compiles_to_balanced_braces() {
let r = render_demo_modules();
let open = r.matches('{').count();
let close = r.matches('}').count();
assert_eq!(open, close, "unbalanced braces in rendered output: {open} open, {close} close");
let paren_open = r.matches('(').count();
let paren_close = r.matches(')').count();
assert_eq!(paren_open, paren_close, "unbalanced parens in rendered output: {paren_open} open, {paren_close} close",);
}