// The mip pyramid. Pure, so all of this runs with no overlay, no socket and no encoder.
import test from 'brittle'
import {
planLevels, scaleTo, makePointer, selectLevel, IMAGE_LEVELS, VIDEO_LEVELS, AUTHOR, derivedBy
} from '../lib/levels.js'
// ⛔ §6.6.1 #2 — a level is a FLOOR, not a guarantee.
test('★ never upscale — a small asset has fewer levels, not padded ones', (t) => {
t.is(planLevels({ mimeType: 'image/png', width: 4000, height: 3000 }).length, IMAGE_LEVELS.length,
'a big picture gets the whole ladder below it')
t.alike(planLevels({ mimeType: 'image/png', width: 300, height: 200 }), [],
'⛔ a 300px picture generates NOTHING below it — the source is its only level')
const mid = planLevels({ mimeType: 'image/png', width: 800, height: 600 }).map((l) => l.name)
t.alike(mid, ['small', 'thumb'], 'and a middling one gets only the levels genuinely smaller')
})
test('video ladders on height, the way every encoder ladder is expressed', (t) => {
t.alike(planLevels({ mimeType: 'video/mp4', width: 1920, height: 1080 }).map((l) => l.name),
['720p', '480p'], 'never a level at or above the source height')
t.alike(planLevels({ mimeType: 'video/mp4', width: 640, height: 360 }), [], 'a small clip has one level')
t.ok(VIDEO_LEVELS.every((l) => l.bitrate && l.profile), 'each carries its encoder settings')
})
test('scaling preserves aspect and never yields zero', (t) => {
t.alike(scaleTo({ width: 4000, height: 2000 }, 2000), { width: 2000, height: 1000 })
t.alike(scaleTo({ width: 100, height: 50 }, 2000), { width: 100, height: 50 }, 'no upscale')
const tiny = scaleTo({ width: 3000, height: 4 }, 320)
t.ok(tiny.height >= 1, 'a wafer-thin image still has a height')
})
test('the pointer carries the levels — largest first, base is level 0', (t) => {
const p = makePointer({
mimeType: 'image/png',
size: 900,
levels: [
{ oid: 'c', w: 320, h: 240, size: 50 },
{ oid: 'a', w: 2000, h: 1500, size: 900 },
{ oid: 'b', w: 640, h: 480, size: 200 }
]
})
t.alike(p.levels.map((l) => l.oid), ['a', 'b', 'c'], 'largest first')
t.is(p.levels[0].by, AUTHOR, 'and authored unless said otherwise')
})
// ⛔ §6.6.4 #2/#3 — deriving is not authoring.
test('⛔ a derived level says who derived it', (t) => {
const p = makePointer({
mimeType: 'image/png',
size: 900,
levels: [
{ oid: 'a', w: 2000, h: 1500, size: 900 },
{ oid: 'b', w: 640, h: 480, size: 200, by: derivedBy('ap-bridge') }
]
})
t.is(p.levels[0].by, AUTHOR, "the author's own bytes")
t.is(p.levels[1].by, 'derived:ap-bridge', '⛔ or an author’s bytes and a re-encode become the same thing')
})
// ⛔ Bridges SELECT.
test('★ selectLevel picks the biggest that fits the ceiling', (t) => {
const p = makePointer({
mimeType: 'image/png',
size: 3000000,
levels: [
{ oid: 'a', w: 2000, h: 1500, size: 3000000 },
{ oid: 'b', w: 1280, h: 960, size: 900000 },
{ oid: 'c', w: 640, h: 480, size: 200000 }
]
})
t.is(selectLevel(p, { maxBytes: 2000000 }).level.oid, 'b', 'the base is too heavy, the next one fits')
t.is(selectLevel(p, { maxBytes: 1000000 }).level.oid, 'b', 'bsky post ceiling')
t.is(selectLevel(p, { maxBytes: 500000 }).level.oid, 'c', 'a profile ceiling reaches further down')
t.is(selectLevel(p, { maxBytes: Infinity }).level.oid, 'a', 'and with no ceiling, the author’s own bytes')
})
// ⛔ §6.6.4 #1 — a clear absence, never a 404.
test('⛔ when nothing fits it SAYS so rather than returning silence', (t) => {
const p = makePointer({ mimeType: 'image/png', size: 9, levels: [{ oid: 'a', w: 100, h: 100, size: 5000000 }] })
const r = selectLevel(p, { maxBytes: 1000 })
t.absent(r.level)
t.ok(r.reason.includes('no level fits'), 'the reason distinguishes it from a failed fetch: ' + r.reason)
t.ok(selectLevel({ levels: [] }, {}).reason.includes('no levels'), 'and so does an asset with none')
})
test('the chosen level is always reported, even on success', (t) => {
const p = makePointer({ mimeType: 'image/png', size: 9, levels: [{ oid: 'a', w: 100, h: 100, size: 9 }] })
t.ok(selectLevel(p, { maxBytes: 100 }).reason, '⛔ a bridge that quietly serves a smaller picture has altered what was published')
})