'use strict'; const assert = require('assert/strict'); const crypto = require('crypto'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { nativeRoadEdits, roadRevisions } = require('../src'); const { compileInput } = require('../src/compile/compiler'); const { snapshot: outputSnapshot } = require('./fixture-baseline'); const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'road-revisions-')); const source = '\n'; const overrides = { schema: 'native-road-overrides/v1', overrides: [] }; const signals = { schema: 'native-traffic-signals/v1', provenance: 'empty', assemblies: { type: 'FeatureCollection', features: [] }, }; const options = { edgeLines: false, junctionTemplates: { enabled: false, references: [] } }; fs.writeFileSync(path.join(directory, 'source.osm'), source); fs.writeFileSync(path.join(directory, 'native-road-overrides.json'), `${JSON.stringify(overrides, null, 2)}\n`); fs.writeFileSync(path.join(directory, 'native-traffic-signals.json'), `${JSON.stringify(signals, null, 2)}\n`); const legacyBytes = new Map( ['source.osm', 'native-road-overrides.json', 'native-traffic-signals.json'].map((file) => [ file, fs.readFileSync(path.join(directory, file)), ]), ); const first = roadRevisions.ensureRevisionStore(directory, options); assert.equal(first.baseline.manifest.id, 'rev-0001'); assert.equal(first.baseline.manifest.schema, roadRevisions.REVISION_SCHEMA); assert.equal(first.active.activeRevisionId, 'rev-0001'); assert.equal(first.baseline.directEdits.schema, nativeRoadEdits.EDITS_SCHEMA); assert.equal(first.baseline.directEdits.documentVersion, 0); assert.equal(first.baseline.directEdits.base.areaConfigSha256, first.baseline.manifest.source.areaConfigSha256); assert.deepEqual(first.baseline.areaConfig, options); for (const [file, bytes] of legacyBytes) assert.deepEqual(fs.readFileSync(path.join(directory, file)), bytes, `${file} remains byte-for-byte unchanged`); const digest = crypto.createHash('sha256').update(source).digest('hex'); assert.equal(first.baseline.manifest.source.osmSha256, digest); assert.equal(fs.readdirSync(path.join(directory, 'osm')).filter((file) => file.endsWith('.osm')).length, 1); roadRevisions.ensureRevisionStore(directory, options); assert.equal( fs.readdirSync(path.join(directory, 'osm')).filter((file) => file.endsWith('.osm')).length, 1, 'same OSM is stored once', ); const activeEdits = nativeRoadEdits.emptyEditDocument({ osmSha256: digest, areaConfigSha256: first.baseline.manifest.source.areaConfigSha256, compilerGeometryVersion: 'native-road-package/v1.1', }); activeEdits.operations = [{ id: 'op-1', createdAt: '2026-08-26T12:00:00.000Z', constraintIds: [] }]; fs.writeFileSync(path.join(directory, 'active', 'native-road-edits.json'), `${JSON.stringify(activeEdits, null, 2)}\n`); const checkpoint = roadRevisions.createCheckpoint(directory, 'Before junction changes'); assert.equal(checkpoint.manifest.id, 'rev-0002'); assert.equal(checkpoint.manifest.label, 'Before junction changes'); assert.equal(checkpoint.manifest.parentRevisionId, 'rev-0001'); assert.deepEqual(checkpoint.nativeRoadOverrides, overrides); assert.deepEqual(checkpoint.trafficSignals, signals); assert.deepEqual(checkpoint.directEdits, activeEdits); assert.deepEqual(checkpoint.areaConfig, options); assert.equal( checkpoint.manifest.digests.nativeRoadOverrides, crypto .createHash('sha256') .update(fs.readFileSync(path.join(directory, 'revisions', 'rev-0002', 'native-road-overrides.json'))) .digest('hex'), ); assert.deepEqual( roadRevisions.readRevision(directory, 'rev-0002'), checkpoint, 'a checkpoint can be completely restored', ); assert.throws(() => roadRevisions.createCheckpoint(directory, ''), /label must be a non-empty string/); assert.throws(() => roadRevisions.readRevision(directory, '../rev-0002'), /Invalid revision id/); const changedOptions = { edgeLines: true, junctionTemplates: { enabled: false, references: [] } }; const changedSnapshot = roadRevisions.setActiveAreaConfig(directory, changedOptions); assert.equal( nativeRoadEdits.loadEditDocument(path.join(directory, 'active', 'native-road-edits.json')).base.areaConfigSha256, changedSnapshot.areaConfig.digest, 'the active document points to the current snapshot', ); assert.equal( nativeRoadEdits.loadEditDocument(path.join(directory, 'active', 'native-road-edits.json')).documentVersion, 1, 'updating the active snapshot is a versioned document write', ); assert.deepEqual( roadRevisions.readRevision(directory, 'rev-0002').areaConfig, options, 'old revisions retain their snapshot', ); roadRevisions.setActiveAreaConfig(directory, options); const fixture = path.join(__dirname, 'fixtures', 'fengshu-er-road.osm'); fs.copyFileSync(fixture, path.join(directory, 'source.osm')); const frozen = roadRevisions.createCheckpoint(directory, 'Fixture baseline'); const snapshot = path.join(directory, 'active', 'area-config.snapshot.json'); const compileBase = { areaId: 'snapshot-test', osmFile: path.join(directory, 'source.osm'), outDir: path.join(directory, 'snapshot-output'), stagingDir: path.join(directory, 'snapshot-pipeline'), overridesFile: path.join(directory, 'native-road-overrides.json'), trafficSignalsFile: path.join(directory, 'native-traffic-signals.json'), options: { edgeLines: true, junctionTemplates: { enabled: false, references: [] } }, areaConfigSnapshotFile: snapshot, }; const fromSnapshot = compileInput(compileBase); assert.equal(fromSnapshot.area.nativeRoad.edgeLines, false, 'the snapshot wins over changed external options'); const fromRevision = compileInput({ ...compileBase, areaId: 'frozen-revision-test', outDir: path.join(directory, 'revision-output'), stagingDir: path.join(directory, 'revision-pipeline'), osmFile: path.join(directory, frozen.manifest.source.osmFile), areaConfigSnapshotFile: path.join(directory, 'revisions', frozen.manifest.id, 'area-config.snapshot.json'), }); assert.equal(fromRevision.area.nativeRoad.edgeLines, false, 'a frozen revision ignores later external config changes'); assert.throws( () => compileInput({ ...compileBase, areaConfigSnapshotFile: path.join(directory, 'missing.snapshot.json') }), /Area config snapshot is missing/, ); // The workbench compile is the one caller that passes editsFile. With no v2 // constraints the direct-edit solver stage must be an identity transform, so // both compiles have to publish byte-identical outputs. const activeEditsFile = path.join(directory, 'active', 'native-road-edits.json'); assert.ok(fs.existsSync(activeEditsFile)); assert.deepEqual(nativeRoadEdits.loadEditDocument(activeEditsFile).constraints, []); const identityBase = { ...compileBase, areaId: 'edits-identity-test' }; const withoutEdits = { ...identityBase, outDir: path.join(directory, 'no-edits-output'), stagingDir: path.join(directory, 'no-edits-pipeline'), }; const withEdits = { ...identityBase, outDir: path.join(directory, 'with-edits-output'), stagingDir: path.join(directory, 'with-edits-pipeline'), editsFile: activeEditsFile, }; compileInput(withoutEdits); compileInput(withEdits); assert.deepEqual( outputSnapshot(withEdits).files, outputSnapshot(withoutEdits).files, 'an empty v2 document must not change compiled output', ); assert.throws( () => compileInput({ ...identityBase, editsFile: '' }), /editsFile must be a non-empty string when present/, ); assert.throws( () => compileInput({ ...identityBase, editsFile: '' }), /editsFile must be a non-empty string when present/, ); fs.rmSync(directory, { recursive: true, force: true }); console.log('road revision storage tests passed');