OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-overlay / test / pointer.test.js

128 lines · javascript ref:
import test from 'brittle'
import { serialize, parse, isPointer, INLINE_MAX } from '../lib/pointer.js'

test('serialize + parse round-trip (small file, inline chunks)', (t) => {
  const info = {
    oid: 'a'.repeat(64),
    size: 32768,
    chunkCount: 3,
    chunks: [
      { hash: 'b'.repeat(64), size: 12288 },
      { hash: 'c'.repeat(64), size: 15360 },
      { hash: 'd'.repeat(64), size: 5120 }
    ]
  }

  const text = serialize(info)
  const parsed = parse(text)

  t.is(parsed.version, 1)
  t.is(parsed.oid, info.oid)
  t.is(parsed.size, info.size)
  t.is(parsed.chunkCount, 3)
  t.is(parsed.chunks.length, 3)
  t.is(parsed.chunks[0].hash, 'b'.repeat(64))
  t.is(parsed.chunks[0].size, 12288)
  t.is(parsed.chunks[2].hash, 'd'.repeat(64))
  t.is(parsed.manifestHash, null)
})

test('serialize + parse round-trip (large file, manifest reference)', (t) => {
  const info = {
    oid: 'e'.repeat(64),
    size: 4294967296, // 4GB
    chunkCount: 262144,
    manifestHash: 'f'.repeat(64)
  }

  const text = serialize(info)
  const parsed = parse(text)

  t.is(parsed.oid, info.oid)
  t.is(parsed.size, info.size)
  t.is(parsed.chunkCount, 262144)
  t.is(parsed.manifestHash, 'f'.repeat(64))
  t.is(parsed.chunks, null)
})

test('isPointer detects valid pointers', (t) => {
  const text = serialize({
    oid: 'a'.repeat(64),
    size: 100,
    chunkCount: 1,
    chunks: [{ hash: 'b'.repeat(64), size: 100 }]
  })

  t.is(isPointer(text), true)
  t.is(isPointer(Buffer.from(text)), true)
})

test('isPointer rejects non-pointer content', (t) => {
  t.is(isPointer('hello world'), false)
  t.is(isPointer('version https://git-lfs.github.com/spec/v1'), false)
  t.is(isPointer(''), false)
  t.is(isPointer(Buffer.from([0x89, 0x50, 0x4e, 0x47])), false) // PNG header
})

test('parse rejects malformed pointer', (t) => {
  try {
    parse('not a pointer')
    t.fail('should throw')
  } catch (err) {
    t.ok(err.message.includes('Invalid'))
  }
})

test('INLINE_MAX is 64', (t) => {
  t.is(INLINE_MAX, 64)
})

test('serialize uses inline for exactly 64 chunks', (t) => {
  const chunks = Array.from({ length: 64 }, (_, i) => ({
    hash: i.toString(16).padStart(64, '0'),
    size: 16384
  }))

  const text = serialize({
    oid: 'a'.repeat(64),
    size: 64 * 16384,
    chunkCount: 64,
    chunks
  })

  t.ok(text.includes('blake2b:'), 'inline chunks present')
  t.ok(!text.includes('manifest'), 'no manifest reference')
})

test('serialize uses manifest for 65+ chunks', (t) => {
  const text = serialize({
    oid: 'a'.repeat(64),
    size: 65 * 16384,
    chunkCount: 65,
    manifestHash: 'b'.repeat(64)
  })

  t.ok(text.includes('manifest'), 'manifest reference present')
  // Only the manifest line contains blake2b — no per-chunk lines
  const lines = text.trim().split('\n')
  const chunkLines = lines.filter(l => l.match(/^blake2b:[0-9a-f]+ \d+$/))
  t.is(chunkLines.length, 0, 'no inline chunk lines')
})

test('parse accepts legacy 3-line pointer (header + oid + size, no chunks)', (t) => {
  // Older serializers omitted the `chunks N` line. Existing repos still
  // contain pointers in this shape; sync paths only need oid+size to
  // fetch by content hash, so parse must accept them.
  const legacy = 'hyper-overlay v1\n' +
    'oid blake2b:' + 'a'.repeat(64) + '\n' +
    'size 4096\n'

  const parsed = parse(legacy)
  t.is(parsed.version, 1)
  t.is(parsed.oid, 'a'.repeat(64))
  t.is(parsed.size, 4096)
  t.is(parsed.chunkCount, 0, 'chunkCount defaults to 0 for legacy pointers')
  t.is(parsed.chunks, null)
  t.is(parsed.manifestHash, null)
})