mirror of
https://github.com/unraid/api.git
synced 2025-12-30 21:19:49 -06:00
fix: enhance getKeyFile function to handle missing key file gracefully (#1659)
- Updated the getKeyFile function to catch ENOENT errors when the specified key file does not exist, returning an empty string instead of throwing an error. - Added new tests to verify the behavior of getKeyFile when the key file is missing and when it exists, ensuring robust error handling and correct functionality. These changes improve the reliability of the key file retrieval process in the application. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - None - Bug Fixes - Prevents errors when the key file is missing by returning an empty value instead of failing, while preserving existing behaviors in other states. - Tests - Refactored tests to use a mocked filesystem with better isolation. - Added scenarios for missing key files and correctly decoded keys. - Improved assertions for clearer, deterministic outcomes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { expect, test, vi } from 'vitest';
|
||||
|
||||
import { store } from '@app/store/index.js';
|
||||
import { FileLoadStatus, StateFileKey } from '@app/store/types.js';
|
||||
|
||||
import '@app/core/utils/misc/get-key-file.js';
|
||||
import '@app/store/modules/emhttp.js';
|
||||
|
||||
vi.mock('fs/promises');
|
||||
|
||||
test('Before loading key returns null', async () => {
|
||||
const { getKeyFile } = await import('@app/core/utils/misc/get-key-file.js');
|
||||
const { status } = store.getState().registration;
|
||||
@@ -48,21 +49,70 @@ test('Returns empty key if key location is empty', async () => {
|
||||
await expect(getKeyFile()).resolves.toBe('');
|
||||
});
|
||||
|
||||
test(
|
||||
'Returns decoded key file if key location exists',
|
||||
async () => {
|
||||
const { getKeyFile } = await import('@app/core/utils/misc/get-key-file.js');
|
||||
const { loadStateFiles } = await import('@app/store/modules/emhttp.js');
|
||||
const { loadRegistrationKey } = await import('@app/store/modules/registration.js');
|
||||
// Load state files into store
|
||||
await store.dispatch(loadStateFiles());
|
||||
await store.dispatch(loadRegistrationKey());
|
||||
// Check if store has state files loaded
|
||||
const { status } = store.getState().registration;
|
||||
expect(status).toBe(FileLoadStatus.LOADED);
|
||||
await expect(getKeyFile()).resolves.toMatchInlineSnapshot(
|
||||
'"hVs1tLjvC9FiiQsIwIQ7G1KszAcexf0IneThhnmf22SB0dGs5WzRkqMiSMmt2DtR5HOXFUD32YyxuzGeUXmky3zKpSu6xhZNKVg5atGM1OfvkzHBMldI3SeBLuUFSgejLbpNUMdTrbk64JJdbzle4O8wiQgkIpAMIGxeYLwLBD4zHBcfyzq40QnxG--HcX6j25eE0xqa2zWj-j0b0rCAXahJV2a3ySCbPzr1MvfPRTVb0rr7KJ-25R592hYrz4H7Sc1B3p0lr6QUxHE6o7bcYrWKDRtIVoZ8SMPpd1_0gzYIcl5GsDFzFumTXUh8NEnl0Q8hwW1YE-tRc6Y_rrvd7w"'
|
||||
);
|
||||
},
|
||||
{ timeout: 10000 }
|
||||
);
|
||||
test('Returns empty string when key file does not exist (ENOENT)', async () => {
|
||||
const { readFile } = await import('fs/promises');
|
||||
|
||||
// Mock readFile to throw ENOENT error
|
||||
const readFileMock = vi.mocked(readFile);
|
||||
readFileMock.mockRejectedValueOnce(
|
||||
Object.assign(new Error('ENOENT: no such file or directory'), { code: 'ENOENT' })
|
||||
);
|
||||
|
||||
// Clear the module cache and re-import to get fresh module with mock
|
||||
vi.resetModules();
|
||||
const { getKeyFile } = await import('@app/core/utils/misc/get-key-file.js');
|
||||
const { updateEmhttpState } = await import('@app/store/modules/emhttp.js');
|
||||
const { store: freshStore } = await import('@app/store/index.js');
|
||||
|
||||
// Set key file location to a non-existent file
|
||||
freshStore.dispatch(
|
||||
updateEmhttpState({
|
||||
field: StateFileKey.var,
|
||||
state: {
|
||||
regFile: '/boot/config/Pro.key',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Should return empty string when file doesn't exist
|
||||
await expect(getKeyFile()).resolves.toBe('');
|
||||
|
||||
// Clear mock
|
||||
readFileMock.mockReset();
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
test('Returns decoded key file if key location exists', async () => {
|
||||
const { readFile } = await import('fs/promises');
|
||||
|
||||
// Mock a valid key file content
|
||||
const mockKeyContent =
|
||||
'hVs1tLjvC9FiiQsIwIQ7G1KszAcexf0IneThhnmf22SB0dGs5WzRkqMiSMmt2DtR5HOXFUD32YyxuzGeUXmky3zKpSu6xhZNKVg5atGM1OfvkzHBMldI3SeBLuUFSgejLbpNUMdTrbk64JJdbzle4O8wiQgkIpAMIGxeYLwLBD4zHBcfyzq40QnxG--HcX6j25eE0xqa2zWj-j0b0rCAXahJV2a3ySCbPzr1MvfPRTVb0rr7KJ-25R592hYrz4H7Sc1B3p0lr6QUxHE6o7bcYrWKDRtIVoZ8SMPpd1_0gzYIcl5GsDFzFumTXUh8NEnl0Q8hwW1YE-tRc6Y_rrvd7w==';
|
||||
const binaryContent = Buffer.from(mockKeyContent, 'base64').toString('binary');
|
||||
|
||||
const readFileMock = vi.mocked(readFile);
|
||||
readFileMock.mockResolvedValue(binaryContent);
|
||||
|
||||
// Clear the module cache and re-import to get fresh module with mock
|
||||
vi.resetModules();
|
||||
const { getKeyFile } = await import('@app/core/utils/misc/get-key-file.js');
|
||||
const { loadStateFiles } = await import('@app/store/modules/emhttp.js');
|
||||
const { loadRegistrationKey } = await import('@app/store/modules/registration.js');
|
||||
const { store: freshStore } = await import('@app/store/index.js');
|
||||
|
||||
// Load state files into store
|
||||
await freshStore.dispatch(loadStateFiles());
|
||||
await freshStore.dispatch(loadRegistrationKey());
|
||||
// Check if store has state files loaded
|
||||
const { status } = freshStore.getState().registration;
|
||||
expect(status).toBe(FileLoadStatus.LOADED);
|
||||
|
||||
const result = await getKeyFile();
|
||||
expect(result).toBe(
|
||||
'hVs1tLjvC9FiiQsIwIQ7G1KszAcexf0IneThhnmf22SB0dGs5WzRkqMiSMmt2DtR5HOXFUD32YyxuzGeUXmky3zKpSu6xhZNKVg5atGM1OfvkzHBMldI3SeBLuUFSgejLbpNUMdTrbk64JJdbzle4O8wiQgkIpAMIGxeYLwLBD4zHBcfyzq40QnxG--HcX6j25eE0xqa2zWj-j0b0rCAXahJV2a3ySCbPzr1MvfPRTVb0rr7KJ-25R592hYrz4H7Sc1B3p0lr6QUxHE6o7bcYrWKDRtIVoZ8SMPpd1_0gzYIcl5GsDFzFumTXUh8NEnl0Q8hwW1YE-tRc6Y_rrvd7w'
|
||||
);
|
||||
|
||||
// Clear mock
|
||||
readFileMock.mockReset();
|
||||
vi.resetModules();
|
||||
}, 10000);
|
||||
|
||||
@@ -16,11 +16,22 @@ export const getKeyFile = async function (appStore: RootState = store.getState()
|
||||
|
||||
const keyFileName = basename(emhttp.var?.regFile);
|
||||
const registrationKeyFilePath = join(paths['keyfile-base'], keyFileName);
|
||||
const keyFile = await readFile(registrationKeyFilePath, 'binary');
|
||||
return Buffer.from(keyFile, 'binary')
|
||||
.toString('base64')
|
||||
.trim()
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
|
||||
try {
|
||||
const keyFile = await readFile(registrationKeyFilePath, 'binary');
|
||||
return Buffer.from(keyFile, 'binary')
|
||||
.toString('base64')
|
||||
.trim()
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
} catch (error) {
|
||||
// Handle ENOENT error when Pro.key file doesn't exist
|
||||
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
||||
// Return empty string when key file is missing (ENOKEYFILE state)
|
||||
return '';
|
||||
}
|
||||
// Re-throw other errors
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user