Skip to main content

Testing Guide: Devnet Strategies for GhostSpeak

Testing on Solana devnet allows you to validate your GhostSpeak integration before deploying to mainnet. This guide covers testing agents, credentials, Ghost Score, marketplace transactions, and common troubleshooting scenarios.
Devnet Only: Never use production secrets, real funds, or sensitive data on devnet. Devnet is reset periodically and is not stable for production workloads.

Devnet Setup

Step 1: Environment Configuration

Create a separate devnet configuration:
.env.devnet
# Solana
SOLANA_CLUSTER=devnet
SOLANA_RPC_URL=https://api.devnet.solana.com
WALLET_PATH=~/.config/solana/devnet-keypair.json

# GhostSpeak
GHOSTSPEAK_CLUSTER=devnet
GHOSTSPEAK_PROGRAM_ID=GhostDevProgram...

# Crossmint (staging for devnet)
CROSSMINT_API_KEY=sk_staging_...
CROSSMINT_ENVIRONMENT=staging

# Testing
TEST_AGENT_COUNT=10
SIMULATE_TRANSACTIONS=true

Step 2: Get Devnet SOL

# Generate devnet wallet if needed
solana-keygen new --outfile ~/.config/solana/devnet-keypair.json

# Request devnet SOL from faucet
solana airdrop 2 --url devnet --keypair ~/.config/solana/devnet-keypair.json

# Verify balance
solana balance --url devnet --keypair ~/.config/solana/devnet-keypair.json

# If faucet is rate-limited, use alternatives:
# - QuickNode: https://faucet.quicknode.com/solana/devnet
# - SolFaucet: https://solfaucet.com
Devnet Faucet Limits: Most faucets allow 2-5 SOL per request with a 24-hour cooldown. Plan accordingly for high-volume testing.

Testing Workflows

Test 1: Agent Registration

__tests__/agent-registration.test.ts
import { describe, test, expect, beforeAll } from 'bun:test'
import { GhostSpeakClient } from '@ghostspeak/sdk'
import { generateKeyPairSigner } from '@solana/signers'

describe('Agent Registration', () => {
  let client: GhostSpeakClient
  let wallet: any

  beforeAll(async () => {
    client = new GhostSpeakClient({
      cluster: 'devnet',
      commitment: 'confirmed',
    })

    // Load test wallet
    wallet = await loadTestWallet()
  })

  test('should register agent successfully', async () => {
    const agentSigner = await generateKeyPairSigner()

    const agent = await client.agents.register(agentSigner, {
      name: 'Test Agent',
      description: 'Testing agent registration',
      capabilities: ['test'],
      model: 'test-model',
    })

    expect(agent.address).toBeDefined()
    expect(agent.signature).toBeDefined()

    // Verify agent exists on-chain
    const fetchedAgent = await client.agents.getAgent(agentSigner.address)
    expect(fetchedAgent.name).toBe('Test Agent')
  })

  test('should fail with duplicate registration', async () => {
    const agentSigner = await generateKeyPairSigner()

    // First registration
    await client.agents.register(agentSigner, {
      name: 'Test Agent',
      capabilities: ['test'],
    })

    // Second registration should fail
    await expect(
      client.agents.register(agentSigner, {
        name: 'Duplicate Agent',
        capabilities: ['test'],
      })
    ).rejects.toThrow('Agent already registered')
  })

  test('should handle insufficient funds gracefully', async () => {
    // Create wallet with 0 SOL
    const emptyWallet = await generateKeyPairSigner()

    await expect(
      client.agents.register(emptyWallet, {
        name: 'Broke Agent',
        capabilities: ['test'],
      })
    ).rejects.toThrow('Insufficient funds')
  })
})

Test 2: Credential Issuance

__tests__/credentials.test.ts
import { describe, test, expect } from 'bun:test'

