Skip to content

Commit 01d5ea5

Browse files
authored
test: add unit tests for TotalVotingPowerSafe (backport #5570) (#5581)
## Summary This PR adds comprehensive unit tests for the `TotalVotingPowerSafe` method in `types/validator_set.go`, addressing the request in issue #5522. ## Changes - Added `TestValidatorSet_TotalVotingPowerSafe` to `types/validator_set_test.go`. - Implemented a table-driven test structure using `testify/require` for strict assertions. - Added 11 test cases covering: - **Happy Path:** Standard validator sets with safe sums. - **Zero State:** Handling of nil and empty validator slices (expects 0, no error). - **Boundaries:** Sums exactly at `MaxTotalVotingPower`. - **Overflow:** Scenarios exceeding `MaxTotalVotingPower` (checking that it returns the expected error). ## Related Issues Closes #5522 #### PR checklist - [x] Tests written/updated - [ ] Changelog entry added in `.changelog` (N/A: `test` only change) - [ ] Updated relevant documentation (`docs/` or `spec/`) and code comments --- > [!NOTE] > Adds comprehensive unit tests for `TotalVotingPowerSafe` in `types/validator_set_test.go` to validate summation behavior and overflow handling. > > - Introduces `TestValidatorSet_TotalVotingPowerSafe` with 11 cases: normal sums, nil/empty sets, single validator, boundaries at `MaxTotalVotingPower`, and multiple overflow scenarios (ensuring error with "exceeds maximum") > - Verifies correct totals when within limits and zero result with error on overflow, without invoking `NewValidatorSet` to avoid panic > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a1931b6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <hr>This is an automatic backport of pull request #5570 done by [Mergify](https://mergify.com). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds comprehensive coverage for `TotalVotingPowerSafe` behavior and overflow handling. > > - Introduces `TestValidatorSet_TotalVotingPowerSafe` in `types/validator_set_test.go` > - Covers normal sums, nil/empty sets, single validator, boundaries at `MaxTotalVotingPower`, and multiple overflow cases (asserting error contains "exceeds maximum") > - Bypasses `NewValidatorSet` to avoid panic during overflow scenarios; uses `require` for strict assertions > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit bd517d2. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
2 parents 1d28c19 + bd517d2 commit 01d5ea5

1 file changed

Lines changed: 138 additions & 0 deletions

File tree

types/validator_set_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,3 +1697,141 @@ func TestValidatorSet_AllKeysHaveSameType(t *testing.T) {
16971697
}
16981698
}
16991699
}
1700+
1701+
func TestValidatorSet_TotalVotingPowerSafe(t *testing.T) {
1702+
testCases := []struct {
1703+
name string
1704+
validators []*Validator
1705+
expectedPower int64
1706+
expectError bool
1707+
errorContains string
1708+
}{
1709+
{
1710+
name: "happy path - normal validators",
1711+
validators: []*Validator{
1712+
NewValidator(ed25519.GenPrivKey().PubKey(), 100),
1713+
NewValidator(ed25519.GenPrivKey().PubKey(), 200),
1714+
NewValidator(ed25519.GenPrivKey().PubKey(), 300),
1715+
},
1716+
expectedPower: 600,
1717+
expectError: false,
1718+
},
1719+
{
1720+
name: "zero state - empty validator set",
1721+
validators: []*Validator{},
1722+
expectedPower: 0,
1723+
expectError: false,
1724+
},
1725+
{
1726+
name: "zero state - nil validator set",
1727+
validators: nil,
1728+
expectedPower: 0,
1729+
expectError: false,
1730+
},
1731+
{
1732+
name: "single validator",
1733+
validators: []*Validator{
1734+
NewValidator(ed25519.GenPrivKey().PubKey(), 1000),
1735+
},
1736+
expectedPower: 1000,
1737+
expectError: false,
1738+
},
1739+
{
1740+
name: "boundary - exactly at MaxTotalVotingPower",
1741+
validators: []*Validator{
1742+
NewValidator(ed25519.GenPrivKey().PubKey(), MaxTotalVotingPower),
1743+
},
1744+
expectedPower: MaxTotalVotingPower,
1745+
expectError: false,
1746+
},
1747+
{
1748+
name: "boundary - sum equals MaxTotalVotingPower",
1749+
validators: []*Validator{
1750+
NewValidator(ed25519.GenPrivKey().PubKey(), MaxTotalVotingPower-100),
1751+
NewValidator(ed25519.GenPrivKey().PubKey(), 100),
1752+
},
1753+
expectedPower: MaxTotalVotingPower,
1754+
expectError: false,
1755+
},
1756+
{
1757+
name: "overflow - exceeds MaxTotalVotingPower",
1758+
validators: []*Validator{
1759+
NewValidator(ed25519.GenPrivKey().PubKey(), MaxTotalVotingPower/2+1),
1760+
NewValidator(ed25519.GenPrivKey().PubKey(), MaxTotalVotingPower/2+1),
1761+
},
1762+
expectedPower: 0,
1763+
expectError: true,
1764+
errorContains: "exceeds maximum",
1765+
},
1766+
{
1767+
name: "overflow - multiple validators exceeding MaxTotalVotingPower",
1768+
validators: []*Validator{
1769+
NewValidator(ed25519.GenPrivKey().PubKey(), MaxTotalVotingPower),
1770+
NewValidator(ed25519.GenPrivKey().PubKey(), 1),
1771+
},
1772+
expectedPower: 0,
1773+
expectError: true,
1774+
errorContains: "exceeds maximum",
1775+
},
1776+
{
1777+
name: "overflow - three large validators",
1778+
validators: []*Validator{
1779+
NewValidator(ed25519.GenPrivKey().PubKey(), math.MaxInt64/2),
1780+
NewValidator(ed25519.GenPrivKey().PubKey(), math.MaxInt64/2),
1781+
NewValidator(ed25519.GenPrivKey().PubKey(), 100),
1782+
},
1783+
expectedPower: 0,
1784+
expectError: true,
1785+
errorContains: "exceeds maximum",
1786+
},
1787+
{
1788+
name: "validators with zero voting power",
1789+
validators: []*Validator{
1790+
NewValidator(ed25519.GenPrivKey().PubKey(), 100),
1791+
NewValidator(ed25519.GenPrivKey().PubKey(), 0),
1792+
NewValidator(ed25519.GenPrivKey().PubKey(), 200),
1793+
},
1794+
expectedPower: 300,
1795+
expectError: false,
1796+
},
1797+
{
1798+
name: "large number of validators - within limit",
1799+
validators: func() []*Validator {
1800+
vals := make([]*Validator, 100)
1801+
for i := 0; i < 100; i++ {
1802+
vals[i] = NewValidator(ed25519.GenPrivKey().PubKey(), 1000)
1803+
}
1804+
return vals
1805+
}(),
1806+
expectedPower: 100000,
1807+
expectError: false,
1808+
},
1809+
}
1810+
1811+
for _, tc := range testCases {
1812+
t.Run(tc.name, func(t *testing.T) {
1813+
// Create validator set without using NewValidatorSet to avoid panic on overflow
1814+
valSet := &ValidatorSet{
1815+
Validators: tc.validators,
1816+
}
1817+
1818+
// Call TotalVotingPowerSafe
1819+
totalPower, err := valSet.TotalVotingPowerSafe()
1820+
1821+
// Assert expectations
1822+
if tc.expectError {
1823+
require.Error(t, err, "expected error but got none")
1824+
if tc.errorContains != "" {
1825+
require.Contains(t, err.Error(), tc.errorContains,
1826+
"error message should contain expected text")
1827+
}
1828+
require.Equal(t, tc.expectedPower, totalPower,
1829+
"power should be %d when error occurs", tc.expectedPower)
1830+
} else {
1831+
require.NoError(t, err, "unexpected error: %v", err)
1832+
require.Equal(t, tc.expectedPower, totalPower,
1833+
"total voting power should be %d", tc.expectedPower)
1834+
}
1835+
})
1836+
}
1837+
}

0 commit comments

Comments
 (0)