{"id":49682549,"url":"https://github.com/CorvidLabs/swift-algotest","last_synced_at":"2026-05-23T21:00:52.067Z","repository":{"id":327490724,"uuid":"1108144385","full_name":"CorvidLabs/swift-algotest","owner":"CorvidLabs","description":"🧪 Mock the chain, not your tests","archived":false,"fork":false,"pushed_at":"2026-01-21T00:20:05.000Z","size":59,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-02T11:43:53.058Z","etag":null,"topics":["algorand","blockchain","mocking","swift","testing"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CorvidLabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-02T04:44:04.000Z","updated_at":"2026-01-21T00:20:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/CorvidLabs/swift-algotest","commit_stats":null,"previous_names":["corvidlabs/swift-algo-test","corvidlabs/swift-algotest"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/CorvidLabs/swift-algotest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorvidLabs%2Fswift-algotest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorvidLabs%2Fswift-algotest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorvidLabs%2Fswift-algotest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorvidLabs%2Fswift-algotest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CorvidLabs","download_url":"https://codeload.github.com/CorvidLabs/swift-algotest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorvidLabs%2Fswift-algotest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33412082,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T18:09:33.147Z","status":"ssl_error","status_checked_at":"2026-05-23T18:09:31.380Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorand","blockchain","mocking","swift","testing"],"created_at":"2026-05-07T08:00:26.875Z","updated_at":"2026-05-23T21:00:52.054Z","avatar_url":"https://github.com/CorvidLabs.png","language":"Swift","funding_links":[],"categories":["Development \u0026 Tools"],"sub_categories":["Testing \u0026 Debugging"],"readme":"# AlgoTest\n\n[![macOS](https://img.shields.io/github/actions/workflow/status/CorvidLabs/swift-algotest/macOS.yml?label=macOS\u0026branch=main)](https://github.com/CorvidLabs/swift-algotest/actions/workflows/macOS.yml)\n[![Ubuntu](https://img.shields.io/github/actions/workflow/status/CorvidLabs/swift-algotest/ubuntu.yml?label=Ubuntu\u0026branch=main)](https://github.com/CorvidLabs/swift-algotest/actions/workflows/ubuntu.yml)\n[![License](https://img.shields.io/github/license/CorvidLabs/swift-algotest)](https://github.com/CorvidLabs/swift-algotest/blob/main/LICENSE)\n[![Version](https://img.shields.io/github/v/release/CorvidLabs/swift-algotest)](https://github.com/CorvidLabs/swift-algotest/releases)\n\n\u003e **Pre-1.0 Notice**: This library is under active development. The API may change between minor versions until 1.0.\n\nA comprehensive testing utilities framework for Algorand blockchain development in Swift, built with Swift 6 and strict concurrency support.\n\n## Features\n\n- **Sandbox Management**: Protocol-based sandbox control with state management\n- **Account Management**: Factory pattern for creating and funding test accounts with pooling support\n- **Transaction Building**: Fluent API for building test transactions with sensible defaults\n- **Mock Clients**: Actor-based mock implementations of Algod and Indexer clients\n- **State Snapshots**: Capture and compare blockchain state across operations\n- **Comprehensive Assertions**: XCTest extensions for Algorand-specific testing\n\n## Requirements\n\n- Swift 6.0+\n- iOS 15.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+ / visionOS 1.0+\n\n## Installation\n\nAdd AlgoTest to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/CorvidLabs/swift-algotest.git\", from: \"0.2.0\")\n]\n```\n\n## Quick Start\n\n### Sandbox Management\n\n```swift\nimport AlgoTest\n\n// Create and start a local sandbox\nlet sandbox = LocalSandbox()\ntry await sandbox.start()\n\n// Get sandbox URLs\nlet algodURL = try await sandbox.algodURL\nlet indexerURL = try await sandbox.indexerURL\n\n// Ensure sandbox is running before operations\ntry await sandbox.ensureRunning()\n\n// Stop the sandbox\ntry await sandbox.stop()\n```\n\n### Account Creation\n\n```swift\nimport AlgoTest\n\n// Create a single funded account\nlet factory = AccountFactory()\nlet account = try await factory.createAccount(\n    fundedWith: 10_000_000, // 10 ALGO\n    purpose: \"Test sender\"\n)\n\n// Create multiple accounts at once\nlet accounts = try await factory.createAccounts(\n    count: 5,\n    fundedWith: 5_000_000\n)\n\n// Use an account pool for efficient reuse\nlet pool = AccountPool()\ntry await pool.initialize()\n\ntry await pool.withAccount { account in\n    // Use account here\n    // Automatically released back to pool\n}\n```\n\n### Transaction Building\n\n```swift\nimport AlgoTest\n\nlet sender = FundedAccount.mock()\nlet receiver = FundedAccount.mock()\n\n// Build a payment transaction\nlet transaction = try TestTransactionBuilder\n    .payment(from: sender)\n    .to(receiver)\n    .amount(1_000_000)\n    .note(\"Test payment\")\n    .fee(2_000)\n    .build()\n\n// Use pre-built scenarios\nlet tx = try TransactionScenarios.simplePayment(\n    from: sender,\n    to: receiver,\n    amount: 500_000\n)\n\n// Batch payments\nlet transactions = try TransactionScenarios.batchPayments(\n    from: sender,\n    to: [receiver1, receiver2, receiver3],\n    amount: 1_000_000\n)\n```\n\n### Mock Clients\n\n```swift\nimport AlgoTest\n\n// Create mock algod client\nlet client = MockAlgodClient()\n\n// Register accounts\nawait client.register(account: MockResponses.AccountInfo(\n    address: \"ADDR...\",\n    balance: 10_000_000\n))\n\n// Submit transactions\nlet txID = try await client.submitTransaction(transaction)\n\n// Advance blockchain rounds\nawait client.advance(rounds: 5)\n\n// Query account info\nlet accountInfo = try await client.accountInfo(for: address)\n```\n\n### State Snapshots\n\n```swift\nimport AlgoTest\n\nlet client = MockAlgodClient()\nlet capture = SnapshotCapture(client: client)\n\n// Capture state before and after operations\nlet result = try await capture.captureAround(\n    accounts: [sender.address, receiver.address]\n) {\n    // Perform operations\n    return try await client.submitTransaction(transaction)\n}\n\n// Compare snapshots\nlet diff = result.after.diff(from: result.before)\nif let change = diff.balanceChanges[sender.address] {\n    print(\"Balance changed by: \\(change.delta)\")\n}\n\n// Store snapshots for later comparison\nlet store = SnapshotStore()\nawait store.store(id: \"before\", snapshot: result.before)\nawait store.store(id: \"after\", snapshot: result.after)\n\nlet comparison = try await store.compare(from: \"before\", to: \"after\")\n```\n\n### Test Assertions\n\n```swift\nimport XCTest\nimport AlgoTest\n\nclass MyAlgorandTests: XCTestCase {\n    func testPayment() async throws {\n        let sender = FundedAccount.mock(balance: 10_000_000)\n        let receiver = FundedAccount.mock()\n\n        let transaction = try TestTransactionBuilder\n            .payment(from: sender)\n            .to(receiver)\n            .amount(1_000_000)\n            .build()\n\n        // Validate transaction structure\n        assertValidTransaction(transaction)\n        assertTransactionParties(transaction, sender: sender, receiver: receiver)\n        assertTransactionAmount(transaction, equals: 1_000_000)\n\n        // Validate account can afford transaction\n        assertCanAffordTransaction(account: sender, transaction: transaction)\n\n        // Validate balances\n        assertSufficientBalance(account: sender, required: 1_001_000)\n        assertBalanceInRange(sender.initialBalance, min: 1_000_000, max: 100_000_000)\n    }\n}\n```\n\n## Architecture\n\n### Protocol-Oriented Design\n\nAlgoTest follows protocol-oriented design principles with the `Sandbox` protocol defining sandbox operations:\n\n```swift\nprotocol Sandbox: Sendable {\n    var state: SandboxState { get async }\n    var algodURL: URL { get async throws }\n    var indexerURL: URL { get async throws }\n\n    func start() async throws\n    func stop() async throws\n    func reset() async throws\n}\n```\n\n### Actor-Based Concurrency\n\nAll stateful components use Swift actors for thread-safe operations:\n\n- `LocalSandbox`: Manages sandbox lifecycle\n- `AccountFactory`: Creates and tracks accounts\n- `AccountPool`: Manages reusable account pool\n- `MockAlgodClient`: Thread-safe mock client\n- `MockIndexerClient`: Thread-safe indexer mock\n- `SnapshotCapture`: Captures state snapshots\n- `SnapshotStore`: Stores and compares snapshots\n\n### Sendable Conformance\n\nAll public types conform to `Sendable` for safe concurrent usage:\n\n- Value types: `FundedAccount`, `Transaction`, `StateSnapshot`, `MockResponses.*`\n- Enums: `SandboxState`, `AlgoTestError`, `TransactionScenarios`\n- Actors: All stateful components\n\n## Error Handling\n\nAlgoTest uses a comprehensive error type with `Sendable` conformance:\n\n```swift\nenum AlgoTestError: Error, Sendable, Equatable {\n    case sandboxNotRunning\n    case sandboxAlreadyRunning\n    case accountCreationFailed(String)\n    case fundingFailed(amount: UInt64, reason: String)\n    case insufficientBalance(required: UInt64, available: UInt64)\n    case transactionBuildFailed(String)\n    case assertionFailed(String)\n    case snapshotCaptureFailed(String)\n    case mockConfigurationError(String)\n    // ...\n}\n```\n\n## Testing\n\nThe package includes 83+ comprehensive tests covering:\n\n- Sandbox lifecycle management\n- Account creation and pooling\n- Transaction building and scenarios\n- Mock client operations\n- State snapshots and comparisons\n- All assertion helpers\n- Integration scenarios\n\nRun tests:\n\n```bash\nswift test\n```\n\n## License\n\nMIT License - Copyright (c) 2026 Leif\n\n## Contributing\n\nContributions are welcome! Please ensure:\n\n- Swift 6 compatibility\n- Strict concurrency support\n- All types are `Sendable`\n- Comprehensive test coverage\n- Clear documentation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCorvidLabs%2Fswift-algotest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCorvidLabs%2Fswift-algotest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCorvidLabs%2Fswift-algotest/lists"}