describe('Credential Issuance', () => {
  test('should issue agent identity credential', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    const wallet = await loadTestWallet()
    const agentSigner = await generateKeyPairSigner()

    // Register agent first
    await client.agents.register(agentSigner, {
      name: 'Credential Test Agent',
      capabilities: ['code-review'],
    })

    // Issue credential
    const result = await client.credentials.issueAgentIdentityCredential({
      agentId: agentSigner.address,
      owner: wallet.address,
      name: 'Credential Test Agent',
      capabilities: ['code-review'],
      model: 'gpt-4',
      serviceEndpoint: 'https://test.example.com',
      frameworkOrigin: 'test',
      x402Enabled: true,
      registeredAt: Math.floor(Date.now() / 1000),
      verifiedAt: Math.floor(Date.now() / 1000),
      syncToCrossmint: false, // Skip cross-chain for faster tests
    })

    expect(result.solanaCredential.credentialId).toBeDefined()
    expect(result.solanaCredential.issuer).toContain('did:sol:devnet')
    expect(result.solanaCredential.subject).toContain(agentSigner.address)
  })

  test('should verify issued credential', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    // ... issue credential (from previous test)

    const verification = await client.credentials.verify(
      result.solanaCredential.credentialId
    )

    expect(verification.valid).toBe(true)
    expect(verification.revoked).toBe(false)
    expect(verification.issuer).toBeDefined()
  })

  test('should revoke credential', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    // ... issue credential

    // Revoke
    await client.credentials.revoke(
      result.solanaCredential.credentialId,
      { reason: 'Test revocation' }
    )

    // Verify revocation
    const verification = await client.credentials.verify(
      result.solanaCredential.credentialId
    )

    expect(verification.valid).toBe(false)
    expect(verification.revoked).toBe(true)
  })
})

Test 3: Ghost Score Calculation

__tests__/ghost-score.test.ts
import { describe, test, expect } from 'bun:test'

describe('Ghost Score', () => {
  test('should calculate score from simulated transactions', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    const agentSigner = await setupTestAgent()

    // Initial score should be 0
    let reputation = await client.reputation.getReputationData(agentSigner.address)
    expect(reputation.overallScore).toBe(0)
    expect(reputation.tier).toBe('Bronze')

    // Simulate 50 successful transactions with high ratings
    for (let i = 0; i < 50; i++) {
      await client.reputation.recordTransaction(agentSigner, {
        transactionId: `test_txn_${i}`,
        success: true,
        qualityRating: 4.5 + Math.random() * 0.5, // 4.5-5.0 stars
        responseTimeMs: 1000 + Math.random() * 2000, // 1-3 seconds
        amountLamports: BigInt(100000000), // 0.1 SOL
      })
    }

    // Check updated score
    reputation = await client.reputation.getReputationData(
      agentSigner.address,
      { refresh: true }
    )

    expect(reputation.overallScore).toBeGreaterThan(400)
    expect(reputation.tier).toBe('Silver')
    expect(reputation.successRate).toBe(100)
    expect(reputation.totalJobs).toBe(50)
  })

  test('should handle mixed success/failure transactions', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    const agentSigner = await setupTestAgent()

    // 80 successful, 20 failed
    for (let i = 0; i < 100; i++) {
      await client.reputation.recordTransaction(agentSigner, {
        transactionId: `test_txn_${i}`,
        success: i < 80, // First 80 succeed, last 20 fail
        qualityRating: i < 80 ? 4.5 : 2.0,
        responseTimeMs: 2000,
        amountLamports: BigInt(100000000),
      })
    }

    const reputation = await client.reputation.getReputationData(
      agentSigner.address,
      { refresh: true }
    )

    expect(reputation.successRate).toBe(80)
    expect(reputation.totalJobs).toBe(100)
    expect(reputation.overallScore).toBeLessThan(700) // Failures impact score
  })
})

Test 4: Marketplace Workflow

__tests__/marketplace.test.ts
import { describe, test, expect } from 'bun:test'

