x402-engineer 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.
Files changed (38) hide show
  1. package/AGENT.md +102 -0
  2. package/README.md +43 -0
  3. package/dist/cli.cjs +137 -0
  4. package/package.json +51 -0
  5. package/skills/stellar-dev/SKILL.md +146 -0
  6. package/skills/stellar-dev/advanced-patterns.md +188 -0
  7. package/skills/stellar-dev/api-rpc-horizon.md +521 -0
  8. package/skills/stellar-dev/common-pitfalls.md +510 -0
  9. package/skills/stellar-dev/contracts-soroban.md +565 -0
  10. package/skills/stellar-dev/ecosystem.md +430 -0
  11. package/skills/stellar-dev/frontend-stellar-sdk.md +651 -0
  12. package/skills/stellar-dev/resources.md +306 -0
  13. package/skills/stellar-dev/security.md +491 -0
  14. package/skills/stellar-dev/standards-reference.md +94 -0
  15. package/skills/stellar-dev/stellar-assets.md +419 -0
  16. package/skills/stellar-dev/testing.md +786 -0
  17. package/skills/stellar-dev/zk-proofs.md +136 -0
  18. package/skills/x402-add-paywall/SKILL.md +208 -0
  19. package/skills/x402-add-paywall/references/patterns.md +132 -0
  20. package/skills/x402-debug/SKILL.md +92 -0
  21. package/skills/x402-debug/references/checklist.md +146 -0
  22. package/skills/x402-explain/SKILL.md +136 -0
  23. package/skills/x402-init/SKILL.md +129 -0
  24. package/skills/x402-init/templates/env-example.md +17 -0
  25. package/skills/x402-init/templates/express/config.ts.md +29 -0
  26. package/skills/x402-init/templates/express/server.ts.md +30 -0
  27. package/skills/x402-init/templates/fastify/adapter.ts.md +66 -0
  28. package/skills/x402-init/templates/fastify/config.ts.md +29 -0
  29. package/skills/x402-init/templates/fastify/server.ts.md +90 -0
  30. package/skills/x402-init/templates/hono/config.ts.md +29 -0
  31. package/skills/x402-init/templates/hono/server.ts.md +31 -0
  32. package/skills/x402-init/templates/next-app-router/config.ts.md +29 -0
  33. package/skills/x402-init/templates/next-app-router/server.ts.md +31 -0
  34. package/skills/x402-stellar/SKILL.md +139 -0
  35. package/skills/x402-stellar/references/api.md +237 -0
  36. package/skills/x402-stellar/references/patterns.md +276 -0
  37. package/skills/x402-stellar/references/setup.md +138 -0
  38. package/skills/x402-stellar/scripts/check-deps.js +218 -0
