OverviewFilesCommitsIssuesDocsReleasesPeers

favela-peer / test / files-crossstore.test.js

54 lines · javascript ref:
// ⛔ THE BUG. Files and Avatars fetch from the SAME overlay by the SAME content hash, and differ
// only in where they cache locally — blobs/<oid> vs avatars/<oid>. But get() ignores the path
// fetchFile RETURNS and re-reads its own expected path. So when the overlay already holds the bytes
// under the other store's directory, the file is found and then looked for somewhere it is not.
//
// Measured on ashatron: its own avatar sat at avatars/<oid>, and file-get for that oid answered
// {found:false} on the machine holding it. Which made the bridge's per-user blob fetcher look
// broken when it was faithfully reporting what favela-peer told it.

import test from 'brittle'
import fs from '#node:fs'
import os from '#node:os'
import { join } from '#node:path'
import { Files } from '../lib/files.js'

const PNG = Buffer.from('89504e470d0a1a0a0000000d49484452', 'hex')
const tmp = () => fs.mkdtempSync(join(os.tmpdir(), 'xstore-'))

test('★★ a file the overlay holds under ANOTHER path is still found', async (t) => {
  const storage = tmp()
  const oid = 'bb02a15a7f755740174f4ae6b9b4a9724cd53d4159878844db79e3ce9f94b580'

  // The bytes exist, cached by the AVATARS store — a different directory from the one Files uses.
  const elsewhere = join(storage, 'avatars')
  fs.mkdirSync(elsewhere, { recursive: true })
  fs.writeFileSync(join(elsewhere, oid), PNG)

  const files = new Files(storage, {
    // What hyper-overlay does on a local hit: returns where it ALREADY is, not where you asked.
    async fetchFile (hash, opts) { return { destPath: join(elsewhere, hash), local: true, size: PNG.length } }
  })

  const got = await files.get(oid)
  t.ok(got, 'found, rather than reported missing on the machine holding it')
  t.is(got.mimeType, 'image/png', 'and typed from the bytes')
  t.is(got.path, join(elsewhere, oid), 'at the path the overlay actually has it')
})

test('★ a fetch that lands in this store\'s own directory still works', async (t) => {
  const storage = tmp()
  const oid = 'c'.repeat(64)
  const files = new Files(storage, {
    async fetchFile (hash, opts) { fs.writeFileSync(opts.destPath, PNG); return { destPath: opts.destPath, local: false } }
  })
  const got = await files.get(oid)
  t.ok(got)
  t.is(got.mimeType, 'image/png')
})

test('⛔ genuinely absent is still null', async (t) => {
  const files = new Files(tmp(), { async fetchFile () { return null } })
  t.is(await files.get('d'.repeat(64)), null)
})