import { describe, expect, it } from 'vitest' import fs from 'node:fs' import path from 'node:path' import zlib from 'node:zlib' import { MISSILE_METAS, CORPSEEXPLOSION_META, POISONCORPSEEXPLOSION_META, POISONEXPLOSIONCLOUD_META, POISONPUFF_META, CURSECAST_META, BONECAST_META, BONESPEARTRAIL_META, REVIVE_META, REVIVESMALL_META, REVIVEMEDIUM_META, REVIVELARGE_META, BONESPEAREXPLODE_META, TEETH_META, TEETHEXPLODE_META, BONESPEAR_META, BONESPIRIT_META, BONESPIRITEXPLODE_META, POISONNOVA_META, type MissileMeta, } from '../../../src/client/render/missiles-meta.ts' import { OVERLAY_METAS, ALL_OVERLAY_NAMES, CURSE_AMPLIFY_DAMAGE_OVERLAY_META, CURSE_DIM_VISION_OVERLAY_META, CURSE_WEAKEN_OVERLAY_META, CURSE_IRON_MAIDEN_OVERLAY_META, CURSE_TERROR_OVERLAY_META, CURSE_CONFUSE_OVERLAY_META, CURSE_LIFE_TAP_OVERLAY_META, CURSE_ATTRACT_OVERLAY_META, CURSE_DECREPIFY_OVERLAY_META, CURSE_LOWER_RESIST_OVERLAY_META, HIT_IRON_MAIDEN_OVERLAY_META, HIT_LIFE_TAP_OVERLAY_META, BONE_ARMOR_CAST_OVERLAY_META, BONE_ARMOR_FRONT_OVERLAY_META, BONE_ARMOR_BACK_OVERLAY_META, type OverlayMeta, } from '../../../src/client/render/overlays-meta.ts' // Strict PNG RGBA Decoder function decodeRgbaPng(buf: Buffer) { const width = buf.readUInt32BE(16) const height = buf.readUInt32BE(20) const bitDepth = buf[24]! const colorType = buf[25]! const idatParts: Buffer[] = [] let offset = 8 while (offset < buf.length) { const len = buf.readUInt32BE(offset) const type = buf.subarray(offset + 4, offset + 8).toString('ascii') if (type === 'IDAT') { idatParts.push(buf.subarray(offset + 8, offset + 8 + len)) } offset += 12 + len } const idat = Buffer.concat(idatParts) const raw = zlib.inflateSync(idat) const bpp = 4 const rowBytes = width * bpp const pixels = new Uint8Array(width * height * 4) let srcAt = 0 let dstAt = 0 for (let y = 0; y < height; y++) { const filter = raw[srcAt++] const row = raw.subarray(srcAt, srcAt + rowBytes) srcAt += rowBytes const prevRow = y > 0 ? pixels.subarray((y - 1) * rowBytes, y * rowBytes) : null const curRow = pixels.subarray(dstAt, dstAt + rowBytes) for (let x = 0; x < rowBytes; x++) { const left = x >= bpp ? curRow[x - bpp]! : 0 const up = prevRow ? prevRow[x]! : 0 const upLeft = prevRow && x >= bpp ? prevRow[x - bpp]! : 0 let val = row[x]! if (filter === 0) { // None } else if (filter === 1) { val = (val + left) & 0xff } else if (filter === 2) { val = (val + up) & 0xff } else if (filter === 3) { val = (val + Math.floor((left + up) / 2)) & 0xff } else if (filter === 4) { const p = left + up - upLeft const pa = Math.abs(p - left) const pb = Math.abs(p - up) const pc = Math.abs(p - upLeft) let pr = upLeft if (pa <= pb && pa <= pc) pr = left else if (pb <= pc) pr = up val = (val + pr) & 0xff } curRow[x] = val } dstAt += rowBytes } return { width, height, bitDepth, colorType, pixels } } describe('Adv Coverage Audit — Necromancer Assets & Metadata Integrity', () => { const publicMissilesDir = path.resolve(__dirname, '../../../public/missiles') const packMissilesDir = path.resolve(__dirname, '../../../samples/d2-packs/missiles') const publicOverlaysDir = path.resolve(__dirname, '../../../public/overlays') const packOverlaysDir = path.resolve(__dirname, '../../../samples/d2-packs/overlays') const NECRO_MISSILE_REQS = [ { name: 'corpseexplosion', celFile: 'CorpseExplodeGuts', meta: CORPSEEXPLOSION_META }, { name: 'poisoncorpseexplosion', celFile: 'CorpseExplodeGutsPoison', meta: POISONCORPSEEXPLOSION_META }, { name: 'poisonexplosioncloud', celFile: 'PoisonSparks', meta: POISONEXPLOSIONCLOUD_META }, { name: 'poisonpuff', celFile: 'PoisonSmokePuff', meta: POISONPUFF_META }, { name: 'cursecast', celFile: 'CurseCast', meta: CURSECAST_META }, { name: 'bonecast', celFile: 'BoneCast', meta: BONECAST_META }, { name: 'bonespeartrail', celFile: 'BoneSpearTrail', meta: BONESPEARTRAIL_META }, { name: 'revive', celFile: 'Revive', meta: REVIVE_META }, { name: 'revivesmall', celFile: 'Revive', meta: REVIVESMALL_META }, { name: 'revivemedium', celFile: 'Revive', meta: REVIVEMEDIUM_META }, { name: 'revivelarge', celFile: 'Revive', meta: REVIVELARGE_META }, { name: 'bonespearexplode', celFile: 'teethexplode', meta: BONESPEAREXPLODE_META }, { name: 'teeth', celFile: 'teethMissile', meta: TEETH_META }, { name: 'teethexplode', celFile: 'teethexplode', meta: TEETHEXPLODE_META }, { name: 'bonespear', celFile: 'BoneSpear', meta: BONESPEAR_META }, { name: 'bonespirit', celFile: 'BoneSpirit', meta: BONESPIRIT_META }, { name: 'bonespiritexplode', celFile: 'BoneSpiritExplode', meta: BONESPIRITEXPLODE_META }, { name: 'poisonnova', celFile: 'poisonNova', meta: POISONNOVA_META }, ] as const const NECRO_OVERLAY_REQS = [ // 10 Curses overhead state icons { name: 'curseamplifydamage', celFile: 'CurseAmplifyDamageEffect', meta: CURSE_AMPLIFY_DAMAGE_OVERLAY_META, preDraw: false }, { name: 'cursedimvision', celFile: 'CurseDimVisionEffect', meta: CURSE_DIM_VISION_OVERLAY_META, preDraw: false }, { name: 'curseweaken', celFile: 'CurseWeakenEffect', meta: CURSE_WEAKEN_OVERLAY_META, preDraw: false }, { name: 'curseironmaiden', celFile: 'CurseIronMaidenEffect', meta: CURSE_IRON_MAIDEN_OVERLAY_META, preDraw: false }, { name: 'curseterror', celFile: 'CurseTerrorEffect', meta: CURSE_TERROR_OVERLAY_META, preDraw: false }, { name: 'curseconfuse', celFile: 'CurseConfuseEffect', meta: CURSE_CONFUSE_OVERLAY_META, preDraw: false }, { name: 'cursereversevampire', celFile: 'CurseReverseVampireEffect', meta: CURSE_LIFE_TAP_OVERLAY_META, preDraw: false }, { name: 'curseattract', celFile: 'CurseAttractHarmEffect', meta: CURSE_ATTRACT_OVERLAY_META, preDraw: false }, { name: 'cursedecrepify', celFile: 'CurseDecrepifyEffect', meta: CURSE_DECREPIFY_OVERLAY_META, preDraw: false }, { name: 'curselowerresist', celFile: 'CurseLowerResistEffect', meta: CURSE_LOWER_RESIST_OVERLAY_META, preDraw: false }, // 2 Hit overlays { name: 'hit_ironmaiden', celFile: 'AuraThornsHit', meta: HIT_IRON_MAIDEN_OVERLAY_META, preDraw: false }, { name: 'hit_reversevampire', celFile: 'CurseRevVampireHit', meta: HIT_LIFE_TAP_OVERLAY_META, preDraw: false }, // 3 Bone Armor overlays { name: 'bonearmor_cast', celFile: 'BoneShieldEmerge', meta: BONE_ARMOR_CAST_OVERLAY_META, preDraw: false }, { name: 'bonearmor_front', celFile: 'BoneShieldFront', meta: BONE_ARMOR_FRONT_OVERLAY_META, preDraw: false }, { name: 'bonearmor_back', celFile: 'BoneShieldBack', meta: BONE_ARMOR_BACK_OVERLAY_META, preDraw: true }, ] as const describe('Dimension 1: Feature Matrix & Skill Requirements Coverage', () => { it('1.1 Verifies all 30 Necromancer skills required missiles are registered in MISSILE_METAS', () => { for (const m of NECRO_MISSILE_REQS) { expect(MISSILE_METAS[m.name], `Missile ${m.name} must be in MISSILE_METAS`).toBeDefined() expect(MISSILE_METAS[m.name]).toBe(m.meta) expect(m.meta.name).toBe(m.name) expect(m.meta.celFile).toBe(m.celFile) } }) it('1.2 Verifies all 10 Curses overhead overlays, 2 hit overlays, and 3 Bone Armor overlays are in OVERLAY_METAS', () => { for (const o of NECRO_OVERLAY_REQS) { expect(OVERLAY_METAS[o.name], `Overlay ${o.name} must be in OVERLAY_METAS`).toBeDefined() expect(OVERLAY_METAS[o.name]).toBe(o.meta) expect(o.meta.name).toBe(o.name) expect(o.meta.celFile).toBe(o.celFile) expect(o.meta.preDraw).toBe(o.preDraw) } }) it('1.3 Verifies ALL_OVERLAY_NAMES contains all 15 Necromancer overlays', () => { for (const o of NECRO_OVERLAY_REQS) { expect(ALL_OVERLAY_NAMES).toContain(o.name) } }) it('1.4 Verifies bone armor back is preDraw:true while front and cast are preDraw:false', () => { expect(BONE_ARMOR_BACK_OVERLAY_META.preDraw).toBe(true) expect(BONE_ARMOR_FRONT_OVERLAY_META.preDraw).toBe(false) expect(BONE_ARMOR_CAST_OVERLAY_META.preDraw).toBe(false) }) }) describe('Dimension 2: Zero Null / Missing / NaN Frame Arrays', () => { it('2.1 Checks for zero null, undefined, or NaN values in missile frame geometry', () => { for (const m of NECRO_MISSILE_REQS) { const meta = m.meta expect(meta.width).toBeGreaterThan(0) expect(meta.height).toBeGreaterThan(0) expect(meta.directions).toBeGreaterThan(0) expect(meta.framesPerDirection).toBeGreaterThan(0) expect(meta.animSpeed).toBeGreaterThan(0) expect(meta.groups).toBeDefined() expect(meta.groups.length).toBe(meta.directions) for (let d = 0; d < meta.groups.length; d++) { const group = meta.groups[d]! expect(group.length).toBe(meta.framesPerDirection) for (let f = 0; f < group.length; f++) { const frame = group[f]! expect(frame.length).toBe(6) for (let i = 0; i < 6; i++) { const val = frame[i] expect(val, `Null/undefined/NaN in ${m.name} dir ${d} frame ${f} idx ${i}`).not.toBeNull() expect(val).not.toBeUndefined() expect(typeof val).toBe('number') expect(Number.isNaN(val)).toBe(false) expect(Number.isFinite(val)).toBe(true) } const [x, y, w, h, ax, ay] = frame expect(x).toBeGreaterThanOrEqual(0) expect(y).toBeGreaterThanOrEqual(0) expect(w).toBeGreaterThan(0) expect(h).toBeGreaterThan(0) expect(x + w).toBeLessThanOrEqual(meta.width) expect(y + h).toBeLessThanOrEqual(meta.height) expect(ax).toBeGreaterThanOrEqual(-1000) expect(ax).toBeLessThanOrEqual(1000) expect(ay).toBeGreaterThanOrEqual(-1000) expect(ay).toBeLessThanOrEqual(1000) } } } }) it('2.2 Checks for zero null, undefined, or NaN values in overlay frame geometry', () => { for (const o of NECRO_OVERLAY_REQS) { const meta = o.meta expect(meta.width).toBeGreaterThan(0) expect(meta.height).toBeGreaterThan(0) expect(meta.directions).toBe(1) expect(meta.frames).toBeGreaterThan(0) expect(meta.framesPerDirection).toBe(meta.frames) expect(meta.animRate).toBeGreaterThan(0) expect(meta.trans).toBe(3) expect(meta.groups).toBeDefined() expect(meta.groups.length).toBe(1) const group = meta.groups[0]! expect(group.length).toBe(meta.frames) for (let f = 0; f < group.length; f++) { const frame = group[f]! expect(frame.length).toBe(6) for (let i = 0; i < 6; i++) { const val = frame[i] expect(val, `Null/undefined/NaN in ${o.name} frame ${f} idx ${i}`).not.toBeNull() expect(val).not.toBeUndefined() expect(typeof val).toBe('number') expect(Number.isNaN(val)).toBe(false) expect(Number.isFinite(val)).toBe(true) } const [x, y, w, h, ax, ay] = frame expect(x).toBeGreaterThanOrEqual(0) expect(y).toBeGreaterThanOrEqual(0) expect(w).toBeGreaterThan(0) expect(h).toBeGreaterThan(0) expect(x + w).toBeLessThanOrEqual(meta.width) expect(y + h).toBeLessThanOrEqual(meta.height) expect(ax).toBeGreaterThanOrEqual(-1000) expect(ax).toBeLessThanOrEqual(1000) expect(ay).toBeGreaterThanOrEqual(-1000) expect(ay).toBeLessThanOrEqual(1000) } } }) }) describe('Dimension 3: Physical Asset Disk Mirroring & File Health', () => { it('3.1 Every Necromancer missile exists in public/missiles and samples/d2-packs/missiles', () => { for (const m of NECRO_MISSILE_REQS) { for (const dir of [publicMissilesDir, packMissilesDir]) { const pngPath = path.join(dir, `${m.name}.png`) const jsonPath = path.join(dir, `${m.name}.json`) expect(fs.existsSync(pngPath), `Missing ${pngPath}`).toBe(true) expect(fs.existsSync(jsonPath), `Missing ${jsonPath}`).toBe(true) const stat = fs.statSync(pngPath) expect(stat.size).toBeGreaterThan(500) const buf = fs.readFileSync(pngPath) expect(buf.readUInt32BE(0)).toBe(0x89504e47) // \x89PNG } } }) it('3.2 Every Necromancer overlay exists in public/overlays and samples/d2-packs/overlays', () => { for (const o of NECRO_OVERLAY_REQS) { for (const dir of [publicOverlaysDir, packOverlaysDir]) { const pngPath = path.join(dir, `${o.name}.png`) const jsonPath = path.join(dir, `${o.name}.json`) expect(fs.existsSync(pngPath), `Missing ${pngPath}`).toBe(true) expect(fs.existsSync(jsonPath), `Missing ${jsonPath}`).toBe(true) const stat = fs.statSync(pngPath) expect(stat.size).toBeGreaterThan(500) const buf = fs.readFileSync(pngPath) expect(buf.readUInt32BE(0)).toBe(0x89504e47) // \x89PNG } } }) }) describe('Dimension 4: 32-bit RGBA & Transparent Border Thresholding Invariant', () => { it('4.1 Asserts max(r,g,b) <= 4 implies alpha === 0 across all Necromancer missiles', () => { for (const m of NECRO_MISSILE_REQS) { const pngPath = path.join(publicMissilesDir, `${m.name}.png`) const buf = fs.readFileSync(pngPath) const { width, height, pixels } = decodeRgbaPng(buf) expect(width).toBe(m.meta.width) expect(height).toBe(m.meta.height) let blackOpaqueCount = 0 for (let i = 0; i < pixels.length; i += 4) { const r = pixels[i]! const g = pixels[i + 1]! const b = pixels[i + 2]! const a = pixels[i + 3]! const mVal = Math.max(r, g, b) if (mVal <= 4 && a > 0) { blackOpaqueCount++ } } expect(blackOpaqueCount, `Dark halo pixels in ${m.name}.png`).toBe(0) } }) it('4.2 Asserts max(r,g,b) <= 4 implies alpha === 0 across all Necromancer overlays', () => { for (const o of NECRO_OVERLAY_REQS) { const pngPath = path.join(publicOverlaysDir, `${o.name}.png`) const buf = fs.readFileSync(pngPath) const { width, height, pixels } = decodeRgbaPng(buf) expect(width).toBe(o.meta.width) expect(height).toBe(o.meta.height) let blackOpaqueCount = 0 for (let i = 0; i < pixels.length; i += 4) { const r = pixels[i]! const g = pixels[i + 1]! const b = pixels[i + 2]! const a = pixels[i + 3]! const mVal = Math.max(r, g, b) if (mVal <= 4 && a > 0) { blackOpaqueCount++ } } expect(blackOpaqueCount, `Dark halo pixels in ${o.name}.png`).toBe(0) } }) }) })