use super::*;
use crate::instruction::OpCode;
#[test]
fn high_level_view_renders_manifest_groups_block() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "Demo",
"groups": [
{"pubkey": "02f49ce0c33aabbccdd", "signature": "BAt..."},
{"pubkey": "02b00b1eaaaabbbbcccc", "signature": "BAd..."}
],
"abi": {"methods": [], "events": []},
"permissions": [],
"trusts": "*",
"extra": {}
}
"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("groups {"),
"high-level should open a groups block:\n{high_level}"
);
assert!(high_level.contains("pubkey=02f49ce0c33aabbccdd"));
assert!(high_level.contains("pubkey=02b00b1eaaaabbbbcccc"));
assert!(!high_level.contains("BAt..."));
assert!(!high_level.contains("signature="));
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("// groups:"),
"C# header should open a groups comment block:\n{csharp}"
);
assert!(csharp.contains("// pubkey=02f49ce0c33aabbccdd"));
assert!(csharp.contains("// pubkey=02b00b1eaaaabbbbcccc"));
assert!(!csharp.contains("BAt..."));
assert!(!csharp.contains("signature="));
}
#[test]
fn csharp_view_renders_non_string_scalar_extra_metadata() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "Demo",
"abi": {"methods": [], "events": []},
"permissions": [],
"trusts": "*",
"extra": {
"Author": "Anon",
"Version": 2,
"Verified": true,
"Notes": null
}
}
"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(csharp.contains("[ManifestExtra(\"Author\", \"Anon\")]"));
assert!(csharp.contains("[ManifestExtra(\"Version\", \"2\")]"));
assert!(csharp.contains("[ManifestExtra(\"Verified\", \"true\")]"));
assert!(!csharp.contains("ManifestExtra(\"Notes\""));
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(high_level.contains("// Author: Anon"));
assert!(high_level.contains("// Version: 2"));
assert!(high_level.contains("// Verified: true"));
assert!(!high_level.contains("// Notes:"));
}
#[test]
fn csharp_multi_entry_typed_trusts_render_as_block() {
let nef_bytes = build_nef(&[OpCode::Ret.byte()]);
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "MultiTrust",
"abi": {
"methods": [
{
"name": "main",
"parameters": [],
"returntype": "Void",
"offset": 0
}
],
"events": []
},
"permissions": [],
"trusts": {
"hashes": ["0xabc", "0xdef"],
"groups": ["02foo", "02bar"]
}
}
"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("// trusts:"),
"multi-entry typed trusts should render as a block: {csharp}"
);
assert!(
csharp.contains("// hash:0xabc"),
"hash entries should be indented under the trusts block: {csharp}"
);
assert!(
csharp.contains("// hash:0xdef"),
"hash entries should be indented under the trusts block: {csharp}"
);
assert!(
csharp.contains("// group:02foo"),
"group entries should be indented under the trusts block: {csharp}"
);
assert!(
csharp.contains("// group:02bar"),
"group entries should be indented under the trusts block: {csharp}"
);
assert!(
!csharp.contains("// trusts = [hash:0xabc, hash:0xdef, group:02foo, group:02bar]"),
"multi-entry trusts must not stretch onto a single line: {csharp}"
);
}
#[test]
fn header_surfaces_nef_compiler_and_source_fields() {
let nef_bytes = sample_nef();
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, None, OutputFormat::All)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains(" // compiler: test"),
"high-level should surface the NEF compiler field:\n{high_level}"
);
assert!(
!high_level.contains(" // source:"),
"empty source should not emit a placeholder line:\n{high_level}"
);
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains(" // compiler: test"),
"C# header should surface the NEF compiler field at the C# indent:\n{csharp}"
);
assert!(
!csharp.contains("// source:"),
"empty source should not emit a placeholder line in C#:\n{csharp}"
);
}
#[test]
fn csharp_header_renders_method_tokens_block() {
let stdlib_hash: [u8; 20] = [
0xC0, 0xEF, 0x39, 0xCE, 0xE0, 0xE4, 0xE9, 0x25, 0xC6, 0xC2, 0xA0, 0x6A, 0x79, 0xE1, 0x44,
0x0D, 0xD8, 0x6F, 0xCE, 0xAC,
];
let nef_bytes = build_nef_with_single_token(
&[OpCode::Ret.byte()],
stdlib_hash,
"Serialize",
1,
true,
0x0F,
);
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, None, OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("// method tokens declared in NEF:"),
"C# header should open the method tokens comment block:\n{csharp}"
);
assert!(
csharp.contains(
"// Serialize (StdLib::Serialize) hash=C0EF39CEE0E4E925C6C2A06A79E1440DD86FCEAC params=1 returns=true flags=0x0F"
),
"method token line should match high-level layout (with native contract label):\n{csharp}"
);
assert!(
csharp.contains("(ReadStates|WriteStates|AllowCall|AllowNotify)"),
"call flags should be described:\n{csharp}"
);
}
#[test]
fn csharp_header_omits_method_tokens_block_when_none() {
let nef_bytes = sample_nef();
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, None, OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
!csharp.contains("method tokens declared in NEF"),
"no token block expected when NEF has no method tokens:\n{csharp}"
);
}
#[test]
fn csharp_single_entry_typed_trusts_stay_on_one_line() {
let nef_bytes = build_nef(&[OpCode::Ret.byte()]);
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "SingleTrust",
"abi": {
"methods": [
{
"name": "main",
"parameters": [],
"returntype": "Void",
"offset": 0
}
],
"events": []
},
"permissions": [],
"trusts": { "groups": ["02abcdef"] }
}
"#,
)
.expect("manifest parsed");
let decompilation = Decompiler::new()
.decompile_bytes_with_manifest(&nef_bytes, Some(manifest), OutputFormat::All)
.expect("decompile succeeds");
let csharp = decompilation.csharp.as_deref().expect("csharp output");
assert!(
csharp.contains("// trusts = [group:02abcdef]"),
"single-entry trusts should stay compact on one line: {csharp}"
);
assert!(
!csharp.contains("// trusts:"),
"single-entry trusts should not break into a block: {csharp}"
);
}