Skip to content

Commit 117b76c

Browse files
lutterclaude
andcommitted
gnd: Add tests for tuple/struct types in ABI codegen
Add comprehensive tests to verify tuple/struct handling in ABI code generation: - test_tuple_types_in_functions: Verifies struct classes are generated for both function inputs and outputs, with proper ethereum.Tuple extension and component accessors - test_tuple_types_in_events: Verifies struct classes are generated for tuple parameters in events - test_nested_tuple_types: Verifies nested tuples (struct containing struct) generate multiple struct classes with proper hierarchy - test_tuple_array_types: Verifies tuple[] returns Array<StructType> These tests cover important edge cases identified in the TS CLI test suite and ensure tuple/struct handling matches expected behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9e41987 commit 117b76c

File tree

1 file changed

+249
-0
lines changed

1 file changed

+249
-0
lines changed

gnd/src/codegen/abi.rs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,4 +1533,253 @@ mod tests {
15331533
"Should have getSomething1 method"
15341534
);
15351535
}
1536+
1537+
/// Test that tuple/struct types in function inputs and outputs are handled correctly.
1538+
#[test]
1539+
fn test_tuple_types_in_functions() {
1540+
let abi_json = r#"[
1541+
{
1542+
"type": "function",
1543+
"name": "doSomething",
1544+
"inputs": [
1545+
{
1546+
"name": "data",
1547+
"type": "tuple",
1548+
"components": [
1549+
{"name": "owner", "type": "address"},
1550+
{"name": "value", "type": "uint256"}
1551+
]
1552+
}
1553+
],
1554+
"outputs": [
1555+
{
1556+
"name": "result",
1557+
"type": "tuple",
1558+
"components": [
1559+
{"name": "success", "type": "bool"},
1560+
{"name": "newValue", "type": "uint256"}
1561+
]
1562+
}
1563+
],
1564+
"stateMutability": "nonpayable"
1565+
}
1566+
]"#;
1567+
1568+
let contract = parse_abi(abi_json);
1569+
let gen = AbiCodeGenerator::new(contract, "TestContract");
1570+
let types = gen.generate_types();
1571+
1572+
// Get class names
1573+
let class_names: Vec<&str> = types.iter().map(|c| c.name.as_str()).collect();
1574+
1575+
// Should have main contract class
1576+
assert!(
1577+
class_names.contains(&"TestContract"),
1578+
"Should have TestContract class"
1579+
);
1580+
1581+
// Should have struct classes for input and output tuples
1582+
// Input struct: TestContract__doSomethingInputValue0Struct
1583+
// Output struct: TestContract__doSomethingResultValue0Struct
1584+
let has_input_struct = class_names
1585+
.iter()
1586+
.any(|n| n.contains("Input") && n.contains("Struct"));
1587+
let has_output_struct = class_names
1588+
.iter()
1589+
.any(|n| n.contains("Result") && n.contains("Struct"));
1590+
1591+
assert!(
1592+
has_input_struct,
1593+
"Should have input struct class, found: {:?}",
1594+
class_names
1595+
);
1596+
assert!(
1597+
has_output_struct,
1598+
"Should have output/result struct class, found: {:?}",
1599+
class_names
1600+
);
1601+
1602+
// Verify the output struct extends ethereum.Tuple
1603+
let output_struct = types
1604+
.iter()
1605+
.find(|c| c.name.contains("Result") && c.name.contains("Struct"))
1606+
.expect("Should find output struct");
1607+
assert_eq!(
1608+
output_struct.extends,
1609+
Some("ethereum.Tuple".to_string()),
1610+
"Output struct should extend ethereum.Tuple"
1611+
);
1612+
1613+
// Verify the output struct has getters for its components
1614+
// The getters use "get valueN" naming for positional access to tuple elements
1615+
let method_names: Vec<&str> = output_struct
1616+
.methods
1617+
.iter()
1618+
.map(|m| m.name.as_str())
1619+
.collect();
1620+
assert!(
1621+
method_names.iter().any(|n| n.contains("value0")),
1622+
"Should have value0 getter for first component, found methods: {:?}",
1623+
method_names
1624+
);
1625+
assert!(
1626+
method_names.iter().any(|n| n.contains("value1")),
1627+
"Should have value1 getter for second component, found methods: {:?}",
1628+
method_names
1629+
);
1630+
}
1631+
1632+
/// Test that tuple types in events are handled correctly.
1633+
#[test]
1634+
fn test_tuple_types_in_events() {
1635+
let abi_json = r#"[
1636+
{
1637+
"type": "event",
1638+
"name": "DataUpdated",
1639+
"inputs": [
1640+
{"name": "id", "type": "uint256", "indexed": true},
1641+
{
1642+
"name": "data",
1643+
"type": "tuple",
1644+
"indexed": false,
1645+
"components": [
1646+
{"name": "timestamp", "type": "uint256"},
1647+
{"name": "value", "type": "bytes32"}
1648+
]
1649+
}
1650+
],
1651+
"anonymous": false
1652+
}
1653+
]"#;
1654+
1655+
let contract = parse_abi(abi_json);
1656+
let gen = AbiCodeGenerator::new(contract, "TestContract");
1657+
let types = gen.generate_types();
1658+
1659+
// Get class names
1660+
let class_names: Vec<&str> = types.iter().map(|c| c.name.as_str()).collect();
1661+
1662+
// Should have event class
1663+
assert!(
1664+
class_names.contains(&"DataUpdated"),
1665+
"Should have DataUpdated event class"
1666+
);
1667+
1668+
// Should have params class
1669+
assert!(
1670+
class_names.contains(&"DataUpdated__Params"),
1671+
"Should have DataUpdated__Params class"
1672+
);
1673+
1674+
// Should have struct class for the tuple parameter
1675+
let has_struct = class_names
1676+
.iter()
1677+
.any(|n| n.contains("Struct") && n.contains("DataUpdated"));
1678+
assert!(
1679+
has_struct,
1680+
"Should have struct class for tuple parameter, found: {:?}",
1681+
class_names
1682+
);
1683+
}
1684+
1685+
/// Test that nested tuple types (struct with struct field) are handled.
1686+
#[test]
1687+
fn test_nested_tuple_types() {
1688+
let abi_json = r#"[
1689+
{
1690+
"type": "function",
1691+
"name": "getNestedData",
1692+
"inputs": [],
1693+
"outputs": [
1694+
{
1695+
"name": "result",
1696+
"type": "tuple",
1697+
"components": [
1698+
{"name": "id", "type": "uint256"},
1699+
{
1700+
"name": "inner",
1701+
"type": "tuple",
1702+
"components": [
1703+
{"name": "x", "type": "uint256"},
1704+
{"name": "y", "type": "uint256"}
1705+
]
1706+
}
1707+
]
1708+
}
1709+
],
1710+
"stateMutability": "view"
1711+
}
1712+
]"#;
1713+
1714+
let contract = parse_abi(abi_json);
1715+
let gen = AbiCodeGenerator::new(contract, "TestContract");
1716+
let types = gen.generate_types();
1717+
1718+
// Get class names
1719+
let class_names: Vec<&str> = types.iter().map(|c| c.name.as_str()).collect();
1720+
1721+
// Should have main contract class
1722+
assert!(
1723+
class_names.contains(&"TestContract"),
1724+
"Should have TestContract class"
1725+
);
1726+
1727+
// Should have at least two struct classes (outer and inner)
1728+
let struct_count = class_names.iter().filter(|n| n.contains("Struct")).count();
1729+
assert!(
1730+
struct_count >= 2,
1731+
"Should have at least 2 struct classes for nested tuple, found {} in {:?}",
1732+
struct_count,
1733+
class_names
1734+
);
1735+
}
1736+
1737+
/// Test that tuple arrays are handled correctly.
1738+
#[test]
1739+
fn test_tuple_array_types() {
1740+
let abi_json = r#"[
1741+
{
1742+
"type": "function",
1743+
"name": "getAllItems",
1744+
"inputs": [],
1745+
"outputs": [
1746+
{
1747+
"name": "items",
1748+
"type": "tuple[]",
1749+
"components": [
1750+
{"name": "id", "type": "uint256"},
1751+
{"name": "name", "type": "string"}
1752+
]
1753+
}
1754+
],
1755+
"stateMutability": "view"
1756+
}
1757+
]"#;
1758+
1759+
let contract = parse_abi(abi_json);
1760+
let gen = AbiCodeGenerator::new(contract, "TestContract");
1761+
let types = gen.generate_types();
1762+
1763+
// Find the contract class
1764+
let contract_class = types.iter().find(|c| c.name == "TestContract").unwrap();
1765+
1766+
// Find the getAllItems method
1767+
let method = contract_class
1768+
.methods
1769+
.iter()
1770+
.find(|m| m.name == "getAllItems")
1771+
.expect("Should have getAllItems method");
1772+
1773+
// The return type should be an Array of the struct type
1774+
let return_type = method
1775+
.return_type
1776+
.as_ref()
1777+
.expect("Should have return type");
1778+
let return_type_str = return_type.to_string();
1779+
assert!(
1780+
return_type_str.starts_with("Array<"),
1781+
"Return type should be Array<...>, got: {}",
1782+
return_type_str
1783+
);
1784+
}
15361785
}

0 commit comments

Comments
 (0)