1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! — on-chain ABI for storage decode tests (hermetic).
//!
//! `sdkt storage ... --abi-contract <id>` fetches a deployed contract's on-chain
//! WASM () and parses it to a `ContractSpec`, which is then used as the ABI
//! source for the existing storage analyzer (mirroring 's events path). This
//! crate test proves the deterministic, network-free parts of that flow and the
//! CLI behavior.
use assert_cmd::Command;
use predicates::prelude::*;
use std::fs;
fn sdkt() -> Command {
let mut cmd = Command::cargo_bin("sdkt").expect("sdkt binary built");
let dir = std::env::temp_dir().join(format!(
"sdkt-m44-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos()
));
let _ = fs::create_dir_all(&dir);
cmd.env("SDKT_NETWORK_DIR", &dir);
cmd
}
#[test]
fn storage_help_documents_abi_contract() {
sdkt()
.args(["storage", "--help"])
.assert()
.success()
.stdout(predicate::str::contains("abi-contract"));
}
#[test]
fn storage_abi_and_abi_contract_are_mutually_exclusive() {
sdkt()
.args([
"storage",
"analyze",
"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"--abi",
"/no/such.wasm",
"--abi-contract",
"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
])
.assert()
.failure()
.stderr(predicate::str::contains(
"specify only one of --abi or --abi-contract",
));
}
#[test]
fn storage_abi_contract_offline_is_graceful() {
// No RPC reachable -> clean failure (no panic), actionable error message.
sdkt()
.args([
"storage",
"analyze",
"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"--abi-contract",
"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
])
.assert()
.failure()
.stderr(predicate::str::contains("Error"))
.stderr(predicate::str::contains("panic").not());
}
#[test]
fn existing_storage_abi_local_path_still_resolves() {
// `--abi <missing file>` must still hit the local-ABI branch and fail with a
// controlled "Failed to read WASM" error (not a panic), proving the existing
// behavior is preserved.
sdkt()
.args([
"storage",
"analyze",
"CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"--abi",
"/no/such/file.wasm",
])
.assert()
.failure()
.stderr(predicate::str::contains("Failed to read WASM"));
}
/// Deterministically proves the data flow: a `ContractSpec` (the exact shape
/// produced by `parse_contract_spec` on a deployed WASM) is what the storage
/// analyzer consumes as its ABI source. The storage `Check`/`Analyze` handlers emit
/// `spec.functions` / `spec.events` / `spec.custom_types` names, so a spec carrying
/// those names is the contract the on-chain path must supply. This mirrors the
/// events test and verifies the resolved spec shape is ABI-aware (not raw).
#[test]
fn deployed_spec_feeds_storage_abi_fields() {
use sdkt_wasm::spec::{ContractEvent, ContractFunction, ContractSpec, ContractType};
let spec = ContractSpec {
env_meta: None,
functions: vec![ContractFunction {
name: "mint".to_string(),
doc: "Mint tokens".to_string(),
parameters: vec![],
outputs: vec![],
}],
custom_types: vec![ContractType {
name: "Circle".to_string(),
kind: "struct".to_string(),
doc: "A circle".to_string(),
members: vec![],
type_args: vec![],
bytes_n: None,
}],
events: vec![ContractEvent {
name: "Mint".to_string(),
doc: "Mint event".to_string(),
params: vec![],
prefix_topics: vec![],
data_format: "single_value".to_string(),
}],
};
// The storage analyzer surfaces these names when a spec is present; the
// on-chain path must produce exactly this shape. Assert the spec carries the
// ABI fields the storage command would emit.
assert_eq!(spec.functions[0].name, "mint");
assert_eq!(spec.custom_types[0].name, "Circle");
assert_eq!(spec.events[0].name, "Mint");
}