OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-store / test / sequencer.test.js

78 lines · javascript ref:
// FirehoseSequencer: seq assignment, cursor=0 full backfill, cursor>0 partial, and
// live-tail (a frame appended AFTER subscription arrives) + clean abort.

import test from 'brittle'
import { mkdtempSync, rmSync } from 'node:fs'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { RepoStore } from '../lib/repo-store.js'
import { FirehoseSequencer } from '../lib/sequencer.js'

async function newSeq (t) {
  const dir = mkdtempSync(join(tmpdir(), 'seq-'))
  const store = await new RepoStore(dir).ready()
  const core = store.corestore.get({ name: 'firehose' })
  await core.ready()
  const seq = await new FirehoseSequencer(core).ready()
  t.teardown(async () => { await seq.close(); await store.close(); rmSync(dir, { recursive: true, force: true }) })
  return seq
}

const F = (n) => new Uint8Array([n]) // a tiny distinguishable "frame"

// 1. seq is 1-based and monotonic
test('append returns 1-based monotonic seq', async (t) => {
  const s = await newSeq(t)
  const a = await s.append(F(10))
  const b = await s.append(F(20))
  const c = await s.append(F(30))
  t.ok(a === 1 && b === 2 && c === 3 && s.length === 3, 'append returns 1-based monotonic seq')
})

// 2. cursor=0 backfills the full history, in order
test('cursor=0 replays all frames in seq order', async (t) => {
  const s = await newSeq(t)
  for (let i = 1; i <= 4; i++) await s.append(F(i))
  const got = []
  const ac = new AbortController()
  for await (const { seq, bytes } of s.subscribe(0, { signal: ac.signal })) {
    got.push([seq, bytes[0]])
    if (got.length === 4) { ac.abort(); break }
  }
  t.ok(JSON.stringify(got) === JSON.stringify([[1, 1], [2, 2], [3, 3], [4, 4]]),
    'cursor=0 replays all frames in seq order')
})

// 3. cursor>0 backfills only frames with seq > cursor
test('cursor=2 replays only seq 3,4', async (t) => {
  const s = await newSeq(t)
  for (let i = 1; i <= 4; i++) await s.append(F(i))
  const got = []
  const ac = new AbortController()
  for await (const { seq } of s.subscribe(2, { signal: ac.signal })) {
    got.push(seq)
    if (got.length === 2) { ac.abort(); break }
  }
  t.ok(JSON.stringify(got) === JSON.stringify([3, 4]), 'cursor=2 replays only seq 3,4')
})

// 4. live-tail: a frame appended AFTER subscribe() started is delivered
test('live frame appended after subscribe is delivered', async (t) => {
  const s = await newSeq(t)
  await s.append(F(1))
  const ac = new AbortController()
  const got = []
  const consumer = (async () => {
    for await (const { seq, bytes } of s.subscribe(0, { signal: ac.signal })) {
      got.push([seq, bytes[0]])
      if (got.length === 2) { ac.abort(); break }
    }
  })()
  // give the consumer a tick to drain backfill and park on 'append', then append live
  await new Promise(r => setTimeout(r, 50))
  await s.append(F(2))
  await consumer
  t.ok(JSON.stringify(got) === JSON.stringify([[1, 1], [2, 2]]),
    'live frame appended after subscribe is delivered')
})