import { describe, expect, it } from 'vitest'; import { readMapFiles } from '../src/server/assets'; import { buildTerrain, MapValidationError, parseMapDefinition, type MapDefinition, type RgbaImage } from '../src/shared/maps/loader'; import { ASSETS, realMaterials } from './helpers'; const materials = realMaterials(); function def(overrides: Partial = {}): MapDefinition { return parseMapDefinition({ id: 'test', name: 'Test', size: [4, 4], materialPalette: { '#000000': 0, '#F2C46D': 1, '#8899AA': 255 }, spawns: [[1, 0]], cherrySpawn: [2, 0], custard: { startY: 3, speed: 1, finalRushSpeed: 2 }, items: { interval: [1, 2], weights: { candle: 1 } }, ...overrides, }); } /** Rows of 0xRRGGBB pixels -> RGBA image. */ function image(rows: number[][]): RgbaImage { const h = rows.length; const w = rows[0].length; const data = new Uint8Array(w * h * 4); rows.flat().forEach((c, i) => { data.set([c >> 16, (c >> 8) & 0xff, c & 0xff, 255], i * 4); }); return { width: w, height: h, data }; } const E = 0x000000; const S = 0xf2c46d; const P = 0x8899aa; const valid = [ [E, E, E, E], [E, E, E, E], [S, S, S, S], [P, P, P, P], ]; describe('map loader', () => { it('loads a valid map', () => { const bm = buildTerrain(def(), image(valid), materials); expect(bm.get(0, 2)).toBe(1); expect(bm.get(0, 3)).toBe(255); }); it('rejects colors outside the palette', () => { const bad = valid.map((r) => [...r]); bad[2][1] = 0x123456; expect(() => buildTerrain(def(), image(bad), materials)).toThrow(/#123456.*not in the palette/); }); it('rejects a map without plate', () => { const noPlate = valid.map((r) => r.map((c) => (c === P ? S : c))); expect(() => buildTerrain(def(), image(noPlate), materials)).toThrow(/no plate/); }); it('rejects spawns inside terrain or above nothing', () => { expect(() => buildTerrain(def({ spawns: [[1, 2]] }), image(valid), materials)).toThrow(/inside solid/); const hole = valid.map((r) => [...r]); hole[2][0] = E; hole[3][0] = E; expect(() => buildTerrain(def({ spawns: [[0, 0]] }), image(hole), materials)).toThrow(/no ground/); }); it('rejects size mismatches and malformed map.json', () => { expect(() => buildTerrain(def({ size: [5, 4] }), image(valid), materials)).toThrow(/terrain.png is 4x4/); expect(() => parseMapDefinition({ id: 'x' }, 'x')).toThrow(MapValidationError); expect(() => parseMapDefinition({ ...def(), id: 'other' }, 'test')).toThrow(/folder/); }); it('the reference map gateau-etages is valid', () => { const raw = readMapFiles(ASSETS, 'gateau-etages'); const map = parseMapDefinition(raw.definition, 'gateau-etages'); const bm = buildTerrain(map, raw.terrain, materials); expect(bm.width).toBe(2048); expect(bm.height).toBe(1024); }); });