216 lines
7.9 KiB
JavaScript
216 lines
7.9 KiB
JavaScript
/**
|
||
* assignMarkerId.test.js
|
||
* ======================
|
||
* Integration-Test für server/editRobot.js → assignMarkerId().
|
||
*
|
||
* Testet insbesondere den Guard für fehlende position_mm (Marker ohne
|
||
* triangulierte Position, z.B. Einzelkamera-Marker nach Schritt 5).
|
||
*
|
||
* Technisch: editRobot.js ist ein ES-Modul — es wird über den dünnen Runner
|
||
* test/fixtures/runAssignMarkerId.mjs per spawnSync aufgerufen (gleiche
|
||
* Strategie wie yAxisRotation.test.js für das Python-Skript).
|
||
* Datei-I/O läuft gegen echte Temp-Dateien (os.tmpdir()).
|
||
*/
|
||
|
||
const { spawnSync } = require('child_process');
|
||
const os = require('os');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const RUNNER = path.join(__dirname, 'fixtures', 'runAssignMarkerId.mjs');
|
||
|
||
// ── Hilfsfunktionen ───────────────────────────────────────────────────────────
|
||
|
||
function callAssignMarkerId(robotPath, params) {
|
||
const proc = spawnSync('node', [RUNNER, robotPath, JSON.stringify(params)], {
|
||
encoding: 'utf-8',
|
||
});
|
||
if (proc.error) throw proc.error;
|
||
const stdout = (proc.stdout ?? '').trim();
|
||
if (!stdout) throw new Error(`Kein Output.\nstderr: ${proc.stderr}`);
|
||
return JSON.parse(stdout);
|
||
}
|
||
|
||
function makeTempRobot(content) {
|
||
const p = path.join(
|
||
os.tmpdir(),
|
||
`robot_test_${Date.now()}_${Math.random().toString(36).slice(2)}.json`
|
||
);
|
||
fs.writeFileSync(p, JSON.stringify(content, null, 2), 'utf8');
|
||
return p;
|
||
}
|
||
|
||
const ROBOT_WITH_42 = () => ({
|
||
links: {
|
||
Arm1: { markers: [{ id: 42, set: 'A0', position: [100, 200, 300] }] },
|
||
},
|
||
});
|
||
|
||
const ROBOT_EMPTY = () => ({
|
||
links: { Arm1: { markers: [] } },
|
||
});
|
||
|
||
// ── Eingabe-Validierung ───────────────────────────────────────────────────────
|
||
|
||
describe('assignMarkerId – Eingabe-Validierung', () => {
|
||
test('ungültige Marker-ID → changed: false', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, { markerId: -1, link: 'Arm1' });
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/Ungültige Marker-ID/);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('kein link für neuen Marker → changed: false', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 99,
|
||
extraMarkers: [{ marker_id: 99, position_mm: [10, 20, 30] }],
|
||
});
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/Link/);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
});
|
||
|
||
// ── Guard: fehlende position_mm ───────────────────────────────────────────────
|
||
|
||
describe('assignMarkerId – Guard: fehlende position_mm (z.B. Einzelkamera-Marker)', () => {
|
||
test('extraMarker ohne position_mm → changed: false, kein Crash', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 55,
|
||
link: 'Arm1',
|
||
extraMarkers: [{ marker_id: 55 }], // kein position_mm
|
||
});
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/position_mm fehlt/);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('extraMarker mit position_mm: null → changed: false, kein Crash', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 56,
|
||
link: 'Arm1',
|
||
extraMarkers: [{ marker_id: 56, position_mm: null }],
|
||
});
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/position_mm fehlt/);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('extraMarker mit position_mm als String → changed: false, kein Crash', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 57,
|
||
link: 'Arm1',
|
||
extraMarkers: [{ marker_id: 57, position_mm: '[1,2,3]' }],
|
||
});
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/position_mm fehlt/);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
});
|
||
|
||
// ── Normalfall: Marker hinzufügen ─────────────────────────────────────────────
|
||
|
||
describe('assignMarkerId – Normalfall: neuen Marker hinzufügen', () => {
|
||
test('gültige position_mm → changed: true, action: added, Datei geschrieben', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 77,
|
||
link: 'Arm1',
|
||
extraMarkers: [{ marker_id: 77, position_mm: [10.1, 20.22, 30.333] }],
|
||
});
|
||
expect(r.changed).toBe(true);
|
||
expect(r.change.action).toBe('added');
|
||
expect(r.change.markerId).toBe(77);
|
||
expect(r.change.newLink).toBe('Arm1');
|
||
|
||
const saved = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||
const added = saved.links.Arm1.markers.find(m => m.id === 77);
|
||
expect(added).toBeDefined();
|
||
expect(Array.isArray(added.position)).toBe(true);
|
||
expect(added.position).toHaveLength(3);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('Marker nicht in extraMarkers → changed: false', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, { markerId: 99, link: 'Arm1', extraMarkers: [] });
|
||
expect(r.changed).toBe(false);
|
||
expect(r.error).toMatch(/nicht.*vorhanden/i);
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('mit set → Marker hat set-Wert in der gespeicherten Datei', () => {
|
||
const p = makeTempRobot(ROBOT_EMPTY());
|
||
try {
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 78,
|
||
link: 'Arm1',
|
||
set: 'A0',
|
||
extraMarkers: [{ marker_id: 78, position_mm: [1, 2, 3] }],
|
||
});
|
||
expect(r.changed).toBe(true);
|
||
const saved = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||
const added = saved.links.Arm1.markers.find(m => m.id === 78);
|
||
expect(added.set).toBe('A0');
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
});
|
||
|
||
// ── Normalfall: bestehenden Marker aktualisieren ──────────────────────────────
|
||
|
||
describe('assignMarkerId – Normalfall: bestehenden Marker aktualisieren', () => {
|
||
test('Set ändern → changed: true, action: updated, Datei geändert', () => {
|
||
const p = makeTempRobot(ROBOT_WITH_42());
|
||
try {
|
||
const r = callAssignMarkerId(p, { markerId: 42, set: 'B0' });
|
||
expect(r.changed).toBe(true);
|
||
expect(r.change.action).toBe('updated');
|
||
expect(r.change.oldSet).toBe('A0');
|
||
expect(r.change.newSet).toBe('B0');
|
||
|
||
const saved = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||
expect(saved.links.Arm1.markers.find(m => m.id === 42).set).toBe('B0');
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('in anderen Link verschieben → oldLink / newLink korrekt, Datei geändert', () => {
|
||
const p = makeTempRobot(ROBOT_WITH_42());
|
||
try {
|
||
const r = callAssignMarkerId(p, { markerId: 42, link: 'Arm2' });
|
||
expect(r.changed).toBe(true);
|
||
expect(r.change.oldLink).toBe('Arm1');
|
||
expect(r.change.newLink).toBe('Arm2');
|
||
|
||
const saved = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||
expect(saved.links.Arm1.markers.find(m => m.id === 42)).toBeUndefined();
|
||
expect(saved.links.Arm2.markers.find(m => m.id === 42)).toBeDefined();
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
|
||
test('bestehender Marker: fehlende position_mm in extraMarkers ist irrelevant', () => {
|
||
const p = makeTempRobot(ROBOT_WITH_42());
|
||
try {
|
||
// Marker 42 ist in robot.json → position_mm-Guard darf nicht zuschlagen
|
||
const r = callAssignMarkerId(p, {
|
||
markerId: 42,
|
||
set: 'C0',
|
||
extraMarkers: [{ marker_id: 42 }], // kein position_mm – aber irrelevant
|
||
});
|
||
expect(r.changed).toBe(true);
|
||
expect(r.change.action).toBe('updated');
|
||
} finally { fs.unlinkSync(p); }
|
||
});
|
||
});
|