use fiscal::format_utils::{
format_cents, format_cents_2, format_cents_10, format_cents_or_none, format_cents_or_zero,
format_decimal, format_rate, format_rate_4, format_rate4, format_rate4_or_zero,
};
use fiscal::newtypes::Cents;
use fiscal::state_codes::{get_state_by_code, get_state_code};
use fiscal::tax_element::{TaxElement, TaxField, serialize_tax_element};
use fiscal::tax_icms::{IcmsTotals, create_icms_totals, merge_icms_totals};
use fiscal::xml_utils::{escape_xml, extract_xml_tag_value, tag};
use insta::{assert_debug_snapshot, assert_snapshot};
#[test]
fn snapshot_simple_tag() {
assert_snapshot!(tag("xNome", &[], "Test Company".into()));
}
#[test]
fn snapshot_tag_empty_children() {
assert_snapshot!(tag("emit", &[], vec![].into()));
}
#[test]
fn snapshot_tag_with_text_escaping() {
assert_snapshot!(tag("xNome", &[], "A & B <Corp> \"LLC\"".into()));
}
#[test]
fn snapshot_tag_with_attrs() {
assert_snapshot!(tag(
"det",
&[("nItem", "1")],
vec![tag(
"prod",
&[],
vec![
tag("cProd", &[], "001".into()),
tag("xProd", &[], "Widget".into()),
]
.into(),
),]
.into(),
));
}
#[test]
fn snapshot_tag_with_multiple_attrs() {
assert_snapshot!(tag(
"infNFe",
&[("versao", "4.00"), ("Id", "NFe35...")],
vec![tag("ide", &[], vec![].into())].into(),
));
}
#[test]
fn snapshot_tag_self_closing_like() {
assert_snapshot!(tag("xMotivo", &[], vec![].into()));
}
#[test]
fn snapshot_escape_xml_ampersand() {
assert_snapshot!(escape_xml("Tom & Jerry"));
}
#[test]
fn snapshot_escape_xml_all_chars() {
assert_snapshot!(escape_xml("<tag attr=\"val\" & 'single'>"));
}
#[test]
fn snapshot_escape_xml_clean_string() {
assert_snapshot!(escape_xml("Normal text 123"));
}
#[test]
fn snapshot_extract_xml_tag_value() {
let xml = "<nfeProc><protNFe><infProt><cStat>100</cStat></infProt></protNFe></nfeProc>";
assert_snapshot!(extract_xml_tag_value(xml, "cStat").unwrap());
}
#[test]
fn snapshot_extract_xml_tag_value_not_found() {
let xml = "<root><a>1</a></root>";
assert_debug_snapshot!(extract_xml_tag_value(xml, "missing"));
}
#[test]
fn snapshot_tax_element_icms00() {
let element = TaxElement {
outer_tag: Some("ICMS".into()),
outer_fields: vec![],
variant_tag: "ICMS00".into(),
fields: vec![
TaxField::new("orig", "0"),
TaxField::new("CST", "00"),
TaxField::new("modBC", "3"),
TaxField::new("vBC", "100.00"),
TaxField::new("pICMS", "18.0000"),
TaxField::new("vICMS", "18.00"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_pis_aliq() {
let element = TaxElement {
outer_tag: Some("PIS".into()),
outer_fields: vec![],
variant_tag: "PISAliq".into(),
fields: vec![
TaxField::new("CST", "01"),
TaxField::new("vBC", "100.00"),
TaxField::new("pPIS", "1.6500"),
TaxField::new("vPIS", "1.65"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_ipi_with_outer_fields() {
let element = TaxElement {
outer_tag: Some("IPI".into()),
outer_fields: vec![TaxField::new("cEnq", "999")],
variant_tag: "IPITrib".into(),
fields: vec![
TaxField::new("CST", "50"),
TaxField::new("vBC", "100.00"),
TaxField::new("pIPI", "5.0000"),
TaxField::new("vIPI", "5.00"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_icms_uf_dest_no_outer() {
let element = TaxElement {
outer_tag: None,
outer_fields: vec![],
variant_tag: "ICMSUFDest".into(),
fields: vec![
TaxField::new("vBCUFDest", "1000.00"),
TaxField::new("vBCFCPUFDest", "1000.00"),
TaxField::new("pFCPUFDest", "2.0000"),
TaxField::new("pICMSUFDest", "18.0000"),
TaxField::new("pICMSInter", "12.0000"),
TaxField::new("pICMSInterPart", "100.0000"),
TaxField::new("vFCPUFDest", "20.00"),
TaxField::new("vICMSUFDest", "60.00"),
TaxField::new("vICMSUFRemet", "0.00"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_ii_no_outer() {
let element = TaxElement {
outer_tag: None,
outer_fields: vec![],
variant_tag: "II".into(),
fields: vec![
TaxField::new("vBC", "5000.00"),
TaxField::new("vDespAdu", "200.00"),
TaxField::new("vII", "800.00"),
TaxField::new("vIOF", "50.00"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_cofins() {
let element = TaxElement {
outer_tag: Some("COFINS".into()),
outer_fields: vec![],
variant_tag: "COFINSAliq".into(),
fields: vec![
TaxField::new("CST", "01"),
TaxField::new("vBC", "100.00"),
TaxField::new("pCOFINS", "7.6000"),
TaxField::new("vCOFINS", "7.60"),
],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_tax_element_xml_escaping_in_fields() {
let element = TaxElement {
outer_tag: None,
outer_fields: vec![],
variant_tag: "obs".into(),
fields: vec![TaxField::new("xTexto", "Value > 100 & special <chars>")],
};
assert_snapshot!(serialize_tax_element(&element));
}
#[test]
fn snapshot_format_cents_zero() {
assert_snapshot!(format_cents(0, 2), @"0.00");
}
#[test]
fn snapshot_format_cents_1050() {
assert_snapshot!(format_cents_2(1050), @"10.50");
}
#[test]
fn snapshot_format_cents_123456() {
assert_snapshot!(format_cents_2(123456), @"1234.56");
}
#[test]
fn snapshot_format_cents_negative() {
assert_snapshot!(format_cents_2(-500), @"-5.00");
}
#[test]
fn snapshot_format_cents_10_places() {
assert_snapshot!(format_cents_10(1050), @"10.5000000000");
}
#[test]
fn snapshot_format_cents_1_cent() {
assert_snapshot!(format_cents_2(1), @"0.01");
}
#[test]
fn snapshot_format_decimal() {
#[allow(clippy::approx_constant)]
let pi_approx = 3.14159;
assert_snapshot!(format_decimal(pi_approx, 4), @"3.1416");
}
#[test]
fn snapshot_format_rate_18_percent() {
assert_snapshot!(format_rate(1800, 4), @"18.0000");
}
#[test]
fn snapshot_format_rate_12_percent() {
assert_snapshot!(format_rate_4(1200), @"12.0000");
}
#[test]
fn snapshot_format_rate_7_percent() {
assert_snapshot!(format_rate(700, 4), @"7.0000");
}
#[test]
fn snapshot_format_rate_4_percent() {
assert_snapshot!(format_rate(400, 4), @"4.0000");
}
#[test]
fn snapshot_format_rate_zero() {
assert_snapshot!(format_rate(0, 4), @"0.0000");
}
#[test]
fn snapshot_format_rate4_pis() {
assert_snapshot!(format_rate4(16500), @"1.6500");
}
#[test]
fn snapshot_format_rate4_cofins() {
assert_snapshot!(format_rate4(76000), @"7.6000");
}
#[test]
fn snapshot_format_rate4_zero() {
assert_snapshot!(format_rate4(0), @"0.0000");
}
#[test]
fn snapshot_format_cents_or_none_some() {
assert_debug_snapshot!(format_cents_or_none(Some(1050), 2), @r#"
Some(
"10.50",
)
"#);
}
#[test]
fn snapshot_format_cents_or_none_none() {
assert_debug_snapshot!(format_cents_or_none(None, 2), @"None");
}
#[test]
fn snapshot_format_cents_or_zero_some() {
assert_snapshot!(format_cents_or_zero(Some(999), 2), @"9.99");
}
#[test]
fn snapshot_format_cents_or_zero_none() {
assert_snapshot!(format_cents_or_zero(None, 2), @"0.00");
}
#[test]
fn snapshot_format_rate4_or_zero_some() {
assert_snapshot!(format_rate4_or_zero(Some(16500)), @"1.6500");
}
#[test]
fn snapshot_format_rate4_or_zero_none() {
assert_snapshot!(format_rate4_or_zero(None), @"0.0000");
}
#[test]
fn snapshot_all_state_codes() {
let mut mappings: Vec<String> = vec![
"AC", "AL", "AM", "AP", "BA", "CE", "DF", "ES", "GO", "MA", "MG", "MS", "MT", "PA", "PB",
"PE", "PI", "PR", "RJ", "RN", "RO", "RR", "RS", "SC", "SE", "SP", "TO",
]
.into_iter()
.map(|uf| format!("{uf} -> {}", get_state_code(uf).unwrap()))
.collect();
mappings.sort();
assert_snapshot!(mappings.join("\n"));
}
#[test]
fn snapshot_all_reverse_lookups() {
let codes = vec![
"11", "12", "13", "14", "15", "16", "17", "21", "22", "23", "24", "25", "26", "27", "28",
"29", "31", "32", "33", "35", "41", "42", "43", "50", "51", "52", "53",
];
let mut mappings: Vec<String> = codes
.into_iter()
.map(|code| format!("{code} -> {}", get_state_by_code(code).unwrap()))
.collect();
mappings.sort();
assert_snapshot!(mappings.join("\n"));
}
#[test]
fn snapshot_state_code_unknown() {
assert_debug_snapshot!(get_state_code("XX"), @r###"
Err(
InvalidStateCode(
"XX",
),
)
"###);
}
#[test]
fn snapshot_state_by_code_unknown() {
assert_debug_snapshot!(get_state_by_code("99"), @r###"
Err(
InvalidStateCode(
"99",
),
)
"###);
}
#[test]
fn snapshot_specific_state_pr() {
assert_snapshot!(get_state_code("PR").unwrap(), @"41");
}
#[test]
fn snapshot_specific_state_sp() {
assert_snapshot!(get_state_code("SP").unwrap(), @"35");
}
#[test]
fn snapshot_reverse_lookup_41() {
assert_snapshot!(get_state_by_code("41").unwrap(), @"PR");
}
#[test]
fn snapshot_reverse_lookup_35() {
assert_snapshot!(get_state_by_code("35").unwrap(), @"SP");
}
#[test]
fn snapshot_create_icms_totals() {
let totals = create_icms_totals();
assert_debug_snapshot!(totals);
}
#[test]
fn snapshot_merge_icms_totals() {
let mut target = create_icms_totals();
let mut source = IcmsTotals::new();
source.v_bc = Cents(10000);
source.v_icms = Cents(1800);
source.v_bc_st = Cents(5000);
source.v_st = Cents(900);
source.v_fcp = Cents(200);
source.v_fcp_st = Cents(100);
source.v_fcp_st_ret = Cents(50);
source.v_fcp_uf_dest = Cents(300);
source.v_icms_uf_dest = Cents(600);
merge_icms_totals(&mut target, &source);
assert_debug_snapshot!(target);
}
#[test]
fn snapshot_merge_icms_totals_accumulates() {
let mut target = IcmsTotals::new();
target.v_bc = Cents(10000);
target.v_icms = Cents(1800);
target.v_icms_deson = Cents(100);
target.v_fcp = Cents(200);
let mut source = IcmsTotals::new();
source.v_bc = Cents(20000);
source.v_icms = Cents(3600);
source.v_icms_deson = Cents(200);
source.v_bc_st = Cents(5000);
source.v_st = Cents(900);
source.v_fcp = Cents(400);
source.v_fcp_st = Cents(100);
source.v_fcp_st_ret = Cents(50);
source.q_bc_mono = 1000;
source.v_icms_mono = Cents(500);
merge_icms_totals(&mut target, &source);
assert_debug_snapshot!(target);
}