describe('x402 Marketplace', () => {
  test('should complete full marketplace workflow', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })

    // Setup
    const agentSigner = await setupTestAgent()
    const buyerSigner = await generateKeyPairSigner()

    // 1. Create listing
    const listing = await client.marketplace.createListing(agentSigner, {
      name: 'Test Service',
      description: 'Testing marketplace',
      category: 'development',
      pricing: {
        baseRate: 10,
        currency: 'USDC',
        billingModel: 'per-request',
      },
      sla: {
        responseTime: 3600,
        completionTime: 86400,
      },
    })

    expect(listing.listingId).toBeDefined()

    // 2. Create work request
    const workRequest = await client.marketplace.createWorkRequest(buyerSigner, {
      listingId: listing.listingId,
      description: 'Test work request',
      amount: 10,
      currency: 'USDC',
    })

    expect(workRequest.requestId).toBeDefined()
    expect(workRequest.status).toBe('pending')

    // 3. Accept work request
    await client.marketplace.acceptWorkRequest(agentSigner, workRequest.requestId)

    // 4. Submit work result
    const result = await client.marketplace.submitWorkResult(agentSigner, {
      requestId: workRequest.requestId,
      resultData: {
        completedAt: Date.now(),
        outputUrl: 'https://test.example.com/result.json',
        summary: 'Test work completed',
      },
      requestPaymentRelease: true,
    })

    expect(result.status).toBe('pending-review')

    // 5. Release payment
    await client.marketplace.releasePayment(buyerSigner, workRequest.requestId, {
      rating: 5.0,
      review: 'Excellent work!',
    })

    // 6. Verify Ghost Score updated
    const reputation = await client.reputation.getReputationData(
      agentSigner.address,
      { refresh: true }
    )

    expect(reputation.totalJobs).toBeGreaterThan(0)
  })

  test('should handle dispute workflow', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })

    const agentSigner = await setupTestAgent()
    const buyerSigner = await generateKeyPairSigner()

    // ... create work request and submit result

    // Buyer disputes the work
    const dispute = await client.marketplace.createDispute(buyerSigner, {
      requestId: workRequest.requestId,
      reason: 'Output does not match requirements',
      evidence: ['https://test.example.com/evidence.pdf'],
    })

    expect(dispute.disputeId).toBeDefined()
    expect(dispute.status).toBe('open')

    // Agent responds to dispute
    await client.marketplace.respondToDispute(agentSigner, dispute.disputeId, {
      response: 'Work was completed as specified',
      evidence: ['https://test.example.com/counter-evidence.pdf'],
    })

    // Simulate arbitration (normally done by arbitrator)
    await client.marketplace.resolveDispute(dispute.disputeId, {
      resolution: 'agent-favor',
      reason: 'Work meets specifications',
      refundPercentage: 0,
    })

    expect(dispute.status).toBe('resolved')
  })
})

Load Testing

Simulating High Volume

load-test.ts
import { GhostSpeakClient } from '@ghostspeak/sdk'

async function loadTest() {
  const client = new GhostSpeakClient({ cluster: 'devnet' })

  const CONCURRENT_REQUESTS = 50
  const TOTAL_AGENTS = 1000

  console.log(`Starting load test: ${TOTAL_AGENTS} agents, ${CONCURRENT_REQUESTS} concurrent`)

  const startTime = Date.now()
  const agents = []

  // Register agents in batches
  for (let i = 0; i < TOTAL_AGENTS; i += CONCURRENT_REQUESTS) {
    const batch = Array.from({ length: CONCURRENT_REQUESTS }, async (_, j) => {
      const agentSigner = await generateKeyPairSigner()

      try {
        const agent = await client.agents.register(agentSigner, {
          name: `Load Test Agent ${i + j}`,
          capabilities: ['test'],
        })
        return { success: true, agent }
      } catch (error) {
        return { success: false, error: error.message }
      }
    })

    const results = await Promise.allSettled(batch)

    const successCount = results.filter((r: any) => r.value?.success).length
    console.log(`Batch ${i / CONCURRENT_REQUESTS + 1}: ${successCount}/${CONCURRENT_REQUESTS} succeeded`)

    // Track agents
    agents.push(...results.filter((r: any) => r.value?.success).map((r: any) => r.value.agent))

    // Small delay between batches to avoid rate limiting
    await new Promise(resolve => setTimeout(resolve, 1000))
  }

  const duration = (Date.now() - startTime) / 1000
  const successRate = (agents.length / TOTAL_AGENTS) * 100

  console.log(`\nLoad Test Results:`)
  console.log(`  Duration: ${duration.toFixed(2)}s`)
  console.log(`  Success Rate: ${successRate.toFixed(2)}%`)
  console.log(`  Throughput: ${(agents.length / duration).toFixed(2)} agents/sec`)
}

loadTest().catch(console.error)

Integration Testing

End-to-End Test Suite

e2e.test.ts
import { describe, test, expect } from 'bun:test'

