'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const path = require('node:path'); const { parseDsl } = require('../src/dsl-parser'); const { loadAtlasDirs } = require('../src/atlas-loader'); const { buildMap } = require('../src/builder'); const { encodeGid } = require('../src/gid'); const FIX_ATLAS = path.join(__dirname, 'fixtures', 'atlases'); const { registry } = loadAtlasDirs([FIX_ATLAS]); function build(src, opts = {}) { const ast = parseDsl(src); return buildMap(ast, registry, opts); } test('buildMap: minimal map produces schema-v2 shape', () => { const map = build('size 2 2\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass\n', { defaultId: 'fallback' }); assert.equal(map.schema_version, 2); assert.equal(map.id, 'fallback'); assert.deepEqual(map.size, { w: 2, h: 2 }); assert.deepEqual(map.atlases, ['atlas_a']); assert.equal(map.layers.surface.tiles.length, 4); // Cell (0,0) is index 0 (row-major: y*w + x) assert.equal(map.layers.surface.tiles[0], encodeGid(0, 1, 0)); assert.equal(map.layers.surface.tiles[1], 0); // (1,0) empty }); test('buildMap: AST id wins over defaultId', () => { const map = build('id from_dsl\nsize 1 1\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass\n', { defaultId: 'fallback' }); assert.equal(map.id, 'from_dsl'); }); test('buildMap: fill produces correct row-major GID array', () => { const src = 'size 3 3\natlas atlas_a\nlayer surface\n fill 0 0 2 2 atlas_a:stone\n'; const map = build(src, { defaultId: 'test' }); const gid = encodeGid(0, 2, 0); for (let i = 0; i < 9; i++) assert.equal(map.layers.surface.tiles[i], gid, `cell ${i}`); }); test('buildMap: rotation encodes into GID', () => { const src = 'size 1 1\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass rot 3\n'; const map = build(src, { defaultId: 't' }); assert.equal(map.layers.surface.tiles[0], encodeGid(0, 1, 3)); }); test('buildMap: multiple atlases get correct atlas_index', () => { const src = 'size 1 1\natlas atlas_a\natlas atlas_b\nlayer surface\n set 0 0 atlas_b:water\n'; const map = build(src, { defaultId: 't' }); // atlas_b is index 1 in declaration order assert.equal(map.layers.surface.tiles[0], encodeGid(1, 1, 0)); }); test('buildMap: roof block produces flat array of size w*h', () => { const src = 'size 2 2\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass\nroof\n set 1 1 1\n'; const map = build(src, { defaultId: 't' }); assert.equal(map.roof.length, 4); assert.equal(map.roof[3], 1); assert.equal(map.roof[0], 0); }); test('buildMap: empty layers are not serialised', () => { const src = 'size 2 2\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass\n'; const map = build(src, { defaultId: 't' }); assert.deepEqual(Object.keys(map.layers), ['surface']); assert.equal(map.roof, undefined); }); test('buildMap: unknown atlas reference is an error', () => { const src = 'size 1 1\natlas atlas_a\nlayer surface\n set 0 0 atlas_c:grass\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => err.message.match(/atlas_c.*not declared/) && err.line === 4 ); }); test('buildMap: undeclared atlas in DSL is an error', () => { const src = 'size 1 1\natlas missing_atlas\nlayer surface\n set 0 0 missing_atlas:grass\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => !!err.message.match(/missing_atlas.*registry/) ); }); test('buildMap: unknown tile name in atlas is an error', () => { const src = 'size 1 1\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:nonexistent\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => err.message.match(/nonexistent/) && err.line === 4 ); }); test('buildMap: out-of-bounds set is an error', () => { const src = 'size 4 4\natlas atlas_a\nlayer surface\n set 5 0 atlas_a:grass\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => err.message.match(/out of bounds/) && err.line === 4 ); }); test('buildMap: out-of-bounds fill is an error', () => { const src = 'size 4 4\natlas atlas_a\nlayer surface\n fill 0 0 4 4 atlas_a:grass\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => err.message.match(/out of bounds/) && err.line === 4 ); }); test('buildMap: out-of-bounds roof set is an error', () => { const src = 'size 2 2\natlas atlas_a\nlayer surface\n set 0 0 atlas_a:grass\nroof\n set 2 2 1\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => err.message.match(/out of bounds/) && err.line === 6 ); }); test('buildMap: no atlases declared is an error', () => { const src = 'size 1 1\n'; assert.throws( () => build(src, { defaultId: 't' }), (err) => !!err.message.match(/atlases.*non-empty/) ); }); test('buildMap: empty defaultId AND no DSL id is an error', () => { const src = 'size 1 1\natlas atlas_a\n'; assert.throws( () => build(src, { defaultId: '' }), (err) => !!err.message.match(/id/) ); });