Files
Multi-Hoster-Upload/tests/upload-audit.test.js
T
Sucukdeluxe d21b721de4 fix: keep internal audit logs in user data
Route upload audit and account rotation output through one internal userData log resolver with a contained fallback directory.

Report active writer paths to diagnostics and log reveal actions while preserving the configurable fileuploader.log fallback contract and leaving existing Desktop files untouched.

Cover primary paths, fallback containment, rotation, active path reporting, log opening, and upload log isolation with focused tests.
2026-08-26 12:27:13 +02:00

115 lines
5.4 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const {
createInternalLogPathResolver,
createInternalLogWriter,
createUploadAuditWriter,
getLogOpenDirectory
} = require('../lib/upload-audit');
function createTempDirectory(t, prefix) {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
return directory;
}
test('internal audit and account rotation paths share the userData logs directory', (t) => {
const directory = createTempDirectory(t, 'mhu-internal-log-paths-');
const userDataPath = path.join(directory, 'user-data');
const resolveInternalLogPath = createInternalLogPathResolver({ fs, path, userDataPath });
assert.equal(resolveInternalLogPath('upload-audit.log'), path.join(userDataPath, 'logs', 'upload-audit.log'));
assert.equal(resolveInternalLogPath('account-rotation.log'), path.join(userDataPath, 'logs', 'account-rotation.log'));
});
test('internal log paths fall back to another directory below userData without touching Desktop files', (t) => {
const directory = createTempDirectory(t, 'mhu-internal-log-fallback-');
const userDataPath = path.join(directory, 'user-data');
const desktopPath = path.join(directory, 'Desktop');
const desktopAuditPath = path.join(desktopPath, 'upload-audit.log');
const desktopRotationPath = path.join(desktopPath, 'account-rotation.log');
fs.mkdirSync(userDataPath, { recursive: true });
fs.mkdirSync(desktopPath, { recursive: true });
fs.writeFileSync(path.join(userDataPath, 'logs'), 'blocked');
fs.writeFileSync(desktopAuditPath, 'existing audit');
fs.writeFileSync(desktopRotationPath, 'existing rotation');
const resolveInternalLogPath = createInternalLogPathResolver({ fs, path, userDataPath });
assert.equal(resolveInternalLogPath('upload-audit.log'), path.join(userDataPath, 'internal-logs', 'upload-audit.log'));
assert.equal(resolveInternalLogPath('account-rotation.log'), path.join(userDataPath, 'internal-logs', 'account-rotation.log'));
assert.equal(fs.readFileSync(desktopAuditPath, 'utf8'), 'existing audit');
assert.equal(fs.readFileSync(desktopRotationPath, 'utf8'), 'existing rotation');
});
test('internal rotation writer retries inside userData and reports the file that accepted the write', async (t) => {
const directory = createTempDirectory(t, 'mhu-internal-log-writer-');
const userDataPath = path.join(directory, 'user-data');
const primaryPath = path.join(userDataPath, 'logs', 'account-rotation.log');
const fallbackPath = path.join(userDataPath, 'internal-logs', 'account-rotation.log');
const appendTargets = [];
const rotationTargets = [];
const testFs = {
mkdirSync: fs.mkdirSync,
promises: {
appendFile: async (targetPath, ...args) => {
appendTargets.push(targetPath);
if (targetPath === primaryPath) throw Object.assign(new Error('blocked primary'), { code: 'EACCES' });
return fs.promises.appendFile(targetPath, ...args);
}
}
};
const targets = [primaryPath, fallbackPath];
const resolveInternalLogPath = (_fileName, excludedPaths = new Set()) => targets.find(targetPath => !excludedPaths.has(targetPath)) || null;
const writer = createInternalLogWriter({
fs: testFs,
path,
fileName: 'account-rotation.log',
resolveInternalLogPath,
rotateLogFile: targetPath => rotationTargets.push(targetPath),
reportError: () => {},
retryDelays: [0, 0]
});
assert.equal(await writer.append('[rotation]\n', 'rot-log'), true);
assert.deepEqual(appendTargets, [primaryPath, fallbackPath]);
assert.deepEqual(rotationTargets, [primaryPath, fallbackPath]);
assert.equal(writer.getActivePath(), fallbackPath);
assert.equal(fs.readFileSync(fallbackPath, 'utf8'), '[rotation]\n');
});
test('upload audit writer leaves the configured fileuploader log contract unchanged', async (t) => {
const directory = createTempDirectory(t, 'mhu-upload-log-contract-');
const userDataPath = path.join(directory, 'user-data');
const customUploadDirectory = path.join(directory, 'custom-upload-logs');
const uploadLogPath = path.join(customUploadDirectory, '13-08-2026-mdu-session-14-20-123456.log');
fs.mkdirSync(customUploadDirectory, { recursive: true });
fs.writeFileSync(uploadLogPath, 'existing upload entry\n');
const resolveInternalLogPath = () => path.join(userDataPath, 'logs', 'upload-audit.log');
const writer = createUploadAuditWriter({
fs,
path,
resolveInternalLogPath,
rotateLogFile: () => {},
reportError: () => {},
retryDelays: [0]
});
assert.equal(await writer.append('# UPLOAD-PLAN {"plannedUploadCount":4}\r\n', 'upload-plan'), true);
assert.equal(writer.getActivePath(), path.join(userDataPath, 'logs', 'upload-audit.log'));
assert.equal(fs.readFileSync(uploadLogPath, 'utf8'), 'existing upload entry\n');
assert.equal(fs.readFileSync(writer.getActivePath(), 'utf8'), '# UPLOAD-PLAN {"plannedUploadCount":4}\r\n');
});
test('log opening uses the reported internal file directory before the general fallback directory', () => {
assert.equal(
getLogOpenDirectory('C:\\AppData\\Multi-Hoster\\logs\\upload-audit.log', 'C:\\Program Files\\Multi-Hoster', path.win32),
'C:\\AppData\\Multi-Hoster\\logs'
);
assert.equal(getLogOpenDirectory(null, 'C:\\Program Files\\Multi-Hoster', path.win32), 'C:\\Program Files\\Multi-Hoster');
});