OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / test / vdrive-ci.test.js

166 lines · javascript ref:
/**
 * vdrive in ci.yaml — config parsing and the publish hook.
 *
 * The hook writes a real drive over a release version dir and returns a
 * pear:// link, without the drive storing any of the bytes.
 */
import test from 'brittle'
import fs from '#node:fs'
import fsp from '#node:fs/promises'
import path from '#node:path'
import os from '#node:os'

import { loadCIConfig } from '../lib/ci-runner.js'
import { publishVersionToDrive, vdriveStoreDir } from '../lib/vdrive-publish.js'

function tmpdir (name) {
  return fsp.mkdtemp(path.join(os.tmpdir(), 'pgv3-vdrive-' + name + '-'))
}

function writeConfig (dir, yaml) {
  fs.mkdirSync(path.join(dir, '.pear-git'), { recursive: true })
  fs.writeFileSync(path.join(dir, '.pear-git', 'ci.yaml'), yaml)
  return dir
}

const STEPS = 'steps:\n  - name: build\n    run: "true"\n'

test('ci.yaml vdrive: absent → null', async (t) => {
  const dir = await tmpdir('cfg1')
  writeConfig(dir, STEPS)
  t.is(loadCIConfig(dir).vdrive, null)
})

test('ci.yaml vdrive: true → enabled, all channels', async (t) => {
  const dir = await tmpdir('cfg2')
  writeConfig(dir, STEPS + 'vdrive: true\n')
  const cfg = loadCIConfig(dir)
  t.is(cfg.vdrive.enabled, true)
  t.is(cfg.vdrive.channels, null)
})

test('ci.yaml vdrive: false → disabled', async (t) => {
  const dir = await tmpdir('cfg3')
  writeConfig(dir, STEPS + 'vdrive: false\n')
  t.is(loadCIConfig(dir).vdrive.enabled, false)
})

test('ci.yaml vdrive: mapping with channels', async (t) => {
  const dir = await tmpdir('cfg4')
  writeConfig(dir, STEPS + 'vdrive:\n  channels: [production]\n')
  const cfg = loadCIConfig(dir)
  t.is(cfg.vdrive.enabled, true)
  t.alike(cfg.vdrive.channels, ['production'])
})

test('ci.yaml vdrive: garbage is rejected', async (t) => {
  const dir = await tmpdir('cfg5')
  writeConfig(dir, STEPS + 'vdrive: maybe\n')
  t.exception(() => loadCIConfig(dir), /vdrive/)

  const dir2 = await tmpdir('cfg6')
  writeConfig(dir2, STEPS + 'vdrive:\n  channels: notalist\n')
  t.exception(() => loadCIConfig(dir2), /channels/)
})

test('hook is inert when not enabled', async (t) => {
  const res = await publishVersionToDrive({ vdrive: null })
  t.is(res.ok, false)
  t.is(res.skipped, true)
})

test('hook skips a channel that is not selected', async (t) => {
  const res = await publishVersionToDrive({
    releaseDir: '/tmp',
    repo: 'x',
    version: '1',
    channel: 'experimental',
    vdrive: { enabled: true, channels: ['production'] }
  })
  t.is(res.skipped, true)
  t.ok(res.reason.includes('channel not selected'))
})

test('hook publishes a version dir into a drive holding no blob bytes', async (t) => {
  const releaseDir = await tmpdir('rel')
  const repo = 'demo'
  const version = '1.0.0'
  const versionDir = path.join(releaseDir, repo, 'versions', version)
  await fsp.mkdir(path.join(versionDir, 'by-arch', 'linux-x64', 'app'), { recursive: true })

  const payload = Buffer.alloc(2 * 1024 * 1024, 7)
  await fsp.writeFile(path.join(versionDir, 'by-arch', 'linux-x64', 'app', 'demo'), payload)
  await fsp.writeFile(path.join(versionDir, 'package.json'), JSON.stringify({ name: repo }))
  await fsp.writeFile(path.join(versionDir, 'empty.stamp'), '')

  const { chunk } = await import('hyper-overlay/chunker')
  const res = await publishVersionToDrive({
    releaseDir,
    repo,
    version,
    channel: 'production',
    vdrive: { enabled: true, channels: null },
    chunk
  })

  t.is(res.ok, true, 'published')
  t.ok(res.link.startsWith('pear://'), 'has a pear link: ' + res.link)
  t.is(res.files, 3, 'all three files, including the 0-byte one')
  t.ok(res.pointerBlocks > 0, 'blocks were redirected, not stored')

  // the drive must be far smaller than the bytes it publishes
  const storeDir = vdriveStoreDir(releaseDir, repo)
  const size = await dirSize(storeDir)
  t.ok(size < payload.length / 4, `drive store ${size}B << payload ${payload.length}B`)

  await fsp.rm(releaseDir, { recursive: true, force: true })
})

async function dirSize (dir) {
  let total = 0
  for (const e of await fsp.readdir(dir, { withFileTypes: true })) {
    const full = path.join(dir, e.name)
    if (e.isDirectory()) total += await dirSize(full)
    else total += (await fsp.stat(full)).size
  }
  return total
}

test('the drive is recorded in the release manifest', async (t) => {
  // Without this the link is unrecoverable: it was never in the manifest, never
  // in `pgh deploy log`, and once the seeder holds the store the only trace is a
  // daemon log line.
  const releaseDir = await tmpdir('manifest')
  const repo = 'demo'
  const version = '1.0.0'
  const versionDir = path.join(releaseDir, repo, 'versions', version)
  await fsp.mkdir(versionDir, { recursive: true })
  await fsp.writeFile(path.join(versionDir, 'app.bin'), Buffer.alloc(256 * 1024, 4))
  // a manifest as ReleaseManager would have written it
  await fsp.writeFile(
    path.join(versionDir, 'manifest.json'),
    JSON.stringify({ version, builtAt: Date.now(), artifacts: [] }, null, 2)
  )

  const { chunk } = await import('hyper-overlay/chunker')
  const res = await publishVersionToDrive({
    releaseDir,
    repo,
    version,
    channel: 'production',
    vdrive: { enabled: true, channels: null },
    chunk
  })

  t.is(res.ok, true)
  t.is(res.annotated, true, 'manifest was annotated')

  const manifest = JSON.parse(await fsp.readFile(path.join(versionDir, 'manifest.json'), 'utf8'))
  t.ok(manifest.vdrive, 'manifest carries a vdrive block')
  t.is(manifest.vdrive.link, res.link, 'and the link matches what was published')
  t.ok(manifest.vdrive.key && manifest.vdrive.blocks > 0, 'with key and block count')
  t.ok(manifest.version === version, 'existing manifest fields survive')

  await fsp.rm(releaseDir, { recursive: true, force: true })
})