Barazo AppView backend
barazo.forum
1import { describe, it, expect } from 'vitest'
2import * as dagCbor from '@ipld/dag-cbor'
3import { decodeEventStreamFrame } from '../../../src/lib/cbor-frames.js'
4
5// ---------------------------------------------------------------------------
6// Helper: encode a CBOR frame (header || body) as the AT Protocol does
7// ---------------------------------------------------------------------------
8
9function encodeFrame(header: Record<string, unknown>, body: Record<string, unknown>): Uint8Array {
10 const headerBytes = dagCbor.encode(header)
11 const bodyBytes = dagCbor.encode(body)
12 const frame = new Uint8Array(headerBytes.length + bodyBytes.length)
13 frame.set(headerBytes, 0)
14 frame.set(bodyBytes, headerBytes.length)
15 return frame
16}
17
18// ---------------------------------------------------------------------------
19// Tests
20// ---------------------------------------------------------------------------
21
22describe('decodeEventStreamFrame', () => {
23 it('decodes a valid #labels frame into header and body', () => {
24 const header = { op: 1, t: '#labels' }
25 const body = {
26 seq: 42,
27 labels: [
28 {
29 src: 'did:plc:labeler1',
30 uri: 'did:plc:user1',
31 val: 'spam',
32 neg: false,
33 cts: '2026-01-15T12:00:00.000Z',
34 },
35 ],
36 }
37 const frame = encodeFrame(header, body)
38
39 const result = decodeEventStreamFrame(frame)
40
41 expect(result.header).toEqual(header)
42 expect(result.body).toEqual(body)
43 })
44
45 it('decodes a frame with an error header (op: -1)', () => {
46 const header = { op: -1 }
47 const body = { error: 'ConsumerTooSlow', message: 'Consumer is too slow' }
48 const frame = encodeFrame(header, body)
49
50 const result = decodeEventStreamFrame(frame)
51
52 expect(result.header).toEqual(header)
53 expect(result.body).toEqual(body)
54 })
55
56 it('decodes a frame with a different message type', () => {
57 const header = { op: 1, t: '#info' }
58 const body = { name: 'OutdatedCursor' }
59 const frame = encodeFrame(header, body)
60
61 const result = decodeEventStreamFrame(frame)
62
63 expect(result.header).toEqual(header)
64 expect(result.body).toEqual(body)
65 })
66
67 it('preserves all label fields including optional exp', () => {
68 const header = { op: 1, t: '#labels' }
69 const body = {
70 seq: 1,
71 labels: [
72 {
73 src: 'did:plc:labeler1',
74 uri: 'did:plc:user1',
75 val: 'spam',
76 neg: false,
77 cts: '2026-01-15T12:00:00.000Z',
78 exp: '2026-02-15T12:00:00.000Z',
79 },
80 ],
81 }
82 const frame = encodeFrame(header, body)
83
84 const result = decodeEventStreamFrame(frame)
85
86 expect(result.body).toEqual(body)
87 })
88
89 it('handles body with multiple labels', () => {
90 const header = { op: 1, t: '#labels' }
91 const body = {
92 seq: 5,
93 labels: [
94 {
95 src: 'did:plc:labeler1',
96 uri: 'did:plc:user1',
97 val: 'spam',
98 neg: false,
99 cts: '2026-01-15T12:00:00.000Z',
100 },
101 {
102 src: 'did:plc:labeler1',
103 uri: 'did:plc:user2',
104 val: '!hide',
105 neg: true,
106 cts: '2026-01-15T13:00:00.000Z',
107 },
108 ],
109 }
110 const frame = encodeFrame(header, body)
111
112 const result = decodeEventStreamFrame(frame)
113
114 expect(result.body).toEqual(body)
115 const labels = result.body.labels as unknown[]
116 expect(labels).toHaveLength(2)
117 })
118
119 it('throws on empty buffer', () => {
120 expect(() => decodeEventStreamFrame(new Uint8Array(0))).toThrow()
121 })
122
123 it('throws on truncated data (header only, no body)', () => {
124 const headerBytes = dagCbor.encode({ op: 1, t: '#labels' })
125 expect(() => decodeEventStreamFrame(headerBytes)).toThrow()
126 })
127
128 it('throws on invalid CBOR data', () => {
129 const garbage = new Uint8Array([0xff, 0xfe, 0xfd, 0xfc])
130 expect(() => decodeEventStreamFrame(garbage)).toThrow()
131 })
132
133 it('accepts ArrayBuffer input', () => {
134 const header = { op: 1, t: '#labels' }
135 const body = { seq: 1, labels: [] }
136 const frame = encodeFrame(header, body)
137
138 const result = decodeEventStreamFrame(frame.buffer as ArrayBuffer)
139
140 expect(result.header).toEqual(header)
141 expect(result.body).toEqual(body)
142 })
143
144 it('accepts Buffer input', () => {
145 const header = { op: 1, t: '#labels' }
146 const body = { seq: 1, labels: [] }
147 const frame = encodeFrame(header, body)
148
149 const result = decodeEventStreamFrame(Buffer.from(frame))
150
151 expect(result.header).toEqual(header)
152 expect(result.body).toEqual(body)
153 })
154})