@claritydao/midnight-agora-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +1 -0
- package/LICENSE +0 -0
- package/README.md +1134 -0
- package/dist/browser-providers.d.ts +21 -0
- package/dist/browser-providers.d.ts.map +1 -0
- package/dist/browser-providers.js +147 -0
- package/dist/browser-providers.js.map +1 -0
- package/dist/common-types.d.ts +29 -0
- package/dist/common-types.d.ts.map +1 -0
- package/dist/common-types.js +2 -0
- package/dist/common-types.js.map +1 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +108 -0
- package/dist/errors.js.map +1 -0
- package/dist/governor-api.d.ts +348 -0
- package/dist/governor-api.d.ts.map +1 -0
- package/dist/governor-api.js +716 -0
- package/dist/governor-api.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +87 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +93 -0
- package/dist/manager.js.map +1 -0
- package/dist/types.d.ts +307 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +77 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +34 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +57 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/validators.d.ts +52 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +160 -0
- package/dist/validators.js.map +1 -0
- package/eslint.config.mjs +48 -0
- package/package.json +39 -0
- package/src/browser-providers.ts +274 -0
- package/src/common-types.ts +51 -0
- package/src/errors.ts +124 -0
- package/src/governor-api.ts +948 -0
- package/src/index.ts +51 -0
- package/src/manager.ts +203 -0
- package/src/types.ts +403 -0
- package/src/utils/index.ts +60 -0
- package/src/validators.ts +245 -0
- package/test/README.md +409 -0
- package/test/errors.test.ts +179 -0
- package/test/integration/governor-api.test.ts +370 -0
- package/test/mocks/providers.ts +130 -0
- package/test/types.test.ts +112 -0
- package/test/utils.test.ts +111 -0
- package/test/validators.test.ts +368 -0
- package/test/wasm-init.test.ts +132 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation utilities for the Agora DAO Governor SDK.
|
|
3
|
+
*
|
|
4
|
+
* @module validators
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { InvalidParameterError, InvalidConfigurationError } from "./errors";
|
|
8
|
+
import type {
|
|
9
|
+
GovernanceConfig,
|
|
10
|
+
GovernorConfig,
|
|
11
|
+
DeploymentConfig,
|
|
12
|
+
TokenConfig
|
|
13
|
+
} from "./types";
|
|
14
|
+
import { VoteChoice } from "./types";
|
|
15
|
+
|
|
16
|
+
// Constants for validation limits
|
|
17
|
+
const MAX_TITLE_LENGTH = 256;
|
|
18
|
+
const MAX_DESCRIPTION_LENGTH = 10000;
|
|
19
|
+
const MAX_TOKEN_NAME_LENGTH = 64;
|
|
20
|
+
const MAX_TOKEN_SYMBOL_LENGTH = 16;
|
|
21
|
+
const MAX_TOKEN_DECIMALS = 18n;
|
|
22
|
+
const MIN_VOTING_PERIOD = 60n;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validates that a value is a non-empty string.
|
|
26
|
+
*/
|
|
27
|
+
export function validateNonEmptyString(
|
|
28
|
+
value: string,
|
|
29
|
+
parameterName: string
|
|
30
|
+
): void {
|
|
31
|
+
if (typeof value !== "string") {
|
|
32
|
+
throw new InvalidParameterError(parameterName, "must be a string");
|
|
33
|
+
}
|
|
34
|
+
if (value.trim().length === 0) {
|
|
35
|
+
throw new InvalidParameterError(parameterName, "cannot be empty");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validates that a string does not exceed a maximum length.
|
|
41
|
+
*/
|
|
42
|
+
export function validateMaxLength(
|
|
43
|
+
value: string,
|
|
44
|
+
maxLength: number,
|
|
45
|
+
parameterName: string
|
|
46
|
+
): void {
|
|
47
|
+
if (value.length > maxLength) {
|
|
48
|
+
throw new InvalidParameterError(
|
|
49
|
+
parameterName,
|
|
50
|
+
`cannot exceed ${maxLength} characters (got ${value.length})`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Validates that a value is a valid Uint8Array address (32 bytes).
|
|
57
|
+
*/
|
|
58
|
+
export function validateAddress(
|
|
59
|
+
address: Uint8Array,
|
|
60
|
+
parameterName: string
|
|
61
|
+
): void {
|
|
62
|
+
if (!(address instanceof Uint8Array)) {
|
|
63
|
+
throw new InvalidParameterError(parameterName, "must be a Uint8Array");
|
|
64
|
+
}
|
|
65
|
+
if (address.length !== 32) {
|
|
66
|
+
throw new InvalidParameterError(
|
|
67
|
+
parameterName,
|
|
68
|
+
`must be exactly 32 bytes (got ${address.length})`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Validates that a bigint is positive.
|
|
75
|
+
*/
|
|
76
|
+
export function validatePositiveBigInt(
|
|
77
|
+
value: bigint,
|
|
78
|
+
parameterName: string
|
|
79
|
+
): void {
|
|
80
|
+
if (typeof value !== "bigint") {
|
|
81
|
+
throw new InvalidParameterError(parameterName, "must be a bigint");
|
|
82
|
+
}
|
|
83
|
+
if (value <= 0n) {
|
|
84
|
+
throw new InvalidParameterError(parameterName, "must be greater than 0");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validates that a bigint is non-negative.
|
|
90
|
+
*/
|
|
91
|
+
export function validateNonNegativeBigInt(
|
|
92
|
+
value: bigint,
|
|
93
|
+
parameterName: string
|
|
94
|
+
): void {
|
|
95
|
+
if (typeof value !== "bigint") {
|
|
96
|
+
throw new InvalidParameterError(parameterName, "must be a bigint");
|
|
97
|
+
}
|
|
98
|
+
if (value < 0n) {
|
|
99
|
+
throw new InvalidParameterError(parameterName, "cannot be negative");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validates a vote choice value.
|
|
105
|
+
*/
|
|
106
|
+
export function validateVoteChoice(choice: VoteChoice): void {
|
|
107
|
+
if (
|
|
108
|
+
![VoteChoice.For, VoteChoice.Against, VoteChoice.Abstain].includes(choice)
|
|
109
|
+
) {
|
|
110
|
+
throw new InvalidParameterError(
|
|
111
|
+
"choice",
|
|
112
|
+
"must be VoteChoice.For, VoteChoice.Against, or VoteChoice.Abstain"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Validates proposal parameters.
|
|
119
|
+
*/
|
|
120
|
+
export function validateProposal(
|
|
121
|
+
title: string,
|
|
122
|
+
description: string,
|
|
123
|
+
creator: Uint8Array
|
|
124
|
+
): void {
|
|
125
|
+
validateNonEmptyString(title, "title");
|
|
126
|
+
validateMaxLength(title, MAX_TITLE_LENGTH, "title");
|
|
127
|
+
|
|
128
|
+
validateNonEmptyString(description, "description");
|
|
129
|
+
validateMaxLength(description, MAX_DESCRIPTION_LENGTH, "description");
|
|
130
|
+
|
|
131
|
+
validateAddress(creator, "creator");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Validates governance configuration.
|
|
136
|
+
*/
|
|
137
|
+
export function validateGovernanceConfig(config: GovernanceConfig): void {
|
|
138
|
+
validatePositiveBigInt(config.votingPeriod, "votingPeriod");
|
|
139
|
+
validatePositiveBigInt(config.quorumThreshold, "quorumThreshold");
|
|
140
|
+
validatePositiveBigInt(config.proposalThreshold, "proposalThreshold");
|
|
141
|
+
validatePositiveBigInt(config.proposalLifetime, "proposalLifetime");
|
|
142
|
+
validateNonNegativeBigInt(config.executionDelay, "executionDelay");
|
|
143
|
+
validateNonNegativeBigInt(config.gracePeriod, "gracePeriod");
|
|
144
|
+
validatePositiveBigInt(config.minStakeAmount, "minStakeAmount");
|
|
145
|
+
validateNonNegativeBigInt(config.stakingPeriod, "stakingPeriod");
|
|
146
|
+
|
|
147
|
+
// Logical validations
|
|
148
|
+
if (config.votingPeriod < MIN_VOTING_PERIOD) {
|
|
149
|
+
throw new InvalidConfigurationError(
|
|
150
|
+
`votingPeriod must be at least ${MIN_VOTING_PERIOD} seconds`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (config.proposalThreshold > config.quorumThreshold) {
|
|
155
|
+
throw new InvalidConfigurationError(
|
|
156
|
+
"proposalThreshold cannot exceed quorumThreshold"
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validates governor configuration.
|
|
163
|
+
*/
|
|
164
|
+
export function validateGovernorConfig(config: GovernorConfig): void {
|
|
165
|
+
validateGovernanceConfig(config.governance);
|
|
166
|
+
|
|
167
|
+
validateNonNegativeBigInt(config.executionDelay, "executionDelay");
|
|
168
|
+
validateNonNegativeBigInt(config.gracePeriod, "gracePeriod");
|
|
169
|
+
validatePositiveBigInt(config.maxActiveProposals, "maxActiveProposals");
|
|
170
|
+
validatePositiveBigInt(config.proposalLifetime, "proposalLifetime");
|
|
171
|
+
|
|
172
|
+
if (typeof config.emergencyVetoEnabled !== "boolean") {
|
|
173
|
+
throw new InvalidParameterError(
|
|
174
|
+
"emergencyVetoEnabled",
|
|
175
|
+
"must be a boolean"
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (config.emergencyVetoEnabled) {
|
|
180
|
+
validatePositiveBigInt(
|
|
181
|
+
config.emergencyVetoThreshold,
|
|
182
|
+
"emergencyVetoThreshold"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check consistency
|
|
187
|
+
if (config.executionDelay !== config.governance.executionDelay) {
|
|
188
|
+
throw new InvalidConfigurationError(
|
|
189
|
+
"executionDelay must match governance.executionDelay"
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (config.gracePeriod !== config.governance.gracePeriod) {
|
|
194
|
+
throw new InvalidConfigurationError(
|
|
195
|
+
"gracePeriod must match governance.gracePeriod"
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (config.proposalLifetime !== config.governance.proposalLifetime) {
|
|
200
|
+
throw new InvalidConfigurationError(
|
|
201
|
+
"proposalLifetime must match governance.proposalLifetime"
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Validates token configuration.
|
|
208
|
+
*/
|
|
209
|
+
export function validateTokenConfig(config: TokenConfig): void {
|
|
210
|
+
validateNonEmptyString(config.name, "token.name");
|
|
211
|
+
validateMaxLength(config.name, MAX_TOKEN_NAME_LENGTH, "token.name");
|
|
212
|
+
|
|
213
|
+
validateNonEmptyString(config.symbol, "token.symbol");
|
|
214
|
+
validateMaxLength(config.symbol, MAX_TOKEN_SYMBOL_LENGTH, "token.symbol");
|
|
215
|
+
|
|
216
|
+
validatePositiveBigInt(config.decimals, "token.decimals");
|
|
217
|
+
|
|
218
|
+
if (config.decimals > MAX_TOKEN_DECIMALS) {
|
|
219
|
+
throw new InvalidConfigurationError(
|
|
220
|
+
`token.decimals cannot exceed ${MAX_TOKEN_DECIMALS}`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Validates complete deployment configuration.
|
|
227
|
+
*/
|
|
228
|
+
export function validateDeploymentConfig(config: DeploymentConfig): void {
|
|
229
|
+
validateGovernorConfig(config.governor);
|
|
230
|
+
validateTokenConfig(config.token);
|
|
231
|
+
validateAddress(config.adminKey, "adminKey");
|
|
232
|
+
|
|
233
|
+
// Security check: warn about predictable admin keys
|
|
234
|
+
const allZeros = config.adminKey.every((byte) => byte === 0);
|
|
235
|
+
const allOnes = config.adminKey.every((byte) => byte === 1);
|
|
236
|
+
const sequential =
|
|
237
|
+
config.adminKey[0] === 1 &&
|
|
238
|
+
config.adminKey.slice(1).every((byte) => byte === 0);
|
|
239
|
+
|
|
240
|
+
if (allZeros || allOnes || sequential) {
|
|
241
|
+
console.warn(
|
|
242
|
+
"WARNING: adminKey appears to be predictable. Use cryptographically secure random bytes in production."
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
package/test/README.md
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# SDK Test Suite
|
|
2
|
+
|
|
3
|
+
Comprehensive test suite for the Agora DAO Midnight SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The test suite validates the SDK's functionality across multiple layers:
|
|
8
|
+
- Type definitions and enums
|
|
9
|
+
- Input validators
|
|
10
|
+
- Error classes
|
|
11
|
+
- Utility functions
|
|
12
|
+
- WASM initialization
|
|
13
|
+
- Integration tests with mocked providers
|
|
14
|
+
|
|
15
|
+
## Test Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
test/
|
|
19
|
+
├── types.test.ts # Type system tests (8 tests)
|
|
20
|
+
├── validators.test.ts # Input validation tests (31 tests)
|
|
21
|
+
├── errors.test.ts # Error class tests (14 tests)
|
|
22
|
+
├── utils.test.ts # Utility function tests (14 tests)
|
|
23
|
+
├── wasm-init.test.ts # WASM loading tests (5 tests)
|
|
24
|
+
├── integration/
|
|
25
|
+
│ └── governor-api.test.ts # Integration tests (31 tests)
|
|
26
|
+
└── mocks/
|
|
27
|
+
└── providers.ts # Mock provider factories
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Running Tests
|
|
31
|
+
|
|
32
|
+
### Run All Tests
|
|
33
|
+
```bash
|
|
34
|
+
pnpm test
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Watch Mode
|
|
38
|
+
```bash
|
|
39
|
+
pnpm test --watch
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Run Specific Test File
|
|
43
|
+
```bash
|
|
44
|
+
pnpm test types.test.ts
|
|
45
|
+
pnpm test integration/governor-api.test.ts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Coverage
|
|
49
|
+
```bash
|
|
50
|
+
pnpm test --coverage
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Test Categories
|
|
54
|
+
|
|
55
|
+
### 1. Type System Tests (`types.test.ts`)
|
|
56
|
+
|
|
57
|
+
Tests the core type definitions and enums:
|
|
58
|
+
|
|
59
|
+
- **VoteChoice Enum**: Validates For, Against, Abstain values
|
|
60
|
+
- **ProposalStatus Enum**: Validates all 8 proposal states
|
|
61
|
+
- **ConfigPresets**: Tests production() and testing() configurations
|
|
62
|
+
- **Type Exports**: Ensures all types are properly exported
|
|
63
|
+
|
|
64
|
+
**Key Tests:**
|
|
65
|
+
- VoteChoice enum values (0, 1, 2)
|
|
66
|
+
- ProposalStatus enum completeness
|
|
67
|
+
- ConfigPresets have correct values
|
|
68
|
+
- Production config has higher thresholds than testing
|
|
69
|
+
|
|
70
|
+
### 2. Validator Tests (`validators.test.ts`)
|
|
71
|
+
|
|
72
|
+
Tests input validation for all public SDK methods:
|
|
73
|
+
|
|
74
|
+
**Coverage:**
|
|
75
|
+
- `validateDeploymentConfig()`: Token config, governance config, admin key
|
|
76
|
+
- `validateProposal()`: Title, description, creator validation
|
|
77
|
+
- `validateVoteChoice()`: Enum validation
|
|
78
|
+
- `validateStakeAmount()`: BigInt validation
|
|
79
|
+
- `validateAddress()`: Uint8Array length validation
|
|
80
|
+
- `validateProposalId()`: BigInt validation
|
|
81
|
+
|
|
82
|
+
**Edge Cases:**
|
|
83
|
+
- Empty strings
|
|
84
|
+
- Maximum length limits
|
|
85
|
+
- Invalid enum values
|
|
86
|
+
- Incorrect array lengths
|
|
87
|
+
- Negative values
|
|
88
|
+
|
|
89
|
+
### 3. Error Class Tests (`errors.test.ts`)
|
|
90
|
+
|
|
91
|
+
Tests custom error classes:
|
|
92
|
+
|
|
93
|
+
**Error Types:**
|
|
94
|
+
- `GovernorSDKError`: Base error
|
|
95
|
+
- `WalletNotConnectedError`: Wallet state errors
|
|
96
|
+
- `InsufficientStakeError`: Stake validation errors
|
|
97
|
+
- `InvalidParameterError`: Input validation errors
|
|
98
|
+
- `ContractNotFoundError`: Contract lookup errors
|
|
99
|
+
- `ProposalNotFoundError`: Proposal lookup errors
|
|
100
|
+
- `TransactionError`: Transaction submission errors
|
|
101
|
+
- `DeploymentError`: Contract deployment errors
|
|
102
|
+
- `InvalidConfigurationError`: Configuration errors
|
|
103
|
+
- `NetworkNotConfiguredError`: Network setup errors
|
|
104
|
+
|
|
105
|
+
**Validation:**
|
|
106
|
+
- Correct error names
|
|
107
|
+
- Proper message formatting
|
|
108
|
+
- Error inheritance
|
|
109
|
+
- Parameter validation in constructors
|
|
110
|
+
|
|
111
|
+
### 4. Utility Function Tests (`utils.test.ts`)
|
|
112
|
+
|
|
113
|
+
Tests utility functions:
|
|
114
|
+
|
|
115
|
+
**Functions Tested:**
|
|
116
|
+
- `randomBytes()`: Cryptographically secure random generation
|
|
117
|
+
- `bytesToHex()`: Binary to hex string conversion
|
|
118
|
+
- `hexToBytes()`: Hex string to binary conversion
|
|
119
|
+
- `addressToBytes()`: Midnight address parsing
|
|
120
|
+
|
|
121
|
+
**Key Tests:**
|
|
122
|
+
- Random bytes are non-zero and different each call
|
|
123
|
+
- Hex conversion is reversible
|
|
124
|
+
- Address parsing handles prefixes
|
|
125
|
+
- Edge cases (empty arrays, 0x prefix)
|
|
126
|
+
|
|
127
|
+
### 5. WASM Initialization Tests (`wasm-init.test.ts`)
|
|
128
|
+
|
|
129
|
+
Tests WASM module loading behavior:
|
|
130
|
+
|
|
131
|
+
**Critical Tests:**
|
|
132
|
+
- SDK imports without triggering immediate WASM load
|
|
133
|
+
- WASM modules load only when needed
|
|
134
|
+
- No `ocrt.maxField is not a function` errors
|
|
135
|
+
- Proper lazy loading behavior
|
|
136
|
+
|
|
137
|
+
**Why This Matters:**
|
|
138
|
+
The SDK uses WASM modules from `@midnight-ntwrk/compact-runtime`. These tests ensure:
|
|
139
|
+
- No top-level WASM execution during import
|
|
140
|
+
- Vite can bundle the SDK correctly
|
|
141
|
+
- Browser compatibility is maintained
|
|
142
|
+
|
|
143
|
+
### 6. Integration Tests (`integration/governor-api.test.ts`)
|
|
144
|
+
|
|
145
|
+
Tests SDK logic with mocked providers (no real blockchain):
|
|
146
|
+
|
|
147
|
+
**Test Categories:**
|
|
148
|
+
|
|
149
|
+
#### Provider Interaction (8 tests)
|
|
150
|
+
- Public data provider queries contract state
|
|
151
|
+
- Wallet provider submits transactions
|
|
152
|
+
- Transaction failure handling
|
|
153
|
+
- Proof provider generates ZK proofs
|
|
154
|
+
- Block height queries
|
|
155
|
+
- Private state save/retrieve
|
|
156
|
+
- Network queries
|
|
157
|
+
- Transaction balancing
|
|
158
|
+
|
|
159
|
+
#### Configuration Validation
|
|
160
|
+
- Valid deployment config accepted
|
|
161
|
+
- Invalid deployment config rejected
|
|
162
|
+
- Admin key length validation
|
|
163
|
+
|
|
164
|
+
#### Vote Choice Validation
|
|
165
|
+
- Valid vote choices (For, Against, Abstain)
|
|
166
|
+
- Invalid vote choices rejected
|
|
167
|
+
|
|
168
|
+
#### Proposal Validation
|
|
169
|
+
- Valid proposals accepted
|
|
170
|
+
- Empty titles rejected
|
|
171
|
+
- Title length limits enforced
|
|
172
|
+
- Empty descriptions rejected
|
|
173
|
+
|
|
174
|
+
#### Transaction Flow
|
|
175
|
+
- Transaction balancing
|
|
176
|
+
- Transaction submission
|
|
177
|
+
- Error handling
|
|
178
|
+
|
|
179
|
+
#### Private State Management
|
|
180
|
+
- State retrieval
|
|
181
|
+
- State saving
|
|
182
|
+
- State persistence
|
|
183
|
+
|
|
184
|
+
#### Block Height Queries
|
|
185
|
+
- Current block height queries
|
|
186
|
+
|
|
187
|
+
#### Error Handling Scenarios
|
|
188
|
+
- Wallet connection errors
|
|
189
|
+
- Indexer query errors
|
|
190
|
+
- Proof generation errors
|
|
191
|
+
|
|
192
|
+
#### Wallet State
|
|
193
|
+
- Wallet state retrieval
|
|
194
|
+
- Address, coinPublicKey, encryptionPublicKey
|
|
195
|
+
|
|
196
|
+
#### Configuration Presets
|
|
197
|
+
- Testing configuration values
|
|
198
|
+
- Production configuration values
|
|
199
|
+
- Production has higher thresholds than testing
|
|
200
|
+
|
|
201
|
+
## Mock Providers
|
|
202
|
+
|
|
203
|
+
The test suite uses mock providers to simulate blockchain interactions:
|
|
204
|
+
|
|
205
|
+
### `createMockProviders()`
|
|
206
|
+
|
|
207
|
+
Creates a complete mock provider set with default successful responses:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const mockProviders = createMockProviders();
|
|
211
|
+
// All operations succeed
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `createFailingMockProviders()`
|
|
215
|
+
|
|
216
|
+
Creates providers that simulate transaction failures:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const failingProviders = createFailingMockProviders();
|
|
220
|
+
// submitTransaction() throws error
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `createInsufficientStakeMockProviders()`
|
|
224
|
+
|
|
225
|
+
Creates providers with insufficient stake scenarios:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const insufficientStakeProviders = createInsufficientStakeMockProviders();
|
|
229
|
+
// User has stake < minStakeAmount
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Custom Overrides
|
|
233
|
+
|
|
234
|
+
Override specific provider behavior:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const customProviders = createMockProviders({
|
|
238
|
+
publicDataProvider: {
|
|
239
|
+
queryContractState: vi.fn().mockResolvedValue(customState),
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Test Execution Strategy
|
|
245
|
+
|
|
246
|
+
### Unit Tests
|
|
247
|
+
Fast, isolated tests for individual functions:
|
|
248
|
+
- Type definitions
|
|
249
|
+
- Validators
|
|
250
|
+
- Error classes
|
|
251
|
+
- Utility functions
|
|
252
|
+
|
|
253
|
+
### Integration Tests
|
|
254
|
+
Tests SDK logic with mocked providers:
|
|
255
|
+
- No real blockchain connection
|
|
256
|
+
- No proof generation
|
|
257
|
+
- Fast execution (< 100ms)
|
|
258
|
+
- Suitable for CI/CD
|
|
259
|
+
|
|
260
|
+
### E2E Tests (Not Included)
|
|
261
|
+
For complete end-to-end testing:
|
|
262
|
+
- Use real testnet
|
|
263
|
+
- Real Lace wallet
|
|
264
|
+
- Real proof generation
|
|
265
|
+
- Slow execution (minutes)
|
|
266
|
+
- Manual testing recommended
|
|
267
|
+
|
|
268
|
+
## Common Issues
|
|
269
|
+
|
|
270
|
+
### Issue: "Cannot find module '@agora-dao-midnight/sdk'"
|
|
271
|
+
|
|
272
|
+
**Solution:** Build the SDK first:
|
|
273
|
+
```bash
|
|
274
|
+
cd /path/to/sdk
|
|
275
|
+
pnpm run build
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Issue: "ocrt.maxField is not a function"
|
|
279
|
+
|
|
280
|
+
**Solution:** This should not happen in tests. If it does:
|
|
281
|
+
1. Check that WASM modules are properly mocked
|
|
282
|
+
2. Verify `wasm-init.test.ts` is passing
|
|
283
|
+
3. Ensure no eager WASM loading in imports
|
|
284
|
+
|
|
285
|
+
### Issue: Tests timing out
|
|
286
|
+
|
|
287
|
+
**Solution:**
|
|
288
|
+
- Check if using real providers instead of mocks
|
|
289
|
+
- Increase Vitest timeout in test file
|
|
290
|
+
- Verify no network calls in unit tests
|
|
291
|
+
|
|
292
|
+
## Test Coverage Goals
|
|
293
|
+
|
|
294
|
+
Current coverage: **103 tests passing**
|
|
295
|
+
|
|
296
|
+
Target coverage:
|
|
297
|
+
- Types: 100%
|
|
298
|
+
- Validators: 100%
|
|
299
|
+
- Errors: 100%
|
|
300
|
+
- Utils: 100%
|
|
301
|
+
- WASM Init: 100%
|
|
302
|
+
- Integration: 80%+ (critical paths)
|
|
303
|
+
|
|
304
|
+
## Writing New Tests
|
|
305
|
+
|
|
306
|
+
### Unit Test Template
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { describe, it, expect } from 'vitest';
|
|
310
|
+
import { functionToTest } from '../src/module';
|
|
311
|
+
|
|
312
|
+
describe('Module Name', () => {
|
|
313
|
+
describe('functionToTest', () => {
|
|
314
|
+
it('should handle valid input', () => {
|
|
315
|
+
const result = functionToTest(validInput);
|
|
316
|
+
expect(result).toBe(expectedValue);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should throw on invalid input', () => {
|
|
320
|
+
expect(() => functionToTest(invalidInput)).toThrow();
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Integration Test Template
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
330
|
+
import { createMockProviders } from '../mocks/providers';
|
|
331
|
+
import { GovernorAPI } from '../../src/governor-api';
|
|
332
|
+
|
|
333
|
+
describe('GovernorAPI Feature', () => {
|
|
334
|
+
let mockProviders: GovernorProviders;
|
|
335
|
+
|
|
336
|
+
beforeEach(() => {
|
|
337
|
+
mockProviders = createMockProviders();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should perform operation', async () => {
|
|
341
|
+
const result = await governorAPI.operation(params);
|
|
342
|
+
expect(result).toBeDefined();
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Best Practices
|
|
348
|
+
|
|
349
|
+
1. **Use Mocks for Integration Tests**: Never hit real blockchain in tests
|
|
350
|
+
2. **Test Edge Cases**: Empty strings, max lengths, invalid types
|
|
351
|
+
3. **Test Error Paths**: Ensure errors are thrown correctly
|
|
352
|
+
4. **Keep Tests Fast**: Unit tests < 5ms, integration tests < 100ms
|
|
353
|
+
5. **Clear Test Names**: Describe what the test validates
|
|
354
|
+
6. **Arrange-Act-Assert**: Structure tests clearly
|
|
355
|
+
7. **Don't Test Implementation Details**: Test behavior, not internals
|
|
356
|
+
|
|
357
|
+
## CI/CD Integration
|
|
358
|
+
|
|
359
|
+
The test suite is designed to run in continuous integration:
|
|
360
|
+
|
|
361
|
+
```yaml
|
|
362
|
+
# GitHub Actions example
|
|
363
|
+
- name: Run tests
|
|
364
|
+
run: pnpm test
|
|
365
|
+
|
|
366
|
+
- name: Check coverage
|
|
367
|
+
run: pnpm test --coverage
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
All tests should pass before merging to main.
|
|
371
|
+
|
|
372
|
+
## Future Test Additions
|
|
373
|
+
|
|
374
|
+
Planned test coverage:
|
|
375
|
+
|
|
376
|
+
1. **Contract Interaction Tests**: Test actual contract method calls with mocked blockchain
|
|
377
|
+
2. **State Management Tests**: Comprehensive private state tests
|
|
378
|
+
3. **Transaction Building Tests**: Test transaction construction
|
|
379
|
+
4. **ZK Witness Tests**: Test witness preparation
|
|
380
|
+
5. **Performance Tests**: Benchmark critical operations
|
|
381
|
+
|
|
382
|
+
## Troubleshooting
|
|
383
|
+
|
|
384
|
+
### Tests Fail After SDK Changes
|
|
385
|
+
|
|
386
|
+
1. Rebuild the SDK: `pnpm run build`
|
|
387
|
+
2. Clear test cache: `rm -rf node_modules/.vite`
|
|
388
|
+
3. Reinstall: `pnpm install`
|
|
389
|
+
4. Run tests: `pnpm test`
|
|
390
|
+
|
|
391
|
+
### Mock Provider Issues
|
|
392
|
+
|
|
393
|
+
If mock providers aren't working:
|
|
394
|
+
1. Check `test/mocks/providers.ts`
|
|
395
|
+
2. Verify Vitest mock functions are used: `vi.fn()`
|
|
396
|
+
3. Ensure proper async/await in tests
|
|
397
|
+
|
|
398
|
+
### Type Errors in Tests
|
|
399
|
+
|
|
400
|
+
1. Rebuild SDK types: `pnpm run build`
|
|
401
|
+
2. Check test imports match SDK exports
|
|
402
|
+
3. Verify TypeScript version compatibility
|
|
403
|
+
|
|
404
|
+
## Contact
|
|
405
|
+
|
|
406
|
+
For test-related issues:
|
|
407
|
+
- Check test output for specific error messages
|
|
408
|
+
- Review this README for common solutions
|
|
409
|
+
- Check main SDK README for build instructions
|