Barazo AppView backend barazo.forum
at main 154 lines 4.6 kB view raw
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})