Files

135 lines
5.1 KiB
JavaScript
Raw Permalink Normal View History

import { describe, test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { CondensedGraph } from '../../src/core/CondensedGraph.js';
import { ShardedSnapshotBuilder } from '../../src/core/shards/ShardedSnapshotBuilder.js';
import { ShardedSnapshot } from '../../src/core/shards/ShardedSnapshot.js';
import { FileShardStorage } from '../../src/core/shards/FileShardStorage.js';
import { DeltaShardBinary } from '../../src/core/shards/DeltaShardBinary.js';
function buildSnapshot(bucketSize = 4) {
const graph = new CondensedGraph();
const nodes = [];
for (let i = 0; i < 6; i++) {
nodes.push(graph._ensureNode(`node:${i}`));
}
graph.addEdge(nodes[0], 'owner', nodes[1]);
graph.addEdge(nodes[0], 'owner', nodes[2]);
graph.addEdge(nodes[3], 'owner', nodes[4]);
graph.addEdge(nodes[5], 'owner', nodes[0]);
graph.finalizePerfectHash();
graph.finalizeWaveletAdjacency({ dropAdjacencyList: true });
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sharded-delta-layer-'));
const builder = new ShardedSnapshotBuilder({ bucketSize, includeDirections: ['out', 'in'] });
const manifest = builder.build(graph, dir);
const storage = new FileShardStorage(dir);
const snapshot = new ShardedSnapshot(manifest, storage, { cacheLimit: 8 });
snapshot.initializeSync();
return { snapshot, dir, nodes, manifest };
}
function writeDeltaLayer(snapshot, dir, entries, mode = 'override') {
fs.mkdirSync(dir, { recursive: true });
const merged = new Map();
for (const entry of entries) {
const shardMeta = snapshot._selectShardMeta(entry.relationId, entry.direction, entry.srcId);
assert.ok(shardMeta, 'Missing shard meta for delta entry');
const localSource = snapshot._localSource(entry.srcId, shardMeta);
const key = shardMeta.cacheKey;
let bucket = merged.get(key);
if (!bucket) {
bucket = { shardMeta, additions: [], removals: [] };
merged.set(key, bucket);
}
for (const add of entry.additions) {
bucket.additions.push({
srcLocal: localSource,
otherId: add.dstId,
possBits: add.possBits,
relBits: add.relBits
});
}
for (const rem of entry.removals) {
bucket.removals.push({
srcLocal: localSource,
otherId: rem.dstId
});
}
}
const shards = [];
for (const bucket of merged.values()) {
const shardMeta = bucket.shardMeta;
const buffer = DeltaShardBinary.serialize({
relationId: shardMeta.relationId,
direction: shardMeta.direction,
rangeStart: shardMeta.rangeStart,
rangeEnd: shardMeta.rangeEnd,
nodeCount: snapshot.nodeCount,
additions: bucket.additions,
removals: bucket.removals
});
const shardKey = `delta-${shardMeta.key}`;
fs.writeFileSync(path.join(dir, shardKey), new Uint8Array(buffer));
shards.push({
key: shardKey,
relationId: shardMeta.relationId,
direction: shardMeta.direction,
rangeStart: shardMeta.rangeStart,
rangeEnd: shardMeta.rangeEnd,
cacheKey: shardMeta.cacheKey
});
}
return { shards, storage: new FileShardStorage(dir), mode };
}
// ADR-003: sharded snapshots are stubs in src/core/shards/ — tests are the spec for when the subsystem is implemented.
describe.skip('Sharded delta layering semantics', () => {
test('later override layers win over earlier layers', () => {
const { snapshot, dir, nodes } = buildSnapshot(4);
const relId = snapshot.relationIdToName.indexOf('owner');
const layer1 = writeDeltaLayer(snapshot, path.join(dir, 'l1'), [
{ relationId: relId, direction: 'out', srcId: nodes[0], additions: [{ dstId: nodes[3], possBits: 65535, relBits: 65535 }], removals: [] }
], 'override');
const layer2 = writeDeltaLayer(snapshot, path.join(dir, 'l2'), [
{ relationId: relId, direction: 'out', srcId: nodes[0], additions: [], removals: [{ dstId: nodes[3] }] }
], 'override');
snapshot.setDeltaLayers([layer1, layer2]);
const edge = snapshot.findEdgeSync(nodes[0], relId, nodes[3]);
assert.equal(edge, null);
fs.rmSync(dir, { recursive: true, force: true });
});
test('union overlays do not override writer removals', () => {
const { snapshot, dir, nodes } = buildSnapshot(4);
const relId = snapshot.relationIdToName.indexOf('owner');
const writerLayer = writeDeltaLayer(snapshot, path.join(dir, 'writer'), [
{ relationId: relId, direction: 'out', srcId: nodes[0], additions: [], removals: [{ dstId: nodes[1] }] }
], 'override');
const unionLayer = writeDeltaLayer(snapshot, path.join(dir, 'union'), [
{ relationId: relId, direction: 'out', srcId: nodes[0], additions: [], removals: [{ dstId: nodes[2] }] }
], 'union');
snapshot.setDeltaLayers([writerLayer, unionLayer]);
const removedByWriter = snapshot.findEdgeSync(nodes[0], relId, nodes[1]);
assert.equal(removedByWriter, null);
const unionRemovalIgnored = snapshot.findEdgeSync(nodes[0], relId, nodes[2]);
assert.ok(unionRemovalIgnored, 'Union overlay must not remove base access');
fs.rmSync(dir, { recursive: true, force: true });
});
});