'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const model = require('../../renderer/modules/protection-model'); const available = (id, deviceId, extra = {}) => model.normalizeCopy( { game_id: 'PPSA00001', name: 'Policy Test' }, { id, deviceId, volumeId: `volume-${id}`, relativePath: `copy-${id}`, availabilityState: 'available', integrityState: 'verified', ...extra }, id ); test('protection policy matrix requires healthy copies on distinct devices', () => { const matrix = [ { copies: [], status: 'unprotected', healthy: false }, { copies: [available(1, 'A')], status: 'needs-diversity', healthy: false }, { copies: [available(1, 'A'), available(2, 'B')], status: 'healthy', healthy: true }, { copies: [available(1, 'A'), available(2, 'B', { integrityState: 'failed' })], status: 'needs-diversity', healthy: false } ]; matrix.forEach(entry => { const result = model.evaluate(entry.copies, 2); assert.equal(result.status, entry.status); assert.equal(result.healthy, entry.healthy); }); }); test('two copies on the same device do not satisfy diversity and are not called healthy duplicates', () => { const result = model.evaluate([available(1, 'A'), available(2, 'A')], 2); assert.equal(result.copyCount, 2); assert.equal(result.distinctDeviceCount, 1); assert.equal(result.sameDeviceExcess, 1); assert.equal(result.status, 'needs-diversity'); assert.equal(result.healthy, false); }); test('missing copy remains visible and exact restore action targets its concrete copy id', () => { const rows = model.flatten([{ game_id: 'PPSA00001', name: 'Policy Test', localCopies: [ { id: 41, deviceId: 'A', volumeId: 'primary', relativePath: 'PPSA00001', availabilityState: 'available', integrityState: 'verified' }, { id: 42, deviceId: 'B', volumeId: 'usb', relativePath: 'PPSA00001', availabilityState: 'unavailable', integrityState: 'unknown' } ] }]); const renderer = fs.readFileSync(path.join(__dirname, '..', '..', 'renderer', 'modules', 'backups.js'), 'utf8'); assert.equal(rows.length, 2); assert.equal(rows[1].missing, true); assert.equal(rows[1].policy.missingCopyCount, 1); assert.equal(rows[1].restoreTarget.copyId, '42'); assert.match(renderer, /data-restore-copy-id=/); assert.match(renderer, /state\.protectionRestoreTarget = copy\.restoreTarget/); assert.match(renderer, /No restore has started/); });