use super::*;
use crate::instruction::OpCode;
#[test]
fn csharp_view_respects_manifest_metadata_and_parameters() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "Demo",
"abi": {
"methods": [
{
"name": "deploy-contract",
"parameters": [
{"name": "owner-name", "type": "Hash160"},
{"name": "amount", "type": "Integer"}
],
"returntype": "Void",
"offset": 0
}
],
"events": []
},
"permissions": [],
"trusts": "*",
"extra": {"Author": "Jane Doe", "Email": "jane@example.com"}
}
"#,
)
.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\", \"Jane Doe\")]"));
assert!(csharp.contains("[ManifestExtra(\"Email\", \"jane@example.com\")]"));
assert!(csharp
.contains("public static void deploy_contract(UInt160 owner_name, BigInteger amount)"));
}
#[test]
fn csharp_includes_offsetless_manifest_methods_as_stubs() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "Stubby",
"abi": {
"methods": [
{ "name": "main", "parameters": [], "returntype": "Void", "offset": 0 },
{ "name": "helper", "parameters": [], "returntype": "Void" }
],
"events": []
},
"permissions": [],
"trusts": "*"
}
"#,
)
.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("public static void helper()"),
"offsetless method should appear in C# skeleton"
);
assert!(
csharp.contains("NotImplementedException"),
"offsetless method should be emitted as a stub"
);
}
#[test]
fn csharp_includes_manifest_events() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "Events",
"abi": {
"methods": [
{ "name": "main", "parameters": [], "returntype": "Void", "offset": 0 }
],
"events": [
{
"name": "transfer-event",
"parameters": [
{ "name": "from", "type": "Hash160" },
{ "name": "to", "type": "Hash160" },
{ "name": "amount", "type": "Integer" }
]
}
]
},
"permissions": [],
"trusts": "*"
}
"#,
)
.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("[DisplayName(\"transfer-event\")]"));
assert!(
csharp.contains("public static event Action<UInt160, UInt160, BigInteger> transfer_event;")
);
}
#[test]
fn csharp_escapes_reserved_keywords() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "class",
"abi": {
"methods": [
{
"name": "class",
"parameters": [{ "name": "namespace", "type": "Integer" }],
"returntype": "Void",
"offset": 0
}
],
"events": []
},
"permissions": [],
"trusts": "*"
}
"#,
)
.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("public class @class : SmartContract"));
assert!(csharp.contains("public static void @class(BigInteger @namespace)"));
}
#[test]
fn csharp_mismatch_offset_emits_script_entry_and_manifest_method() {
let nef_bytes = build_nef(&[
OpCode::Push1.byte(),
OpCode::Ret.byte(),
OpCode::Push2.byte(),
OpCode::Ret.byte(),
]);
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "OffsetMismatch",
"abi": {
"methods": [
{
"name": "helper",
"parameters": [],
"returntype": "Integer",
"offset": 2
}
],
"events": []
},
"permissions": [],
"trusts": "*"
}
"#,
)
.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("public static object ScriptEntry()"),
"C# output should keep a synthetic script-entry method when ABI offsets do not include bytecode entry"
);
assert!(
csharp.contains("public static BigInteger helper()"),
"C# output should still emit the manifest method"
);
let before_helper = csharp
.split("public static BigInteger helper")
.next()
.expect("entry section present");
assert!(
before_helper.contains("// 0000: PUSH1"),
"script-entry body should contain bytecode from script start"
);
assert!(
!before_helper.contains("// 0002: PUSH2"),
"script-entry body should stop before helper method offset"
);
}
#[test]
fn csharp_missing_manifest_offset_uses_first_method_as_entry_signature() {
let nef_bytes = sample_nef();
let manifest = ContractManifest::from_json_str(
r#"
{
"name": "OffsetMissing",
"abi": {
"methods": [
{
"name": "main",
"parameters": [],
"returntype": "Integer"
}
],
"events": []
},
"permissions": [],
"trusts": "*"
}
"#,
)
.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("public static BigInteger main()"),
"C# output should reuse the first manifest method signature when offsets are missing"
);
assert!(
!csharp.contains("public static void ScriptEntry()"),
"synthetic ScriptEntry should not be emitted when the manifest omits entry offsets entirely"
);
assert!(
!csharp.contains("NotImplementedException"),
"the fallback entry method should not also be emitted as an offset-less stub"
);
}
#[test]
fn csharp_void_event_parameter_renders_as_object_not_void() {
let nef_bytes = build_nef(&[OpCode::Ret.byte()]);
let manifest = ContractManifest::from_json_str(
r#"{"name":"Ev","abi":{"methods":[
{"name":"main","returntype":"Void","offset":0,"parameters":[],"safe":false}
],"events":[{"name":"Boom","parameters":[{"name":"x","type":"Void"}]}]}}"#,
)
.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("Action<object> Boom"),
"void event parameter must render as object: {csharp}"
);
assert!(
!csharp.contains("Action<void>"),
"void must never appear as a generic type argument: {csharp}"
);
}