farmcontrol-ws/src/usersettings/usersettingsmanager.test.js
Tom Butcher 91c88b5cbb Enhance user settings management and schema
- Introduced a new `pageLayout` field in the user settings schema to allow for top-level layout configurations.
- Refactored the user settings manager to handle `pageLayout` separately from default settings, ensuring proper data structure during updates.
- Updated normalization functions to accommodate the new `pageLayout` field, enhancing the overall user settings management.
- Added tests to verify the correct handling of `pageLayout` updates, ensuring that it is written at the top level and not under defaults.
2026-08-25 01:59:14 +01:00

147 lines
3.6 KiB
JavaScript

import { jest } from '@jest/globals';
const findOne = jest.fn();
const findOneAndUpdate = jest.fn();
jest.unstable_mockModule(
'../database/schemas/misc/usersettings.schema.js',
() => ({
userSettingsModel: {
findOne,
findOneAndUpdate
}
})
);
const { UserSettingsManager, createEmptySettings, isSafeSettingsKey } =
await import('./usersettingsmanager.js');
const queryResult = value => ({
select: jest.fn().mockReturnValue({
lean: jest.fn().mockResolvedValue(value)
})
});
describe('UserSettingsManager', () => {
let manager;
beforeEach(() => {
jest.clearAllMocks();
manager = new UserSettingsManager({
authenticated: true,
user: { _id: 'user-id' }
});
});
it('returns empty defaults when the user has no settings document', async () => {
findOne.mockReturnValue(queryResult(null));
await expect(manager.getUserSettings()).resolves.toEqual(
createEmptySettings()
);
expect(findOne).toHaveBeenCalledWith({ user: 'user-id' });
});
it('atomically upserts only the requested category and key', async () => {
const defaults = {
...createEmptySettings(),
viewMode: { payments: 'cards' }
};
findOneAndUpdate.mockReturnValue(queryResult({ defaults }));
await expect(
manager.updateUserSettings({
category: 'viewMode',
key: 'payments',
value: 'cards'
})
).resolves.toEqual(defaults);
expect(findOneAndUpdate).toHaveBeenCalledWith(
{ user: 'user-id' },
expect.objectContaining({
$set: expect.objectContaining({
'defaults.viewMode.payments': 'cards'
}),
$setOnInsert: expect.objectContaining({ user: 'user-id' })
}),
{
new: true,
upsert: true,
setDefaultsOnInsert: true
}
);
});
it('writes pageLayout at the top level, not under defaults', async () => {
const pageLayout = {
InventoryOverview: {
sectionOrder: ['partStockStats'],
statsOrder: {}
}
};
findOneAndUpdate.mockReturnValue(
queryResult({
defaults: createEmptySettings(),
pageLayout
})
);
await expect(
manager.updateUserSettings({
category: 'pageLayout',
key: 'InventoryOverview',
value: pageLayout.InventoryOverview
})
).resolves.toEqual({
...createEmptySettings(),
pageLayout
});
expect(findOneAndUpdate).toHaveBeenCalledWith(
{ user: 'user-id' },
expect.objectContaining({
$set: expect.objectContaining({
'pageLayout.InventoryOverview': pageLayout.InventoryOverview
})
}),
expect.anything()
);
expect(findOneAndUpdate.mock.calls[0][1].$set).not.toHaveProperty(
'defaults.pageLayout.InventoryOverview'
);
});
it('rejects invalid categories and unsafe keys', async () => {
await expect(
manager.updateUserSettings({
category: 'unknown',
key: 'payments',
value: true
})
).rejects.toThrow('Invalid user settings category');
await expect(
manager.updateUserSettings({
category: 'viewMode',
key: 'payments.value',
value: true
})
).rejects.toThrow('Invalid user settings key');
expect(isSafeSettingsKey('$payments')).toBe(false);
expect(findOneAndUpdate).not.toHaveBeenCalled();
});
it('rejects unauthenticated access', async () => {
manager = new UserSettingsManager({
authenticated: false,
user: { _id: 'user-id' }
});
await expect(manager.getUserSettings()).rejects.toThrow(
'User is not authenticated'
);
});
});