mirror of
https://github.com/got-feedBack/feedBack-desktop.git
synced 2026-07-22 04:41:26 +00:00
The backend has no built-in library default — _get_dlc_dir() returns None when DLC_DIR is unset and config.json is unusable — so deleting DLC_DIR on invalid-config/write-failed stranded the user with an empty library and only a console warn (corrupt config.json, or a saved dlc_dir pointing at an unplugged drive, previously still produced a working library via the env fallback). invalid-config and write-failed now export the resolved fallback as DLC_DIR, restoring pre-#117 behaviour exactly and only where config.json cannot own the path. Config-owned dynamic refresh is unchanged for the configured/bootstrapped paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
217 lines
8.3 KiB
JavaScript
217 lines
8.3 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { loadTs } = require('./_load-ts');
|
|
|
|
const {
|
|
applyLibraryPathToPythonEnvironment,
|
|
normalizeExistingLibraryDirectory,
|
|
prepareLibraryPathForPython,
|
|
} = loadTs('src/main/library-path-config.ts');
|
|
|
|
function tmpConfigDir() {
|
|
return fs.mkdtempSync(path.join(os.tmpdir(), 'feedback-library-path-'));
|
|
}
|
|
|
|
test('normal desktop startup bootstraps the fallback into config instead of DLC_DIR', () => {
|
|
const configDir = tmpConfigDir();
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Music\\fee[dB]ack');
|
|
|
|
assert.deepEqual(result, { status: 'bootstrapped' });
|
|
assert.deepEqual(
|
|
JSON.parse(fs.readFileSync(path.join(configDir, 'config.json'), 'utf8')),
|
|
{ dlc_dir: 'C:\\Music\\fee[dB]ack' },
|
|
);
|
|
assert.equal(result.environmentDlcDir, undefined);
|
|
});
|
|
|
|
test('bootstrap merges the fallback into an existing config without losing settings', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
fs.writeFileSync(configFile, JSON.stringify({ master_difficulty: 75 }));
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'D:\\Songs');
|
|
|
|
assert.equal(result.status, 'bootstrapped');
|
|
assert.deepEqual(
|
|
JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
|
{ master_difficulty: 75, dlc_dir: 'D:\\Songs' },
|
|
);
|
|
});
|
|
|
|
test('an existing saved library stays config-owned and can change between scans', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
const savedSongs = path.join(configDir, 'Saved Songs');
|
|
fs.mkdirSync(savedSongs);
|
|
fs.writeFileSync(configFile, JSON.stringify({ dlc_dir: savedSongs }));
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.deepEqual(result, { status: 'configured' });
|
|
assert.deepEqual(
|
|
JSON.parse(fs.readFileSync(configFile, 'utf8')),
|
|
{ dlc_dir: savedSongs },
|
|
);
|
|
assert.equal(result.environmentDlcDir, undefined);
|
|
});
|
|
|
|
test('a missing saved library is reported without overwriting the intended path', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
const missingSongs = path.join(configDir, 'Disconnected Songs');
|
|
fs.writeFileSync(configFile, JSON.stringify({ dlc_dir: missingSongs }));
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'invalid-config');
|
|
assert.match(result.error, /not an existing directory/);
|
|
assert.deepEqual(JSON.parse(fs.readFileSync(configFile, 'utf8')), { dlc_dir: missingSongs });
|
|
});
|
|
|
|
test('a saved library file is rejected without overwriting the configured value', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
const file = path.join(configDir, 'not-a-library');
|
|
fs.writeFileSync(file, 'x');
|
|
fs.writeFileSync(configFile, JSON.stringify({ dlc_dir: file }));
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'invalid-config');
|
|
assert.match(result.error, /not an existing directory/);
|
|
assert.deepEqual(JSON.parse(fs.readFileSync(configFile, 'utf8')), { dlc_dir: file });
|
|
});
|
|
|
|
test('an explicit valid DLC_DIR remains an environment override', () => {
|
|
const configDir = tmpConfigDir();
|
|
const managedSongs = path.join(configDir, 'Managed Songs');
|
|
fs.mkdirSync(managedSongs);
|
|
const result = prepareLibraryPathForPython(
|
|
configDir,
|
|
'C:\\Default Songs',
|
|
` ${managedSongs} `,
|
|
);
|
|
|
|
assert.deepEqual(result, {
|
|
status: 'explicit-override',
|
|
environmentDlcDir: managedSongs,
|
|
});
|
|
assert.equal(fs.existsSync(path.join(configDir, 'config.json')), false);
|
|
});
|
|
|
|
test('an explicit DLC_DIR rejects whitespace, files, and missing paths', () => {
|
|
const root = tmpConfigDir();
|
|
const file = path.join(root, 'not-a-directory');
|
|
fs.writeFileSync(file, 'x');
|
|
|
|
assert.equal(normalizeExistingLibraryDirectory(' '), undefined);
|
|
assert.equal(normalizeExistingLibraryDirectory(file), undefined);
|
|
assert.equal(normalizeExistingLibraryDirectory(path.join(root, 'missing')), undefined);
|
|
});
|
|
|
|
test('an explicit DLC_DIR is trimmed before directory validation', () => {
|
|
const root = tmpConfigDir();
|
|
const directory = path.join(root, 'Managed Songs');
|
|
fs.mkdirSync(directory);
|
|
|
|
assert.equal(normalizeExistingLibraryDirectory(` ${directory} `), directory);
|
|
});
|
|
|
|
test('a corrupt config is never overwritten during bootstrap', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
fs.writeFileSync(configFile, '{broken');
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'invalid-config');
|
|
assert.match(result.error, /JSON/);
|
|
assert.equal(fs.readFileSync(configFile, 'utf8'), '{broken');
|
|
});
|
|
|
|
test('invalid-config exports the resolved fallback so the backend keeps a library', () => {
|
|
// The backend has no built-in default (_get_dlc_dir returns None): with
|
|
// the env var deleted AND config.json unusable, scans and starter
|
|
// seeding would silently skip. Failure statuses must keep the pre-#117
|
|
// env fallback.
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
fs.writeFileSync(configFile, JSON.stringify({ dlc_dir: path.join(configDir, 'gone') }));
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'invalid-config');
|
|
assert.equal(result.environmentDlcDir, 'C:\\Default Songs');
|
|
|
|
const environment = {};
|
|
applyLibraryPathToPythonEnvironment(environment, result);
|
|
assert.deepEqual(environment, { DLC_DIR: 'C:\\Default Songs' });
|
|
});
|
|
|
|
test('corrupt config exports the resolved fallback without being overwritten', () => {
|
|
const configDir = tmpConfigDir();
|
|
const configFile = path.join(configDir, 'config.json');
|
|
fs.writeFileSync(configFile, '{broken');
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'invalid-config');
|
|
assert.equal(result.environmentDlcDir, 'C:\\Default Songs');
|
|
assert.equal(fs.readFileSync(configFile, 'utf8'), '{broken');
|
|
});
|
|
|
|
test('write-failed exports the resolved fallback so the backend keeps a library', () => {
|
|
// Force the config write to fail by making config.json's parent a file.
|
|
const root = tmpConfigDir();
|
|
const configDir = path.join(root, 'not-a-dir');
|
|
fs.writeFileSync(configDir, 'x');
|
|
|
|
const result = prepareLibraryPathForPython(configDir, 'C:\\Default Songs');
|
|
|
|
assert.equal(result.status, 'write-failed');
|
|
assert.equal(result.environmentDlcDir, 'C:\\Default Songs');
|
|
});
|
|
|
|
test('Python environment omits DLC_DIR when config owns the library path', () => {
|
|
const environment = { DLC_DIR: 'C:\\Stale Parent Value', KEEP: 'yes' };
|
|
|
|
applyLibraryPathToPythonEnvironment(environment, { status: 'configured' });
|
|
|
|
assert.deepEqual(environment, { KEEP: 'yes' });
|
|
});
|
|
|
|
test('Python environment exports only a validated explicit DLC_DIR', () => {
|
|
const configDir = tmpConfigDir();
|
|
const managedSongs = path.join(configDir, 'Managed Songs');
|
|
fs.mkdirSync(managedSongs);
|
|
const preparation = prepareLibraryPathForPython(
|
|
configDir,
|
|
'C:\\Default Songs',
|
|
` ${managedSongs} `,
|
|
);
|
|
const environment = { DLC_DIR: 'C:\\Stale Parent Value', KEEP: 'yes' };
|
|
|
|
applyLibraryPathToPythonEnvironment(environment, preparation);
|
|
|
|
assert.deepEqual(environment, { DLC_DIR: managedSongs, KEEP: 'yes' });
|
|
});
|
|
|
|
test('Python environment removes an invalid explicit DLC_DIR', () => {
|
|
const configDir = tmpConfigDir();
|
|
const fallback = path.join(configDir, 'Fallback Songs');
|
|
fs.mkdirSync(fallback);
|
|
const file = path.join(configDir, 'not-a-directory');
|
|
fs.writeFileSync(file, 'x');
|
|
const preparation = prepareLibraryPathForPython(configDir, fallback, file);
|
|
const environment = { DLC_DIR: file };
|
|
|
|
applyLibraryPathToPythonEnvironment(environment, preparation);
|
|
|
|
assert.deepEqual(environment, {});
|
|
});
|