1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-27 11:02:16 +01:00
unleash.unleash/src/test/e2e/stores/client-instance-store.e2e.test.ts
Thomas Heartman e125c0f072
fix(1-3928): prevent overwriting existing values in instance store (#10360)
Fixes a bug in the instance store where insert and bulkUpsert would
overwrite existing properties if there was a row there already. Now
it'll ignore any properties that are undefined.

The implementation is lifted directly from
`src/lib/db/client-applications-store.ts` (line 107 atm).

Additionally, I've renamed the `insert` method to `upsert` to make it
clearer what it does (and because we already have `bulkUpsert`). The
method seems to only be used in tests, anyway. I do not anticipate any
changes to be required in enterprise (I've checked).

## Discussion points:

This implementation uses `delete` to remove properties from the object.
Why didn't I do it some other way? Two main reasons:
1. We've had this implementation for 4 years in the client applications
store. If there were serious issues with it, we'd probably know by know.
(Probably.)
2. The only way I can think of without deleting, would be to use
`Object.fromEntries` and `Object.toEntries` and either map or reduce.
That'll double the amount of property iterations we'll need to do.

So naively, this strikes me as being more efficient. If you know better
solutions, I will of course be happy to take them. If not, I'd like to
leave this as is and then change it if we see that it's causing issues.
2025-07-16 12:10:15 +00:00

61 lines
1.9 KiB
TypeScript

import faker from 'faker';
import dbInit, { type ITestDb } from '../helpers/database-init.js';
import getLogger from '../../fixtures/no-logger.js';
import type {
IClientInstanceStore,
IUnleashStores,
} from '../../../lib/types/index.js';
import type { INewClientInstance } from '../../../lib/types/stores/client-instance-store.js';
let db: ITestDb;
let stores: IUnleashStores;
let clientInstanceStore: IClientInstanceStore;
beforeAll(async () => {
db = await dbInit('client_application_store_e2e_serial', getLogger);
stores = db.stores;
clientInstanceStore = stores.clientInstanceStore;
});
afterAll(async () => {
await db.destroy();
});
test('Upserting an application keeps values not provided intact', async () => {
const clientInstance: INewClientInstance = {
appName: faker.internet.domainName(),
instanceId: faker.datatype.uuid(),
environment: 'development',
sdkVersion: 'unleash-client-node:6.6.0',
sdkType: 'backend',
};
await clientInstanceStore.upsert(clientInstance);
const initial = await clientInstanceStore.get(clientInstance);
expect(initial).toMatchObject(clientInstance);
const update: INewClientInstance = {
appName: clientInstance.appName,
instanceId: clientInstance.instanceId,
environment: clientInstance.environment,
clientIp: '::2',
};
await clientInstanceStore.upsert(update);
const updated = await clientInstanceStore.get(clientInstance);
const expectedAfterUpdate = {
clientIp: '::2',
sdkVersion: 'unleash-client-node:6.6.0',
sdkType: 'backend',
};
expect(updated).toMatchObject(expectedAfterUpdate);
await clientInstanceStore.bulkUpsert([clientInstance]);
const doubleUpdated = await clientInstanceStore.get(clientInstance);
expect(doubleUpdated).toMatchObject(expectedAfterUpdate);
});