OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-wallet / tests / manual / test-autobase-single.cjs

207 lines · javascript ref:
// test-autobase-single.cjs - Test Autobase with single device
// This verifies the apply() function works correctly before multi-device testing

require('bare-node-runtime/global')
const path = require('bare-path')
const os = require('bare-os')
const fs = require('bare-fs')
const Corestore = require('corestore')
const Hyperbee = require('hyperbee')
const Autobase = require('autobase')
const b4a = require('b4a')

// Test storage directory
const TEST_DIR = path.join(os.homedir(), '.hyper-wallet-test', 'autobase-single')

// Clean test directory
if (fs.existsSync(TEST_DIR)) {
  fs.rmSync(TEST_DIR, { recursive: true })
}
fs.mkdirSync(TEST_DIR, { recursive: true, mode: 0o700 })

/**
 * View class for Autobase
 * Pattern from autobase-example
 */
class View {
  /**
   * Open function - creates Hyperbee view
   * This is called by Autobase to create the view core
   */
  open(store) {
    const core = store.get('view')
    // Note: Don't use valueEncoding - handle JSON manually
    return new Hyperbee(core, {
      keyEncoding: 'utf-8',
      valueEncoding: 'binary'
    })
  }

  /**
   * Apply function - processes operations from input cores
   * Based on autobase examples pattern
   * Note: Parse JSON manually since we're not using valueEncoding
   */
  async apply(nodes, view, base) {
    console.log(`📝 Applying ${nodes.length} operations...`)

    const b = view.batch({ update: false })

    for (const node of nodes) {
      // Parse JSON manually
      const value = JSON.parse(node.value)

      // Handle writer management
      if (value.addWriter) {
        console.log('  ➕ Adding writer:', value.addWriter)
        await base.addWriter(Buffer.from(value.addWriter, 'hex'))
        continue
      }

      // Handle transaction operations
      if (value.type === 'put-tx') {
        console.log('  💾 Storing tx:', value.key)
        // Store value as JSON string
        await b.put(value.key, JSON.stringify(value.value))
      } else if (value.type === 'del-tx') {
        console.log('  🗑️  Deleting tx:', value.key)
        await b.del(value.key)
      }
    }

    await b.flush()
    console.log('✅ Apply complete\n')
  }

  close(view) {
    return view.close()
  }
}

async function runTest() {
  console.log('🧪 Testing Autobase Single Device\n')
  console.log('Test directory:', TEST_DIR, '\n')

  // Initialize Corestore
  const store = new Corestore(TEST_DIR)
  await store.ready()
  console.log('✅ Corestore initialized')

  // Get or create namespace for this autobase
  // Pattern from autobase-example
  const namespace = await Autobase.getLocalKey(store)
  console.log('✅ Namespace key:', b4a.toString(namespace, 'hex').slice(0, 16) + '...')

  // Initialize Autobase
  // First param: Namespaced corestore
  // Second param: bootstrap key (null for new autobase)
  // Third param: View instance with open/apply/close methods
  // Note: No valueEncoding - handle JSON manually
  const autobase = new Autobase(store.namespace(namespace), null, new View())
  await autobase.ready()
  console.log('✅ Autobase initialized')
  console.log('   Autobase key:', b4a.toString(autobase.key, 'hex').slice(0, 16) + '...')
  console.log('   Local writer key:', b4a.toString(autobase.local.key, 'hex').slice(0, 16) + '...')
  console.log('   View length:', autobase.view.length)
  console.log()

  // Test 1: Append a transaction
  console.log('Test 1: Append transaction')
  const tx1 = {
    type: 'put-tx',
    key: 'tx:1234567890:abc123',
    value: {
      hash: 'abc123',
      chain: 'bitcoin',
      to: 'tb1qtest',
      amount: '0.001',
      fee: '0.0001',
      timestamp: 1234567890,
      status: 'pending'
    }
  }

  // Append as JSON string
  await autobase.append(JSON.stringify(tx1))
  console.log('📤 Appended operation')

  // Wait for view to update
  await new Promise(resolve => setTimeout(resolve, 100))

  console.log('   View length after append:', autobase.view.length)

  // Test 2: Read from view
  console.log('\nTest 2: Read from Hyperbee view')
  const entry = await autobase.view.get('tx:1234567890:abc123')
  if (entry) {
    const tx = JSON.parse(entry.value)
    console.log('✅ Transaction found!')
    console.log('   Hash:', tx.hash)
    console.log('   Chain:', tx.chain)
    console.log('   Amount:', tx.amount)
  } else {
    console.log('❌ Transaction not found!')
  }

  // Test 3: Append multiple transactions
  console.log('\nTest 3: Append multiple transactions')
  const tx2 = {
    type: 'put-tx',
    key: 'tx:1234567891:def456',
    value: {
      hash: 'def456',
      chain: 'ethereum',
      to: '0xtest',
      amount: '0.1',
      fee: '0.001',
      timestamp: 1234567891,
      status: 'confirmed'
    }
  }

  const tx3 = {
    type: 'put-tx',
    key: 'tx:1234567892:ghi789',
    value: {
      hash: 'ghi789',
      chain: 'bitcoin',
      to: 'tb1qtest2',
      amount: '0.002',
      fee: '0.0001',
      timestamp: 1234567892,
      status: 'pending'
    }
  }

  await autobase.append(JSON.stringify(tx2))
  await autobase.append(JSON.stringify(tx3))
  console.log('📤 Appended 2 more operations')

  await new Promise(resolve => setTimeout(resolve, 100))

  // Test 4: List all transactions
  console.log('\nTest 4: List all transactions from Hyperbee')
  let count = 0
  for await (const entry of autobase.view.createReadStream()) {
    count++
    const tx = JSON.parse(entry.value)
    console.log(`  ${count}. ${entry.key}`)
    console.log(`     ${tx.chain} - ${tx.amount} - ${tx.status}`)
  }
  console.log(`✅ Found ${count} transactions`)

  // Cleanup
  await autobase.close()
  await store.close()

  console.log('\n✅ All tests passed!')
  console.log('\n💡 The apply() function is working correctly!')
  console.log('   Next: Test multi-device replication')
}

// Run the test
runTest().catch(err => {
  console.error('❌ Test failed:', err)
  process.exit(1)
})