1use serde::{Deserialize, Serialize};
7use crate::codec::transaction_codec::{TransactionHeader, TransactionSignature};
8
9pub const TYPE_NAMES: &[&str] = &[
11 "TransactionEnvelope",
13 "TransactionHeader",
14 "TransactionSignature",
15 "TransactionKeyPage",
16
17 "TokenRecipient",
19 "KeySpec",
20
21 "StatusResponse",
23 "NodeInfo",
24 "QueryResponse",
25 "TransactionResponse",
26 "TransactionResult",
27 "Event",
28 "Attribute",
29 "SignedTransaction",
30 "Signature",
31 "Account",
32 "FaucetResponse",
33
34 "V3SubmitRequest",
36 "V3SubmitResponse",
37 "SubmitResult",
38 "V3Signature",
39
40 "ProtocolTransactionEnvelope",
42 "ProtocolTransactionSignature",
43 "ProtocolTransactionHeader",
44
45 "BinaryReader",
47 "BinaryWriter",
48 "EncodingError",
49 "DecodingError",
50 "FieldReader",
51
52 "Ed25519Signer",
54
55 "AccOptions",
57];
58
59pub trait SampleGenerator {
61 fn generate_sample() -> Self;
63
64 fn generate_samples() -> Vec<Self> where Self: Sized {
66 vec![Self::generate_sample()]
67 }
68}
69
70pub trait RoundtripTestable: Serialize + for<'de> Deserialize<'de> + Clone + PartialEq {
72 fn test_json_roundtrip(&self) -> Result<(), String> {
74 let json = serde_json::to_string(self)
76 .map_err(|e| format!("Failed to serialize to JSON: {}", e))?;
77
78 let deserialized: Self = serde_json::from_str(&json)
80 .map_err(|e| format!("Failed to deserialize from JSON: {}", e))?;
81
82 let json2 = serde_json::to_string(&deserialized)
84 .map_err(|e| format!("Failed to re-serialize to JSON: {}", e))?;
85
86 if self != &deserialized {
88 return Err("Deserialized object differs from original".to_string());
89 }
90
91 if json != json2 {
93 return Err(format!(
94 "Re-serialized JSON differs from original\nOriginal: {}\nRe-serialized: {}",
95 json, json2
96 ));
97 }
98
99 Ok(())
100 }
101
102 fn test_binary_roundtrip(&self) -> Result<(), String> {
104 Ok(())
106 }
107}
108
109impl SampleGenerator for crate::codec::TransactionEnvelope {
112 fn generate_sample() -> Self {
113 use crate::codec::{TransactionHeader, TransactionSignature};
114
115 Self {
116 header: TransactionHeader {
117 principal: "acc://alice.acme/tokens".to_string(),
118 initiator: Some("acc://alice.acme".to_string()),
119 timestamp: 1234567890123,
120 nonce: Some(42),
121 memo: Some("Test transaction".to_string()),
122 metadata: Some(serde_json::json!({"test": "metadata"})),
123 },
124 body: serde_json::json!({
125 "type": "send-tokens",
126 "to": [{
127 "url": "acc://bob.acme/tokens",
128 "amount": "1000"
129 }]
130 }),
131 signatures: vec![TransactionSignature::generate_sample()],
132 }
133 }
134
135 fn generate_samples() -> Vec<Self> {
136 vec![
137 Self::generate_sample(),
138 Self {
140 header: TransactionHeader {
141 principal: "acc://test.acme".to_string(),
142 initiator: None,
143 timestamp: 1000000000000,
144 nonce: None,
145 memo: None,
146 metadata: None,
147 },
148 body: serde_json::json!({"type": "create-identity"}),
149 signatures: vec![],
150 },
151 Self {
153 header: TransactionHeader {
154 principal: "acc://complex.acme/tokens".to_string(),
155 initiator: Some("acc://initiator.acme".to_string()),
156 timestamp: 9999999999999,
157 nonce: Some(999999),
158 memo: Some("Complex test with unicode: test nono".to_string()),
159 metadata: Some(serde_json::json!({
160 "version": "1.0",
161 "flags": ["test", "complex"],
162 "nested": {"deep": {"value": 42}}
163 })),
164 },
165 body: serde_json::json!({
166 "type": "send-tokens",
167 "to": [
168 {"url": "acc://recipient1.acme/tokens", "amount": "100"},
169 {"url": "acc://recipient2.acme/tokens", "amount": "200"},
170 {"url": "acc://recipient3.acme/tokens", "amount": "300"}
171 ]
172 }),
173 signatures: vec![
174 TransactionSignature::generate_sample(),
175 TransactionSignature {
176 signature: vec![0x99; 64],
177 signer: "acc://signer2.acme/book/1".to_string(),
178 timestamp: 1234567890124,
179 vote: Some("approve".to_string()),
180 public_key: Some(vec![0xAA; 32]),
181 key_page: None,
182 }
183 ],
184 }
185 ]
186 }
187}
188
189impl SampleGenerator for crate::codec::TransactionHeader {
190 fn generate_sample() -> Self {
191 Self {
192 principal: "acc://sample.acme/tokens".to_string(),
193 initiator: Some("acc://sample.acme".to_string()),
194 timestamp: 1234567890123,
195 nonce: Some(1),
196 memo: Some("Sample header".to_string()),
197 metadata: Some(serde_json::json!({"sample": true})),
198 }
199 }
200
201 fn generate_samples() -> Vec<Self> {
202 vec![
203 Self::generate_sample(),
204 Self {
206 principal: "acc://minimal.acme".to_string(),
207 initiator: None,
208 timestamp: 0,
209 nonce: None,
210 memo: None,
211 metadata: None,
212 },
213 Self {
215 principal: "acc://üñíçødé.acme/tøkeñs".to_string(),
216 initiator: Some("acc://spëçîál.acme".to_string()),
217 timestamp: u64::MAX,
218 nonce: Some(u64::MAX),
219 memo: Some("Unicode test: star nono cafe resume".to_string()),
220 metadata: Some(serde_json::json!({
221 "unicode": "test",
222 "special": "special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?",
223 "nested": {"array": [1, 2, 3], "object": {"key": "value"}}
224 })),
225 }
226 ]
227 }
228}
229
230impl SampleGenerator for crate::codec::TransactionSignature {
231 fn generate_sample() -> Self {
232 Self {
233 signature: vec![0x42; 64], signer: "acc://signer.acme/book/0".to_string(),
235 timestamp: 1234567890000,
236 vote: Some("approve".to_string()),
237 public_key: Some(vec![0x33; 32]), key_page: Some(crate::codec::TransactionKeyPage {
239 height: 1000,
240 index: 0,
241 }),
242 }
243 }
244
245 fn generate_samples() -> Vec<Self> {
246 vec![
247 Self::generate_sample(),
248 Self {
250 signature: vec![],
251 signer: "acc://min.acme/book/0".to_string(),
252 timestamp: 0,
253 vote: None,
254 public_key: None,
255 key_page: None,
256 },
257 Self {
259 signature: vec![0xFF; 128], signer: "acc://very-long-signer-name-with-many-characters.acme/book/999".to_string(),
261 timestamp: u64::MAX,
262 vote: Some("reject".to_string()),
263 public_key: Some(vec![0x00; 64]), key_page: Some(crate::codec::TransactionKeyPage {
265 height: u64::MAX,
266 index: u32::MAX,
267 }),
268 }
269 ]
270 }
271}
272
273impl SampleGenerator for crate::codec::TransactionKeyPage {
274 fn generate_sample() -> Self {
275 Self {
276 height: 12345,
277 index: 42,
278 }
279 }
280
281 fn generate_samples() -> Vec<Self> {
282 vec![
283 Self::generate_sample(),
284 Self { height: 0, index: 0 },
285 Self { height: u64::MAX, index: u32::MAX },
286 ]
287 }
288}
289
290impl SampleGenerator for crate::codec::TokenRecipient {
291 fn generate_sample() -> Self {
292 Self {
293 url: "acc://recipient.acme/tokens".to_string(),
294 amount: "1000".to_string(),
295 }
296 }
297
298 fn generate_samples() -> Vec<Self> {
299 vec![
300 Self::generate_sample(),
301 Self {
302 url: "acc://zero.acme/tokens".to_string(),
303 amount: "0".to_string(),
304 },
305 Self {
306 url: "acc://big.acme/tokens".to_string(),
307 amount: "999999999999999999999999".to_string(),
308 },
309 Self {
310 url: "acc://üñíçødé.acme/tøkeñs".to_string(),
311 amount: "42.123456789".to_string(),
312 }
313 ]
314 }
315}
316
317impl SampleGenerator for crate::codec::transaction_codec::KeySpec {
318 fn generate_sample() -> Self {
319 Self {
320 public_key_hash: "abcdef1234567890abcdef1234567890abcdef12".to_string(),
321 delegate: None,
322 }
323 }
324
325 fn generate_samples() -> Vec<Self> {
326 vec![
327 Self::generate_sample(),
328 Self {
329 public_key_hash: "0000000000000000000000000000000000000000".to_string(),
330 delegate: None,
331 },
332 Self {
333 public_key_hash: "ffffffffffffffffffffffffffffffffffffffff".to_string(),
334 delegate: Some("acc://example.acme/delegate".to_string()),
335 }
336 ]
337 }
338}
339
340impl RoundtripTestable for crate::codec::TransactionEnvelope {}
342impl RoundtripTestable for crate::codec::TransactionHeader {}
343impl RoundtripTestable for crate::codec::TransactionSignature {}
344impl RoundtripTestable for crate::codec::TransactionKeyPage {}
345impl RoundtripTestable for crate::codec::TokenRecipient {}
346impl RoundtripTestable for crate::codec::KeySpec {}
347
348impl RoundtripTestable for crate::types::StatusResponse {}
350impl RoundtripTestable for crate::types::NodeInfo {}
351impl RoundtripTestable for crate::types::TransactionResponse {}
352impl RoundtripTestable for crate::types::TransactionResult {}
353impl RoundtripTestable for crate::types::Event {}
354impl RoundtripTestable for crate::types::Attribute {}
355impl RoundtripTestable for crate::types::SignedTransaction {}
356impl RoundtripTestable for crate::types::Signature {}
357impl RoundtripTestable for crate::types::Account {}
358impl RoundtripTestable for crate::types::FaucetResponse {}
359impl RoundtripTestable for crate::types::V3SubmitRequest {}
360impl RoundtripTestable for crate::types::V3SubmitResponse {}
361impl RoundtripTestable for crate::types::SubmitResult {}
362impl RoundtripTestable for crate::types::TransactionEnvelope {}
363impl RoundtripTestable for crate::types::V3Signature {}
364
365pub fn get_type_name<T>() -> &'static str {
367 std::any::type_name::<T>()
368}
369
370pub fn verify_type_coverage() -> Result<(), Vec<String>> {
372 let mut missing_types = Vec::new();
373
374 for type_name in TYPE_NAMES {
377 match *type_name {
378 "TransactionEnvelope" | "TransactionHeader" | "TransactionSignature"
380 | "TransactionKeyPage" | "TokenRecipient" | "KeySpec" => {
381 }
383
384 "StatusResponse" | "NodeInfo" | "TransactionResponse" | "TransactionResult"
386 | "Event" | "Attribute" | "Account" | "FaucetResponse" | "V3Signature" => {
387 }
389
390 "QueryResponse" | "SignedTransaction" | "Signature" | "V3SubmitRequest"
392 | "V3SubmitResponse" | "SubmitResult" | "ProtocolTransactionEnvelope"
393 | "ProtocolTransactionSignature" | "ProtocolTransactionHeader"
394 | "BinaryReader" | "BinaryWriter" | "EncodingError" | "DecodingError"
395 | "FieldReader" | "Ed25519Signer" | "AccOptions" => {
396 missing_types.push(type_name.to_string());
398 }
399
400 _ => {
401 missing_types.push(format!("Unknown type: {}", type_name));
403 }
404 }
405 }
406
407 if missing_types.is_empty() {
408 Ok(())
409 } else {
410 Err(missing_types)
411 }
412}
413
414pub fn generate_type_test_report() -> String {
416 let mut report = String::new();
417
418 report.push_str("# Type Matrix Test Coverage Report\n\n");
419 report.push_str(&format!("Total types in matrix: {}\n\n", TYPE_NAMES.len()));
420
421 report.push_str("## Core Transaction Types\n");
422 let core_types = [
423 "TransactionEnvelope", "TransactionHeader", "TransactionSignature",
424 "TransactionKeyPage", "TokenRecipient", "KeySpec"
425 ];
426
427 for type_name in core_types {
428 if TYPE_NAMES.contains(&type_name) {
429 report.push_str(&format!("- [OK] {}\n", type_name));
430 } else {
431 report.push_str(&format!("- [MISSING] {} (missing from TYPE_NAMES)\n", type_name));
432 }
433 }
434
435 report.push_str("\n## API Response Types\n");
436 let api_types = [
437 "StatusResponse", "NodeInfo", "QueryResponse", "TransactionResponse",
438 "TransactionResult", "Event", "Attribute", "Account", "FaucetResponse"
439 ];
440
441 for type_name in api_types {
442 if TYPE_NAMES.contains(&type_name) {
443 report.push_str(&format!("- [OK] {}\n", type_name));
444 } else {
445 report.push_str(&format!("- [MISSING] {} (missing from TYPE_NAMES)\n", type_name));
446 }
447 }
448
449 report.push_str("\n## V3 Protocol Types\n");
450 let v3_types = [
451 "V3SubmitRequest", "V3SubmitResponse", "SubmitResult", "V3Signature"
452 ];
453
454 for type_name in v3_types {
455 if TYPE_NAMES.contains(&type_name) {
456 report.push_str(&format!("- [OK] {}\n", type_name));
457 } else {
458 report.push_str(&format!("- [MISSING] {} (missing from TYPE_NAMES)\n", type_name));
459 }
460 }
461
462 report.push_str("\n## Coverage Status\n");
463 match verify_type_coverage() {
464 Ok(()) => {
465 report.push_str("[OK] All types have test coverage\n");
466 }
467 Err(missing) => {
468 report.push_str(&format!("[MISSING] {} types need test implementations:\n", missing.len()));
469 for missing_type in missing {
470 report.push_str(&format!(" - {}\n", missing_type));
471 }
472 }
473 }
474
475 report
476}
477
478pub fn count_samples<T: SampleGenerator>() -> usize {
480 T::generate_samples().len()
481}