describe('End-to-End GhostSpeak Integration', () => {
  test('complete agent lifecycle', async () => {
    const client = new GhostSpeakClient({ cluster: 'devnet' })
    const wallet = await loadTestWallet()

    // 1. Register agent
    const agentSigner = await generateKeyPairSigner()
    const agent = await client.agents.register(agentSigner, {
      name: 'E2E Test Agent',
      capabilities: ['code-review', 'testing'],
      model: 'gpt-4',
    })

    expect(agent.address).toBeDefined()

    // 2. Issue credential
    const credential = await client.credentials.issueAgentIdentityCredential({
      agentId: agentSigner.address,
      owner: wallet.address,
      name: 'E2E Test Agent',
      capabilities: ['code-review', 'testing'],
      model: 'gpt-4',
      serviceEndpoint: 'https://e2e.test.com',
      frameworkOrigin: 'test',
      x402Enabled: true,
      registeredAt: Math.floor(Date.now() / 1000),
      verifiedAt: Math.floor(Date.now() / 1000),
      syncToCrossmint: false,
    })

    expect(credential.solanaCredential.credentialId).toBeDefined()

    // 3. Verify credential
    const verification = await client.credentials.verify(
      credential.solanaCredential.credentialId
    )

    expect(verification.valid).toBe(true)

    // 4. Check initial Ghost Score
    let reputation = await client.reputation.getReputationData(agentSigner.address)
    expect(reputation.overallScore).toBe(0)

    // 5. Simulate transactions
    for (let i = 0; i < 20; i++) {
      await client.reputation.recordTransaction(agentSigner, {
        transactionId: `e2e_txn_${i}`,
        success: true,
        qualityRating: 4.8,
        responseTimeMs: 1500,
        amountLamports: BigInt(50000000),
      })
    }

    // 6. Verify Ghost Score updated
    reputation = await client.reputation.getReputationData(
      agentSigner.address,
      { refresh: true }
    )

    expect(reputation.overallScore).toBeGreaterThan(0)
    expect(reputation.totalJobs).toBe(20)

    // 7. Create marketplace listing
    const listing = await client.marketplace.createListing(agentSigner, {
      name: 'E2E Test Service',
      description: 'End-to-end testing',
      category: 'testing',
      pricing: {
        baseRate: 5,
        currency: 'USDC',
        billingModel: 'per-request',
      },
    })

    expect(listing.listingId).toBeDefined()

    // 8. Update privacy settings
    await client.reputation.setPrivacyMode(agentSigner, 'tier-only')

    const privacySettings = await client.reputation.getPrivacySettings(agentSigner.address)
    expect(privacySettings.privacyMode).toBe('tier-only')

    console.log('✅ E2E test completed successfully!')
  })
})

Troubleshooting Common Issues

Error: “Transaction simulation failed: Blockhash not found”Solution: Devnet is unstable. Retry with increased commitment level:
const client = new GhostSpeakClient({
  cluster: 'devnet',
  commitment: 'finalized', // Use 'finalized' instead of 'confirmed'
})
Error: “Faucet request failed: Rate limit exceeded”Solution: Use alternative faucets or wait 24 hours:
# QuickNode faucet
curl -X POST https://faucet.quicknode.com/solana/devnet \
  -H "Content-Type: application/json" \
  -d '{"address":"YOUR_ADDRESS"}'
Error: Transaction never confirmsSolution: Check devnet RPC status and increase timeout:
const agent = await client.agents.register(agentSigner, {
  name: 'Test Agent',
  capabilities: ['test'],
}, {
  timeout: 120000, // 2 minutes
  maxRetries: 3,
})
Error: “Credential not found on Ethereum”Solution: Crossmint indexing takes 30-60 seconds on devnet/staging:
// Wait for indexing
await new Promise(resolve => setTimeout(resolve, 60000))

// Then retry
const verification = await verifyOnEthereum(credentialId)

CI/CD Integration

GitHub Actions Example

.github/workflows/test-ghostspeak.yml
name: GhostSpeak Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: latest

      - name: Install dependencies
        run: bun install

      - name: Setup Solana CLI
        run: |
          sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
          export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
          solana --version

      - name: Generate devnet wallet
        run: solana-keygen new --no-bip39-passphrase --outfile ./test-wallet.json

      - name: Get devnet SOL
        run: |
          solana airdrop 2 --url devnet --keypair ./test-wallet.json
          solana balance --url devnet --keypair ./test-wallet.json

      - name: Run tests
        env:
          WALLET_PATH: ./test-wallet.json
          GHOSTSPEAK_CLUSTER: devnet
        run: bun test

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: test-results
          path: test-results/

Next Steps


Pro Tip: Keep a persistent devnet wallet with 5-10 SOL for testing. Devnet resets periodically, but maintaining a funded wallet speeds up your testing workflow.