|
2 | 2 | Test NatSpec parsing, including custom fields (@custom:*) |
3 | 3 | """ |
4 | 4 |
|
5 | | -from crytic_compile.utils.natspec import DevDoc, DevMethod, Natspec, UserDoc, UserMethod |
| 5 | +from crytic_compile.utils.natspec import ( |
| 6 | + DevDoc, |
| 7 | + DevMethod, |
| 8 | + DevStateVariable, |
| 9 | + Natspec, |
| 10 | + UserDoc, |
| 11 | + UserMethod, |
| 12 | +) |
6 | 13 |
|
7 | 14 |
|
8 | 15 | class TestUserMethod: |
@@ -94,6 +101,73 @@ def test_devmethod_empty_method(self) -> None: |
94 | 101 | assert method.custom == {} |
95 | 102 |
|
96 | 103 |
|
| 104 | +class TestDevStateVariable: |
| 105 | + """Tests for DevStateVariable class""" |
| 106 | + |
| 107 | + def test_devstatevariable_basic_fields(self) -> None: |
| 108 | + """Test DevStateVariable parses basic fields""" |
| 109 | + var_data = { |
| 110 | + "details": "The total supply of tokens", |
| 111 | + "returns": {"_0": "The total token supply"}, |
| 112 | + } |
| 113 | + var = DevStateVariable(var_data) |
| 114 | + assert var.details == "The total supply of tokens" |
| 115 | + assert var.variable_return == "The total token supply" |
| 116 | + |
| 117 | + def test_devstatevariable_custom_fields(self) -> None: |
| 118 | + """Test DevStateVariable extracts custom fields""" |
| 119 | + var_data = { |
| 120 | + "details": "Owner address", |
| 121 | + "returns": {"_0": "The contract owner"}, |
| 122 | + "custom:security": "critical", |
| 123 | + "custom:access": "public", |
| 124 | + } |
| 125 | + var = DevStateVariable(var_data) |
| 126 | + assert var.custom == { |
| 127 | + "custom:security": "critical", |
| 128 | + "custom:access": "public", |
| 129 | + } |
| 130 | + |
| 131 | + def test_devstatevariable_no_custom_fields(self) -> None: |
| 132 | + """Test DevStateVariable returns empty dict when no custom fields""" |
| 133 | + var_data = { |
| 134 | + "details": "Balance mapping", |
| 135 | + "returns": {"_0": "User balance"}, |
| 136 | + } |
| 137 | + var = DevStateVariable(var_data) |
| 138 | + assert var.custom == {} |
| 139 | + |
| 140 | + def test_devstatevariable_no_returns(self) -> None: |
| 141 | + """Test DevStateVariable with no returns field""" |
| 142 | + var_data = { |
| 143 | + "details": "Some variable", |
| 144 | + } |
| 145 | + var = DevStateVariable(var_data) |
| 146 | + assert var.details == "Some variable" |
| 147 | + assert var.variable_return is None |
| 148 | + |
| 149 | + def test_devstatevariable_export(self) -> None: |
| 150 | + """Test DevStateVariable export""" |
| 151 | + var_data = { |
| 152 | + "details": "Token name", |
| 153 | + "returns": {"_0": "The token name string"}, |
| 154 | + "custom:immutable": "true", |
| 155 | + } |
| 156 | + var = DevStateVariable(var_data) |
| 157 | + exported = var.export() |
| 158 | + |
| 159 | + assert exported["details"] == "Token name" |
| 160 | + assert exported["return"] == "The token name string" |
| 161 | + assert exported["custom"]["custom:immutable"] == "true" |
| 162 | + |
| 163 | + def test_devstatevariable_empty(self) -> None: |
| 164 | + """Test DevStateVariable with empty dict""" |
| 165 | + var = DevStateVariable({}) |
| 166 | + assert var.details is None |
| 167 | + assert var.variable_return is None |
| 168 | + assert var.custom == {} |
| 169 | + |
| 170 | + |
97 | 171 | class TestUserDoc: |
98 | 172 | """Tests for UserDoc class""" |
99 | 173 |
|
@@ -230,6 +304,47 @@ def test_devdoc_export_with_methods_custom(self) -> None: |
230 | 304 | assert exported["methods"]["foo()"]["custom"]["custom:audit"] == "verified" |
231 | 305 | assert exported["methods"]["foo()"]["details"] == "foo details" |
232 | 306 |
|
| 307 | + def test_devdoc_state_variables(self) -> None: |
| 308 | + """Test DevDoc parses stateVariables""" |
| 309 | + devdoc_data = { |
| 310 | + "title": "Test Contract", |
| 311 | + "methods": {}, |
| 312 | + "stateVariables": { |
| 313 | + "totalSupply": { |
| 314 | + "details": "Total token supply", |
| 315 | + "returns": {"_0": "The total supply"}, |
| 316 | + }, |
| 317 | + "owner": { |
| 318 | + "details": "Contract owner", |
| 319 | + "returns": {"_0": "Owner address"}, |
| 320 | + "custom:security": "critical", |
| 321 | + }, |
| 322 | + }, |
| 323 | + } |
| 324 | + devdoc = DevDoc(devdoc_data) |
| 325 | + |
| 326 | + assert "totalSupply" in devdoc.state_variables |
| 327 | + assert "owner" in devdoc.state_variables |
| 328 | + |
| 329 | + total_supply = devdoc.state_variables["totalSupply"] |
| 330 | + assert total_supply.details == "Total token supply" |
| 331 | + assert total_supply.variable_return == "The total supply" |
| 332 | + assert total_supply.custom == {} |
| 333 | + |
| 334 | + owner = devdoc.state_variables["owner"] |
| 335 | + assert owner.details == "Contract owner" |
| 336 | + assert owner.variable_return == "Owner address" |
| 337 | + assert owner.custom == {"custom:security": "critical"} |
| 338 | + |
| 339 | + def test_devdoc_no_state_variables(self) -> None: |
| 340 | + """Test DevDoc with no stateVariables""" |
| 341 | + devdoc_data = { |
| 342 | + "title": "Test Contract", |
| 343 | + "methods": {}, |
| 344 | + } |
| 345 | + devdoc = DevDoc(devdoc_data) |
| 346 | + assert devdoc.state_variables == {} |
| 347 | + |
233 | 348 |
|
234 | 349 | class TestNatspec: |
235 | 350 | """Tests for Natspec class""" |
|
0 commit comments