@@ -0,0 +1,786 @@
1
+ # Testing Strategy (Local / Testnet / Unit Tests)
2
+
3
+ ## Quick Navigation
4
+ - Strategy overview: [Testing Pyramid](#testing-pyramid)
5
+ - Core test layers: [Unit Testing with Soroban SDK](#unit-testing-with-soroban-sdk), [Local Testing with Stellar Quickstart](#local-testing-with-stellar-quickstart), [Testnet Testing](#testnet-testing)
6
+ - Integration and CI: [Integration Testing Patterns](#integration-testing-patterns), [Test Configuration](#test-configuration), [CI/CD Configuration](#cicd-configuration)
7
+ - Advanced testing: [Fuzz Testing](#fuzz-testing), [Property-Based Testing](#property-based-testing), [Differential Testing with Test Snapshots](#differential-testing-with-test-snapshots), [Fork Testing](#fork-testing), [Mutation Testing](#mutation-testing)
8
+ - Performance and readiness: [Resource Profiling](#resource-profiling), [Best Practices](#best-practices)
9
+
10
+ ## Testing Pyramid
11
+
12
+ 1. **Unit tests (fast)**: Native Rust tests with `soroban-sdk` testutils
13
+ 2. **Local integration tests**: Stellar Quickstart Docker
14
+ 3. **Testnet tests**: Deploy and test on public testnet
15
+ 4. **Mainnet smoke tests**: Final validation before production
16
+
17
+ ## Unit Testing with Soroban SDK
18
+
19
+ The Soroban SDK provides comprehensive testing utilities that run natively (not in WASM), enabling fast iteration with full debugging support.
20
+
21
+ ### Basic Test Setup
22
+
23
+ ```rust
24
+ #![cfg(test)]
25
+
26
+ use soroban_sdk::{testutils::Address as _, Address, Env};
27
+
28
+ // Import your contract
29
+ use crate::{Contract, ContractClient};
30
+
31
+ #[test]
32
+ fn test_basic_functionality() {
33
+ // Create test environment
34
+ let env = Env::default();
35
+
36
+ // Register contract
37
+ let contract_id = env.register_contract(None, Contract);
38
+
39
+ // Create typed client
40
+ let client = ContractClient::new(&env, &contract_id);
41
+
42
+ // Generate test addresses
43
+ let user = Address::generate(&env);
44
+
45
+ // Call contract functions
46
+ client.initialize(&user);
47
+
48
+ // Assert results
49
+ assert_eq!(client.get_value(), 0);
50
+ }
51
+ ```
52
+
53
+ ### Testing Authorization
54
+
55
+ ```rust
56
+ #[test]
57
+ fn test_with_auth() {
58
+ let env = Env::default();
59
+
60
+ // Mock all authorizations automatically
61
+ env.mock_all_auths();
62
+
63
+ let contract_id = env.register_contract(None, TokenContract);
64
+ let client = TokenContractClient::new(&env, &contract_id);
65
+
66
+ let admin = Address::generate(&env);
67
+ let user1 = Address::generate(&env);
68
+ let user2 = Address::generate(&env);
69
+
70
+ // Initialize and mint
71
+ client.initialize(&admin);
72
+ client.mint(&user1, &1000);
73
+
74
+ // Transfer (requires auth from user1)
75
+ client.transfer(&user1, &user2, &100);
76
+
77
+ assert_eq!(client.balance(&user1), 900);
78
+ assert_eq!(client.balance(&user2), 100);
79
+
80
+ // Verify which auths were required
81
+ let auths = env.auths();
82
+ assert_eq!(auths.len(), 1);
83
+ // auths[0] contains (address, contract_id, function, args)
84
+ }
85
+ ```
86
+
87
+ ### Testing with Specific Auth Requirements
88
+
89
+ ```rust
90
+ #[test]
91
+ fn test_specific_auth() {
92
+ let env = Env::default();
93
+ let contract_id = env.register_contract(None, Contract);
94
+ let client = ContractClient::new(&env, &contract_id);
95
+
96
+ let user = Address::generate(&env);
97
+
98
+ // Mock auth only for specific address
99
+ env.mock_auths(&[MockAuth {
100
+ address: &user,
101
+ invoke: &MockAuthInvoke {
102
+ contract: &contract_id,
103
+ fn_name: "transfer",
104
+ args: (&user, &other, &100i128).into_val(&env),
105
+ sub_invokes: &[],
106
+ },
107
+ }]);
108
+
109
+ client.transfer(&user, &other, &100);
110
+ }
111
+ ```
112
+
113
+ ### Testing Time-Dependent Logic
114
+
115
+ ```rust
116
+ #[test]
117
+ fn test_time_based() {
118
+ let env = Env::default();
119
+ let contract_id = env.register_contract(None, VestingContract);
120
+ let client = VestingContractClient::new(&env, &contract_id);
121
+
122
+ let beneficiary = Address::generate(&env);
123
+
124
+ // Set initial timestamp
125
+ env.ledger().set_timestamp(1000);
126
+
127
+ client.create_vesting(&beneficiary, &1000, &2000); // unlock at t=2000
128
+
129
+ // Try to claim before unlock
130
+ assert!(client.try_claim(&beneficiary).is_err());
131
+
132
+ // Advance time past unlock
133
+ env.ledger().set_timestamp(2500);
134
+
135
+ // Now claim succeeds
136
+ client.claim(&beneficiary);
137
+ }
138
+ ```
139
+
140
+ ### Testing Ledger State
141
+
142
+ ```rust
143
+ #[test]
144
+ fn test_ledger_manipulation() {
145
+ let env = Env::default();
146
+
147
+ // Set ledger sequence
148
+ env.ledger().set_sequence_number(1000);
149
+
150
+ // Set timestamp
151
+ env.ledger().set_timestamp(1704067200); // Jan 1, 2024
152
+
153
+ // Set network passphrase
154
+ env.ledger().set_network_id([0u8; 32]); // Custom network ID
155
+
156
+ // Get current values
157
+ let seq = env.ledger().sequence();
158
+ let ts = env.ledger().timestamp();
159
+ }
160
+ ```
161
+
162
+ ### Testing Events
163
+
164
+ ```rust
165
+ #[test]
166
+ fn test_events() {
167
+ let env = Env::default();
168
+ let contract_id = env.register_contract(None, Contract);
169
+ let client = ContractClient::new(&env, &contract_id);
170
+
171
+ client.do_something();
172
+
173
+ // Get all events
174
+ let events = env.events().all();
175
+
176
+ // Check specific event
177
+ assert_eq!(events.len(), 1);
178
+
179
+ let event = &events[0];
180
+ // event.0 = contract_id
181
+ // event.1 = topics (Vec<Val>)
182
+ // event.2 = data (Val)
183
+ }
184
+ ```
185
+
186
+ ### Testing Storage
187
+
188
+ ```rust
189
+ #[test]
190
+ fn test_storage_ttl() {
191
+ let env = Env::default();
192
+ let contract_id = env.register_contract(None, Contract);
193
+ let client = ContractClient::new(&env, &contract_id);
194
+
195
+ client.store_data();
196
+
197
+ // Check TTL
198
+ let key = DataKey::MyData;
199
+ let ttl = env.as_contract(&contract_id, || {
200
+ env.storage().persistent().get_ttl(&key)
201
+ });
202
+
203
+ assert!(ttl > 0);
204
+ }
205
+ ```
206
+
207
+ ### Testing Cross-Contract Calls
208
+
209
+ ```rust
210
+ #[test]
211
+ fn test_cross_contract() {
212
+ let env = Env::default();
213
+
214
+ // Register both contracts
215
+ let token_id = env.register_contract_wasm(None, token::WASM);
216
+ let vault_id = env.register_contract(None, VaultContract);
217
+
218
+ let token_client = token::Client::new(&env, &token_id);
219
+ let vault_client = VaultContractClient::new(&env, &vault_id);
220
+
221
+ env.mock_all_auths();
222
+
223
+ let user = Address::generate(&env);
224
+
225
+ // Setup: mint tokens to user
226
+ token_client.mint(&user, &1000);
227
+
228
+ // Test: deposit tokens into vault
229
+ vault_client.deposit(&user, &token_id, &500);
230
+
231
+ assert_eq!(token_client.balance(&user), 500);
232
+ assert_eq!(vault_client.balance(&user), 500);
233
+ }
234
+ ```
235
+
236
+ ## Local Testing with Stellar Quickstart
237
+
238
+ ### Start Local Network
239
+
240
+ ```bash
241
+ # Pull and run Stellar Quickstart
242
+ docker run --rm -it -p 8000:8000 \
243
+ --name stellar \
244
+ stellar/quickstart:latest \
245
+ --local \
246
+ --enable-soroban-rpc
247
+
248
+ # Or use Stellar CLI
249
+ stellar container start local
250
+ ```
251
+
252
+ ### Configure for Local Network
253
+
254
+ ```typescript
255
+ import * as StellarSdk from "@stellar/stellar-sdk";
256
+
257
+ const LOCAL_RPC = "http://localhost:8000/soroban/rpc";
258
+ const LOCAL_HORIZON = "http://localhost:8000";
259
+ const LOCAL_PASSPHRASE = "Standalone Network ; February 2017";
260
+
261
+ const rpc = new StellarSdk.rpc.Server(LOCAL_RPC);
262
+ const horizon = new StellarSdk.Horizon.Server(LOCAL_HORIZON);
263
+ ```
264
+
265
+ ### Fund Test Accounts (Local)
266
+
267
+ ```bash
268
+ # Using Stellar CLI
269
+ stellar keys generate --global test-account --network local --fund
270
+
271
+ # Or via friendbot endpoint
272
+ curl "http://localhost:8000/friendbot?addr=G..."
273
+ ```
274
+
275
+ ### Deploy and Test Locally
276
+
277
+ ```bash
278
+ # Deploy contract to local network
279
+ stellar contract deploy \
280
+ --wasm target/wasm32-unknown-unknown/release/contract.wasm \
281
+ --source test-account \
282
+ --network local
283
+
284
+ # Invoke contract
285
+ stellar contract invoke \
286
+ --id CONTRACT_ID \
287
+ --source test-account \
288
+ --network local \
289
+ -- \
290
+ function_name \
291
+ --arg value
292
+ ```
293
+
294
+ ## Testnet Testing
295
+
296
+ ### Network Configuration
297
+
298
+ ```bash
299
+ # Testnet RPC: https://soroban-testnet.stellar.org
300
+ # Testnet Horizon: https://horizon-testnet.stellar.org
301
+ # Network Passphrase: "Test SDF Network ; September 2015"
302
+ # Friendbot: https://friendbot.stellar.org
303
+ ```
304
+
305
+ ### Create and Fund Testnet Account
306
+
307
+ ```bash
308
+ # Generate new identity
309
+ stellar keys generate --global my-testnet-key --network testnet
310
+
311
+ # Fund via Friendbot
312
+ stellar keys fund my-testnet-key --network testnet
313
+
314
+ # Or manually
315
+ curl "https://friendbot.stellar.org?addr=G..."
316
+ ```
317
+
318
+ ### Deploy to Testnet
319
+
320
+ ```bash
321
+ # Deploy contract
322
+ stellar contract deploy \
323
+ --wasm target/wasm32-unknown-unknown/release/contract.wasm \
324
+ --source my-testnet-key \
325
+ --network testnet
326
+
327
+ # Install contract code (separate from deployment)
328
+ stellar contract install \
329
+ --wasm target/wasm32-unknown-unknown/release/contract.wasm \
330
+ --source my-testnet-key \
331
+ --network testnet
332
+ ```
333
+
334
+ ### Testnet Reset Awareness
335
+
336
+ **Important**: Testnet resets approximately quarterly:
337
+ - All accounts and contracts are deleted
338
+ - Plan for re-deployment after resets
339
+ - Don't rely on persistent state for test data
340
+
341
+ Check reset schedule: https://stellar.org/developers/blog
342
+
343
+ ## Integration Testing Patterns
344
+
345
+ ### TypeScript Integration Tests
346
+
347
+ ```typescript
348
+ // tests/integration/contract.test.ts
349
+ import * as StellarSdk from "@stellar/stellar-sdk";
350
+
351
+ const RPC_URL = process.env.RPC_URL || "http://localhost:8000/soroban/rpc";
352
+ const NETWORK_PASSPHRASE = process.env.NETWORK_PASSPHRASE || "Standalone Network ; February 2017";
353
+
354
+ describe("Contract Integration Tests", () => {
355
+ let rpc: StellarSdk.rpc.Server;
356
+ let keypair: StellarSdk.Keypair;
357
+ let contractId: string;
358
+
359
+ beforeAll(async () => {
360
+ rpc = new StellarSdk.rpc.Server(RPC_URL);
361
+ keypair = StellarSdk.Keypair.random();
362
+
363
+ // Fund account
364
+ await fundAccount(keypair.publicKey());
365
+
366
+ // Deploy contract
367
+ contractId = await deployContract(keypair);
368
+ });
369
+
370
+ test("should initialize contract", async () => {
371
+ const account = await rpc.getAccount(keypair.publicKey());
372
+ const contract = new StellarSdk.Contract(contractId);
373
+
374
+ const tx = new StellarSdk.TransactionBuilder(account, {
375
+ fee: "100",
376
+ networkPassphrase: NETWORK_PASSPHRASE,
377
+ })
378
+ .addOperation(
379
+ contract.call(
380
+ "initialize",
381
+ StellarSdk.Address.fromString(keypair.publicKey()).toScVal()
382
+ )
383
+ )
384
+ .setTimeout(30)
385
+ .build();
386
+
387
+ const simResult = await rpc.simulateTransaction(tx);
388
+ const preparedTx = StellarSdk.rpc.assembleTransaction(tx, simResult);
389
+
390
+ preparedTx.sign(keypair);
391
+ const result = await rpc.sendTransaction(preparedTx.build());
392
+
393
+ expect(result.status).not.toBe("ERROR");
394
+ });
395
+ });
396
+ ```
397
+
398
+ ### Rust Integration Tests
399
+
400
+ ```rust
401
+ // tests/integration_test.rs
402
+ use soroban_sdk::{Env, Address};
403
+ use std::process::Command;
404
+
405
+ #[test]
406
+ #[ignore] // Run with: cargo test -- --ignored
407
+ fn integration_test_with_local_network() {
408
+ // Requires local network running
409
+ let output = Command::new("stellar")
410
+ .args([
411
+ "contract", "invoke",
412
+ "--id", "CONTRACT_ID",
413
+ "--source", "test-account",
414
+ "--network", "local",
415
+ "--",
416
+ "get_count"
417
+ ])
418
+ .output()
419
+ .expect("Failed to invoke contract");
420
+
421
+ assert!(output.status.success());
422
+ }
423
+ ```
424
+
425
+ ## Test Configuration
426
+
427
+ ### Cargo.toml for Tests
428
+
429
+ ```toml
430
+ [dev-dependencies]
431
+ soroban-sdk = { version = "25.0.1", features = ["testutils"] } # match [dependencies] version
432
+
433
+ [profile.test]
434
+ opt-level = 0
435
+ debug = true
436
+ ```
437
+
438
+ ### Running Tests
439
+
440
+ ```bash
441
+ # Run unit tests
442
+ cargo test
443
+
444
+ # Run with output
445
+ cargo test -- --nocapture
446
+
447
+ # Run specific test
448
+ cargo test test_transfer
449
+
450
+ # Run ignored (integration) tests
451
+ cargo test -- --ignored
452
+ ```
453
+
454
+ ## CI/CD Configuration
455
+
456
+ ### GitHub Actions Example
457
+
458
+ ```yaml
459
+ name: Test Soroban Contract
460
+
461
+ on: [push, pull_request]
462
+
463
+ jobs:
464
+ unit-tests:
465
+ runs-on: ubuntu-latest
466
+ steps:
467
+ - uses: actions/checkout@v4
468
+
469
+ - name: Install Rust
470
+ uses: dtolnay/rust-toolchain@stable
471
+
472
+ - name: Add WASM target
473
+ run: rustup target add wasm32-unknown-unknown
474
+
475
+ - name: Run unit tests
476
+ run: cargo test
477
+
478
+ - name: Build contract
479
+ run: cargo build --release --target wasm32-unknown-unknown
480
+
481
+ integration-tests:
482
+ runs-on: ubuntu-latest
483
+ needs: unit-tests
484
+ services:
485
+ stellar:
486
+ image: stellar/quickstart:latest
487
+ ports:
488
+ - 8000:8000
489
+ options: >-
490
+ --health-cmd "curl -f http://localhost:8000 || exit 1"
491
+ --health-interval 10s
492
+ --health-timeout 5s
493
+ --health-retries 10
494
+
495
+ steps:
496
+ - uses: actions/checkout@v4
497
+
498
+ - name: Install Stellar CLI
499
+ run: |
500
+ cargo install stellar-cli --locked
501
+
502
+ - name: Deploy and test
503
+ run: |
504
+ stellar keys generate --global ci-test --network local --fund
505
+ stellar contract deploy \
506
+ --wasm target/wasm32-unknown-unknown/release/contract.wasm \
507
+ --source ci-test \
508
+ --network local
509
+ ```
510
+
511
+ ## Best Practices
512
+
513
+ ### Test Organization
514
+ ```
515
+ project/
516
+ ├── src/
517
+ │ └── lib.rs
518
+ ├── tests/
519
+ │ ├── common/
520
+ │ │ └── mod.rs # Shared test utilities
521
+ │ ├── unit/
522
+ │ │ ├── mod.rs
523
+ │ │ └── transfer.rs
524
+ │ └── integration/
525
+ │ └── full_flow.rs
526
+ └── Cargo.toml
527
+ ```
528
+
529
+ ### Test Utilities Module
530
+
531
+ ```rust
532
+ // tests/common/mod.rs
533
+ use soroban_sdk::{testutils::Address as _, Address, Env};
534
+ use crate::{Contract, ContractClient};
535
+
536
+ pub fn setup_contract(env: &Env) -> (Address, ContractClient) {
537
+ let contract_id = env.register_contract(None, Contract);
538
+ let client = ContractClient::new(env, &contract_id);
539
+ let admin = Address::generate(env);
540
+
541
+ env.mock_all_auths();
542
+ client.initialize(&admin);
543
+
544
+ (contract_id, client)
545
+ }
546
+
547
+ pub fn create_funded_user(env: &Env, client: &ContractClient, amount: i128) -> Address {
548
+ let user = Address::generate(env);
549
+ client.mint(&user, &amount);
550
+ user
551
+ }
552
+ ```
553
+
554
+ ## Fuzz Testing
555
+
556
+ Soroban has first-class fuzz testing via `cargo-fuzz` and the built-in `SorobanArbitrary` trait. All `#[contracttype]` types automatically derive `SorobanArbitrary` when the `"testutils"` feature is active.
557
+
558
+ ### Setup
559
+
560
+ ```bash
561
+ # Install nightly Rust + cargo-fuzz
562
+ rustup install nightly
563
+ cargo install --locked cargo-fuzz
564
+
565
+ # Initialize fuzz targets
566
+ cargo fuzz init
567
+ ```
568
+
569
+ Update `Cargo.toml` to include both crate types:
570
+ ```toml
571
+ [lib]
572
+ crate-type = ["lib", "cdylib"]
573
+ ```
574
+
575
+ Add to `fuzz/Cargo.toml`:
576
+ ```toml
577
+ [dependencies]
578
+ soroban-sdk = { version = "25.0.1", features = ["testutils"] }
579
+ ```
580
+
581
+ ### Writing a Fuzz Target
582
+
583
+ ```rust
584
+ // fuzz/fuzz_targets/fuzz_deposit.rs
585
+ #![no_main]
586
+
587
+ use libfuzzer_sys::fuzz_target;
588
+ use soroban_sdk::{testutils::Address as _, Address, Env};
589
+ use my_contract::{Contract, ContractClient};
590
+
591
+ fuzz_target!(|input: (u64, i128)| {
592
+ let (seed, amount) = input;
593
+ let env = Env::default();
594
+ env.mock_all_auths();
595
+
596
+ let contract_id = env.register(Contract, ());
597
+ let client = ContractClient::new(&env, &contract_id);
598
+ let user = Address::generate(&env);
599
+
600
+ // Initialize
601
+ client.initialize(&user);
602
+
603
+ // Fuzz deposit — should never panic unexpectedly
604
+ let _ = client.try_deposit(&user, &amount);
605
+ });
606
+ ```
607
+
608
+ ### Running Fuzz Tests
609
+
610
+ ```bash
611
+ # Run (use --sanitizer=thread on macOS)
612
+ cargo +nightly fuzz run fuzz_deposit
613
+
614
+ # Generate code coverage
615
+ cargo +nightly fuzz coverage fuzz_deposit
616
+ ```
617
+
618
+ ### Soroban Token Fuzzer
619
+
620
+ Reusable library for fuzzing token contracts:
621
+ - **GitHub**: https://github.com/brson/soroban-token-fuzzer
622
+
623
+ ### Documentation
624
+
625
+ - [Stellar Fuzzing Guide](https://developers.stellar.org/docs/build/guides/testing/fuzzing)
626
+ - [Fuzzing Example Contract](https://developers.stellar.org/docs/build/smart-contracts/example-contracts/fuzzing)
627
+
628
+ ## Property-Based Testing
629
+
630
+ Use `proptest` with `SorobanArbitrary` for QuickCheck-style property testing that runs in standard `cargo test`.
631
+
632
+ ```rust
633
+ #[cfg(test)]
634
+ mod prop_tests {
635
+ use super::*;
636
+ use proptest::prelude::*;
637
+ use soroban_sdk::{testutils::Address as _, Env};
638
+
639
+ proptest! {
640
+ #[test]
641
+ fn deposit_then_withdraw_preserves_balance(amount in 1i128..=i128::MAX) {
642
+ let env = Env::default();
643
+ env.mock_all_auths();
644
+ let contract_id = env.register(Contract, ());
645
+ let client = ContractClient::new(&env, &contract_id);
646
+ let user = Address::generate(&env);
647
+
648
+ client.initialize(&user);
649
+ client.deposit(&user, &amount);
650
+ client.withdraw(&user, &amount);
651
+
652
+ prop_assert_eq!(client.balance(&user), 0);
653
+ }
654
+ }
655
+ }
656
+ ```
657
+
658
+ **Recommended workflow**: Use `cargo-fuzz` interactively to find deep bugs, then convert to `proptest` for regression prevention in CI.
659
+
660
+ ## Differential Testing with Test Snapshots
661
+
662
+ Soroban automatically writes JSON snapshots at the end of every test to `test_snapshots/`, capturing events and final ledger state. Commit these to source control — diffs reveal unintended behavioral changes.
663
+
664
+ ### Comparing Against Deployed Contracts
665
+
666
+ ```rust
667
+ // Fetch deployed contract for comparison
668
+ // $ stellar contract fetch --id C... --out-file deployed.wasm
669
+
670
+ mod deployed {
671
+ soroban_sdk::contractimport!(file = "deployed.wasm");
672
+ }
673
+
674
+ #[test]
675
+ fn test_upgrade_compatibility() {
676
+ let env = Env::default();
677
+ env.mock_all_auths();
678
+
679
+ // Register both versions
680
+ let old_id = env.register_contract_wasm(None, deployed::WASM);
681
+ let new_id = env.register(NewContract, ());
682
+
683
+ let old_client = deployed::Client::new(&env, &old_id);
684
+ let new_client = NewContractClient::new(&env, &new_id);
685
+
686
+ let user = Address::generate(&env);
687
+
688
+ // Run identical operations and compare
689
+ old_client.initialize(&user);
690
+ new_client.initialize(&user);
691
+
692
+ assert_eq!(old_client.get_value(), new_client.get_value());
693
+ }
694
+ ```
695
+
696
+ - **Docs**: [Differential Tests with Test Snapshots](https://developers.stellar.org/docs/build/guides/testing/differential-tests-with-test-snapshots)
697
+
698
+ ## Fork Testing
699
+
700
+ Test against real production state using ledger snapshots:
701
+
702
+ ```bash
703
+ # Create snapshot of deployed contract
704
+ stellar snapshot create --address C... --output json --out snapshot.json
705
+
706
+ # Optionally at a specific ledger
707
+ stellar snapshot create --address C... --ledger 12345678 --output json --out snapshot.json
708
+ ```
709
+
710
+ ```rust
711
+ #[test]
712
+ fn test_against_mainnet_state() {
713
+ let env = Env::from_ledger_snapshot_file("snapshot.json");
714
+ env.mock_all_auths();
715
+
716
+ let contract_id = /* contract address from snapshot */;
717
+ let client = ContractClient::new(&env, &contract_id);
718
+
719
+ // Test operations against real state
720
+ let result = client.get_value();
721
+ assert!(result > 0);
722
+ }
723
+ ```
724
+
725
+ - **Docs**: [Fork Testing](https://developers.stellar.org/docs/build/guides/testing/fork-testing)
726
+
727
+ ## Mutation Testing
728
+
729
+ Use `cargo-mutants` to verify test quality — modifies source code and checks that tests catch the changes.
730
+
731
+ ```bash
732
+ cargo install --locked cargo-mutants
733
+ cargo mutants
734
+ ```
735
+
736
+ **Output interpretation**:
737
+ - **CAUGHT**: Tests detected the mutation (good coverage)
738
+ - **MISSED**: Tests passed despite mutation (test gap — review `mutants.out/diff/`)
739
+
740
+ - **Docs**: [Mutation Testing](https://developers.stellar.org/docs/build/guides/testing/mutation-testing)
741
+
742
+ ## Resource Profiling
743
+
744
+ Soroban uses a multidimensional resource model (CPU instructions, ledger reads/writes, bytes, events, rent).
745
+
746
+ ### CLI Simulation
747
+
748
+ ```bash
749
+ # Simulate contract invocation to see resource costs
750
+ stellar contract invoke \
751
+ --id CONTRACT_ID \
752
+ --source alice \
753
+ --network testnet \
754
+ --sim-only \
755
+ -- \
756
+ function_name --arg value
757
+ ```
758
+
759
+ ### Stellar Plus Profiler (Cheesecake Labs)
760
+
761
+ ```typescript
762
+ import { StellarPlus } from 'stellar-plus';
763
+
764
+ const profilerPlugin = new StellarPlus.Utils.Plugins.sorobanTransaction.profiler();
765
+ // Collects CPU instructions, RAM, ledger reads/writes
766
+ // Aggregation: sum, average, standard deviation
767
+ // Output: CSV, formatted text tables
768
+ ```
769
+
770
+ - **Docs**: https://docs.cheesecakelabs.com/stellar-plus/reference/utils/plugins/profiler-plugin
771
+
772
+ ### Testing Checklist
773
+
774
+ - [ ] Unit tests cover all public functions
775
+ - [ ] Edge cases tested (zero amounts, max values, empty state)
776
+ - [ ] Authorization tested (correct signers required)
777
+ - [ ] Error conditions tested (invalid inputs, unauthorized)
778
+ - [ ] Events emission verified
779
+ - [ ] Storage TTL behavior validated
780
+ - [ ] Cross-contract interactions tested
781
+ - [ ] Fuzz tests for critical paths (deposits, withdrawals, swaps)
782
+ - [ ] Property-based tests for invariants
783
+ - [ ] Mutation testing confirms test quality
784
+ - [ ] Differential test snapshots committed to source control
785
+ - [ ] Integration tests against local network
786
+ - [ ] Testnet deployment verified before mainnet