Skip to content

Commit daeee42

Browse files
Add SDK module test matrix (#372) (#423)
Document required unit, fixture, error-path, and integration coverage for wallet, payments, transactions, vault, config, and utils, with examples and links from README and contributor docs.
1 parent efc840a commit daeee42

5 files changed

Lines changed: 281 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ Tests live in `tests/` and use [Vitest](https://vitest.dev/). We use fixtures in
119119
- **Write tests for any behaviour change or new feature.** Bug fixes should include a regression test.
120120
- Keep tests isolated — avoid relying on live network calls. Mock external requests where needed.
121121
- Use the existing fixture helpers (`tests/fixtures/accounts.ts`, `transactions.ts`, etc.) rather than hardcoding data.
122+
- Follow the [SDK Module Test Matrix](./docs/module-test-matrix.md) for required unit, fixture, error-path, and integration coverage per module (wallet, payments, transactions, vault, config, utils).
122123

123124
Run the suite before opening a PR:
124125

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ npm install @axionvera/pocketpay-sdk
5050
- [SDK Package Boundary & Dependency Direction Map](./docs/dependency_direction_map.md) - Which module may import which, security-sensitive boundaries, and correct vs. incorrect import examples
5151
- [SDK Roadmap](./docs/roadmap.md) - Directional plans and contributor opportunities across the SDK
5252
- [Testing](./docs/testing.md) - Unit vs integration test lanes and the offline guarantee
53+
- [SDK Module Test Matrix](./docs/module-test-matrix.md) - Required unit, fixture, error-path, and integration tests per major module
5354
- [Pre-PR Verification](./docs/pre-pr-verification.md) - Run `npm run verify:pr` before opening a pull request to confirm tests, docs, CI, and issue acceptance criteria
5455
- [Pre-submission Verification](./docs/pre-submission-verification.md) - Run `npm run presubmit` before submitting a PR (lint, tests, coverage, build)
5556
- [Contribution Quality Gate](./docs/contribution-quality-gate.md) - Maintainer checklist and examples of incomplete vs acceptable issue work before approval

docs/module-test-matrix.md

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
# SDK Module Test Matrix
2+
3+
This matrix tells contributors **which tests are expected** when changing a
4+
major PocketPay SDK module. Use it when writing or reviewing a PR so behaviour
5+
is not shipped without unit, fixture, integration, or error-path coverage.
6+
7+
> **Companion docs:** [testing.md](./testing.md) (unit vs integration lanes),
8+
> [Meaningful Change Review](./meaningful-change-review.md),
9+
> [Contribution Quality Gate](./contribution-quality-gate.md).
10+
11+
## How to read the matrix
12+
13+
| Column | Meaning |
14+
| :--- | :--- |
15+
| **Unit** | Required offline Vitest coverage in `tests/**/*.test.ts` |
16+
| **Fixtures** | Prefer shared data under `tests/fixtures/` over one-off hardcoding |
17+
| **Error paths** | Invalid inputs, typed `PocketPayError` codes, and failure mapping |
18+
| **Integration** | Opt-in `*.integration.test.ts` with `RUN_INTEGRATION=1` when live network behaviour is in scope |
19+
20+
Legend: **R** = required for behaviour changes · **S** = strongly recommended · **O** = optional / when touching live network paths · **** = usually N/A
21+
22+
---
23+
24+
## Test types (expectations)
25+
26+
### Unit tests (default — always offline)
27+
28+
```bash
29+
npm test
30+
```
31+
32+
- Mock Horizon / Friendbot / Soroban (`vi.mock`, `setHorizonServerFactory`, or stubbed `fetch`).
33+
- The offline guard (`tests/setup/offline-guard.ts`) fails any un-mocked network call.
34+
- Assert **return values** and **typed errors** (`PocketPayError` + `.code` / `.validation`), not message text alone.
35+
36+
### Integration tests (opt-in)
37+
38+
```bash
39+
RUN_INTEGRATION=1 npm run test:integration
40+
```
41+
42+
- Live Testnet only when proving end-to-end funding, Horizon, or Soroban behaviour.
43+
- Never put live-network assertions in the default unit suite.
44+
45+
### Fixtures
46+
47+
- Reuse `tests/fixtures/accounts.ts`, `payments.ts`, `transactions.ts`, etc.
48+
- Add new fixtures when a shape is reused across tests (Horizon 404, result codes, funded balances).
49+
50+
### Error-path testing
51+
52+
For every public helper that validates input or talks to the network, cover at least:
53+
54+
1. **Invalid input** — bad keys, amounts, memos, asset specs (sync, no network).
55+
2. **Typed failure**`expects.toMatchObject({ code: '…' })` or `error.code`.
56+
3. **Network / submission failure** — 404, timeout, Horizon result codes, generic wrap (`SEND_ERROR`, etc.) when the module submits or loads accounts.
57+
4. **No network on validation failure** — assert mocks were not called.
58+
59+
---
60+
61+
## Module matrix
62+
63+
### 1. Wallet (`src/wallet/`)
64+
65+
| Area | Unit | Fixtures | Error paths | Integration |
66+
| :--- | :---: | :---: | :---: | :---: |
67+
| `createWallet` / key shape | R | O |||
68+
| `importWallet` / `safeImportWallet` | R | O | R ||
69+
| Enhanced import wrappers | S | O | R ||
70+
| Secret validation / export boundaries | R | O | R ||
71+
| Multi-asset wallet helpers | S | S | S | O |
72+
73+
**Primary tests today:** `tests/wallet.test.ts`, `tests/multi-asset-balance.test.ts`, `tests/fund.test.ts` (funding-related), `tests/balance.test.ts`
74+
75+
**Must cover when changing wallet:**
76+
77+
- Valid keypair generation (`G…` / `S…` format).
78+
- Invalid secret import → `INVALID_SECRET_KEY` (typed).
79+
- Safe / enhanced wrappers never throw; return `ok: false` with `PocketPayError`.
80+
- No secrets logged or asserted by printing full secret keys in failures.
81+
82+
---
83+
84+
### 2. Payments (`src/payments/`)
85+
86+
| Area | Unit | Fixtures | Error paths | Integration |
87+
| :--- | :---: | :---: | :---: | :---: |
88+
| `sendXLM` / `sendAsset` happy path | R | S || O |
89+
| Preflight validation | R | O | R ||
90+
| Trustline / destination checks | R | S | R | O |
91+
| Preview / receipts | S | S | S ||
92+
| Submission / Horizon mapping | R | R | R | O |
93+
94+
**Primary tests today:** `tests/payments.test.ts`, `tests/payments-error-paths.test.ts`, `tests/payments-validation.test.ts`, `tests/payments-preview.test.ts`, `tests/trustline.test.ts`, `tests/destination-validation.test.ts`, `tests/payment-receipt.test.ts`, `tests/memo-validation.test.ts`
95+
96+
**Must cover when changing payments:**
97+
98+
- Invalid destination, amount, memo, asset, self-payment.
99+
- Trustline failures (`MISSING_TRUSTLINE`, `UNFUNDED_DESTINATION`, etc.).
100+
- Network failures (`ACCOUNT_NOT_FOUND`, `PAYMENT_FAILED`, `REQUEST_TIMEOUT`, `TX_STATUS_UNKNOWN`, `SEND_ERROR`).
101+
- Fixtures: `makeHorizon404Error`, `makeHorizonResultCodeError` in `tests/fixtures/payments.ts`.
102+
103+
---
104+
105+
### 3. Transactions (`src/transactions/`)
106+
107+
| Area | Unit | Fixtures | Error paths | Integration |
108+
| :--- | :---: | :---: | :---: | :---: |
109+
| History fetch / mapping | R | R | R | O |
110+
| Filter / sort helpers | R | S | S ||
111+
| Polling / status | R | S | R | O |
112+
| Offline prep / inspect / auth | R | S | R ||
113+
| Build validation | R | S | R ||
114+
115+
**Primary tests today:** `tests/transactions.test.ts`, `tests/filterTransactions.test.ts`, `tests/sortTransactionsByDate.test.ts`, `tests/polling.test.ts`, `tests/signed-transaction-inspection.test.ts`, `tests/auth-requirements.test.ts`, `tests/build-validation.test.ts`, `tests/transaction-authorization.test.ts`, `tests/transactionFixtures.test.ts`
116+
117+
**Must cover when changing transactions:**
118+
119+
- Invalid public key before network.
120+
- Horizon 404 / timeout mapping.
121+
- Mapper preserves SDK-owned summary fields (not raw Horizon leakage).
122+
- Fixture-backed pages in `tests/fixtures/transactions.ts`.
123+
124+
---
125+
126+
### 4. Vault / Soroban helpers (`src/vault/`, `src/soroban/`)
127+
128+
| Area | Unit | Fixtures | Error paths | Integration |
129+
| :--- | :---: | :---: | :---: | :---: |
130+
| Deposit / withdraw / balance wrappers | R | O | R | O |
131+
| Capability / feature gates | R | O | R ||
132+
| Simulation / mapper / client factory | R | O | R | O |
133+
| Contract ID / config resolution | R | O | R ||
134+
135+
**Primary tests today:** `tests/vault.test.ts`, `tests/vault-capabilities.test.ts`, `tests/sorobanMapper.test.ts`, `tests/contract-client-factory.test.ts`, `tests/unsupported-feature.test.ts`
136+
137+
**Must cover when changing vault helpers:**
138+
139+
- Missing / invalid contract ID → typed capability / config errors.
140+
- Simulation or submission failure mapping (no raw RPC dumps with secrets).
141+
- Feature-flag / unsupported paths when flags disable vault ops.
142+
- Live Soroban calls only in integration tests, not unit tests.
143+
144+
---
145+
146+
### 5. Config (`src/config/`)
147+
148+
| Area | Unit | Fixtures | Error paths | Integration |
149+
| :--- | :---: | :---: | :---: | :---: |
150+
| `resolveConfig` / defaults | R | O | R ||
151+
| Config validation | R | O | R ||
152+
| Feature flags / registry | R | O | R ||
153+
| Network URL / timeout overrides | R | O | R ||
154+
155+
**Primary tests today:** `tests/config.test.ts`, `tests/config-validation.test.ts`, `tests/feature-flags.test.ts`, `tests/feature-flag-registry.test.ts`, `tests/env.test.ts`
156+
157+
**Must cover when changing config:**
158+
159+
- Invalid timeout / network / feature-flag values throw or return structured validation errors.
160+
- Source metadata / defaults documented by assertions.
161+
- Env overrides do not leak secrets into logs or error messages.
162+
163+
---
164+
165+
### 6. Utils (`src/utils/`)
166+
167+
| Area | Unit | Fixtures | Error paths | Integration |
168+
| :--- | :---: | :---: | :---: | :---: |
169+
| Amount / stroop helpers | R | O | R ||
170+
| Memo validation / build | R | O | R ||
171+
| Key / hash validators | R | O | R ||
172+
| Explorer / env helpers | S | O | S ||
173+
| Result wrappers (`toResult`, etc.) | R | O | R ||
174+
175+
**Primary tests today:** `tests/utils.test.ts`, `tests/safe-amount.test.ts`, `tests/memo-validation.test.ts`, `tests/explorer.test.ts`, `tests/env.test.ts`, `tests/result.test.ts`, `tests/enhanced-result.test.ts`
176+
177+
**Must cover when changing utils:**
178+
179+
- Boundary amounts (zero, precision, non-decimal).
180+
- Memo too long / wrong type → `TX_INVALID_MEMO` (or documented code).
181+
- Validators throw typed errors; safe wrappers never throw.
182+
183+
---
184+
185+
## Cross-cutting modules (brief)
186+
187+
| Module | Unit focus | Error paths |
188+
| :--- | :--- | :--- |
189+
| `src/errors/` | Code registry, taxonomy, classify helpers | Unknown codes, retryability |
190+
| `src/network/` | Timeout, retry, idempotency | `REQUEST_TIMEOUT`, `TX_STATUS_UNKNOWN` |
191+
| `src/account/` | Sequence / signer safety | Stale sequence, auth mismatch |
192+
| `src/diagnostics/` | Redaction hooks | No secrets in emitted events |
193+
194+
---
195+
196+
## Examples of good tests
197+
198+
### Example 1 — Typed wallet validation error
199+
200+
```ts
201+
it('rejects a non-string secret with INVALID_SECRET_KEY', () => {
202+
expect(() => validateSecretKey(12345 as any)).toThrow(PocketPayError);
203+
try {
204+
validateSecretKey(12345 as any);
205+
} catch (error) {
206+
expect(error).toMatchObject({
207+
code: 'INVALID_SECRET_KEY',
208+
validation: { field: 'secretKey', reason: 'not_a_string' },
209+
});
210+
}
211+
});
212+
```
213+
214+
### Example 2 — Payment error path with Horizon fixture (no live network)
215+
216+
```ts
217+
it('maps Horizon result codes on submission to PAYMENT_FAILED', async () => {
218+
mockLoadAccount.mockResolvedValueOnce(await sourceAccountFor(sender.publicKey));
219+
mockSubmitTransaction.mockRejectedValue(
220+
makeHorizonResultCodeError('tx_insufficient_balance', ['op_underfunded']),
221+
);
222+
223+
await expect(
224+
sendXLM({
225+
sourceSecret: sender.secretKey,
226+
destination: receiver.publicKey,
227+
amount: '10',
228+
}),
229+
).rejects.toMatchObject({ code: 'PAYMENT_FAILED' });
230+
});
231+
```
232+
233+
See `tests/payments-error-paths.test.ts` for the full matrix of destination,
234+
amount, asset, timeout, and submission failures.
235+
236+
### Example 3 — Config validation (sync, no network)
237+
238+
```ts
239+
it('rejects a non-positive timeout', () => {
240+
expect(() => resolveConfig({ timeout: 0 })).toThrow(PocketPayError);
241+
});
242+
```
243+
244+
### Example 4 — Integration test shape (opt-in only)
245+
246+
```ts
247+
// tests/friendbot.integration.test.ts
248+
describe.runIf(process.env.RUN_INTEGRATION === '1')('Friendbot funding', () => {
249+
it('funds a new Testnet account', async () => {
250+
// live network — never import this pattern into default unit tests
251+
});
252+
});
253+
```
254+
255+
---
256+
257+
## Contributor checklist (before opening a PR)
258+
259+
- [ ] Identified the owning module(s) in the matrix above
260+
- [ ] Added or updated **unit** tests for behaviour changes
261+
- [ ] Added **error-path** coverage for validation / network failures
262+
- [ ] Used or extended **fixtures** instead of hardcoding Horizon payloads
263+
- [ ] Kept unit tests **offline**; used integration lane only if needed
264+
- [ ] Ran `npm test` (and `npm run presubmit` before push)
265+
266+
---
267+
268+
## Related links
269+
270+
- [testing.md](./testing.md) — lanes and scripts
271+
- [FIXTURES.md](./FIXTURES.md) — fixture conventions (if present)
272+
- [error-handling.md](./error-handling.md)`PocketPayError` patterns
273+
- [CONTRIBUTING.md](../CONTRIBUTING.md) — writing tests section

docs/pocketpay_sdk_unit_tests.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
Unit tests live in `tests/` and mirror the `src/` module layout.
44

5+
For the full module-level expectations (unit, fixtures, error paths, and
6+
integration), see the [SDK Module Test Matrix](./module-test-matrix.md).
7+
58
## Payment helpers
69

710
Payment error-path coverage (issue #373) lives in:

docs/testing.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ occasionally non-deterministic. Keep them out of the critical path: the default
4747
file and gate it behind `RUN_INTEGRATION` as shown in the existing
4848
integration example.
4949

50+
For module-by-module expectations (wallet, payments, transactions, vault,
51+
config, utils), see the [SDK Module Test Matrix](./module-test-matrix.md).
52+
5053
## Scripts
5154

5255
| Command | What it runs |

0 commit comments

Comments
 (0)