Files
upstream-rocketchat/packages/models/src/updater.spec.ts
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

192 lines
4.7 KiB
TypeScript

import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import { UpdaterImpl } from './updater';
test('updater typings', () => {
const updater = new UpdaterImpl<{
_id: string;
t: 'l';
a: {
b: string;
};
c?: number;
d?: {
e: string;
};
e: string[];
}>();
// @ts-expect-error: it should not allow any string to `t` only `l` is allowed
updater.set('t', 'a');
// `l` is allowed
updater.set('t', 'l');
// `a` is { b: string }
updater.set('a', { b: 'test' });
updater.set('a.b', 'test');
// @ts-expect-error: it should not allow strings to `a`, a is an object containing `b: string`
updater.set('a', 'b');
// @ts-expect-error: `a` is not optional so unset is not allowed
updater.unset('a');
// @ts-expect-error: strings cannot be incremented
updater.inc('a', 1);
// `c` is number but it should be optional, so unset is allowed
updater.unset('c');
updater.set('c', 1);
// @ts-expect-error: `c` is a number
updater.set('c', 'b');
// inc is allowed for numbers
updater.inc('c', 1);
// `d` is { e: string } but it should be optional, so unset is allowed
updater.unset('d');
updater.set('d', { e: 'a' });
// @ts-expect-error: `d` is an object
updater.set('d', 'a');
// @ts-expect-error: it should not allow numbers, since e is a string
updater.addToSet('e', 1);
// @ts-expect-error: it should not allow strings, since a is an object
updater.addToSet('a', 'b');
updater.addToSet('e', 'a');
// @ts-expect-error: it should not allow `njame` its not specified in the model
updater.set('njame', 1);
// `d` is { e: string } and also it should be optional, so unset is allowed
updater.unset('d.e');
// @ts-expect-error: `d` is an object cannot be incremented
updater.inc('d', 1);
// `activity` is a string
const omnichannel = new UpdaterImpl<IOmnichannelRoom>();
omnichannel.addToSet('v.activity', 'asd');
// @ts-expect-error: it should not allow numbers, since activity is a string
omnichannel.addToSet('v.activity', 1);
// @ts-expect-error: it should not allow objects, since activity is a string
omnichannel.addToSet('v.activity', {
asdas: 1,
});
// @ts-expect-error: it should not allow sub properties, since activity is a string
omnichannel.addToSet('v.activity.asd', {
asdas: 1,
});
});
test('updater supports nested paths up to 3 levels deep', () => {
const updater = new UpdaterImpl<{
_id: string;
level1: {
level2: {
level3: string;
};
};
}>();
// 1-level deep path
updater.set('level1', { level2: { level3: 'test' } });
// 2-level deep path
updater.set('level1.level2', { level3: 'test' });
// 3-level deep path
updater.set('level1.level2.level3', 'test');
});
test('updater validates keys on real schemas with nested paths', () => {
const updater = new UpdaterImpl<IOmnichannelRoom>();
// valid 1-level paths
updater.set('fname', 'test');
// valid 2-level paths
updater.set('responseBy.lastMessageTs', new Date());
updater.set('v.activity', ['period1']);
// valid 3-level paths
updater.inc('metrics.response.total', 1);
updater.set('metrics.response.avg', 42);
updater.set('metrics.reaction.ft', 100);
// @ts-expect-error: non-existent top-level key
updater.set('nonExistentField', 'hello');
// @ts-expect-error: non-existent nested key
updater.set('responseBy.nonExistent', new Date());
// @ts-expect-error: 'fname' is a string, not a number — inc should reject it
updater.inc('fname', 1);
});
test('updater $set operations', async () => {
const updater = new UpdaterImpl<{
_id: string;
t: 'l';
a: {
b: string;
};
c?: number;
}>();
updater.set('a', {
b: 'set',
});
expect(updater.getUpdateFilter()).toEqual({ $set: { a: { b: 'set' } } });
});
test('updater $unset operations', async () => {
const updater = new UpdaterImpl<{
_id: string;
t: 'l';
a: {
b: string;
};
c?: number;
}>();
updater.unset('c');
expect(updater.getUpdateFilter()).toEqual({ $unset: { c: 1 } });
});
test('updater inc multiple operations', async () => {
const updater = new UpdaterImpl<{
_id: string;
t: 'l';
a: {
b: string;
};
c?: number;
}>();
updater.inc('c', 1);
updater.inc('c', 1);
expect(updater.getUpdateFilter()).toEqual({ $inc: { c: 2 } });
});
test('it should add items to array', async () => {
const updater = new UpdaterImpl<{
_id: string;
a: string[];
}>();
updater.addToSet('a', 'b');
updater.addToSet('a', 'c');
expect(updater.getUpdateFilter()).toEqual({ $addToSet: { a: { $each: ['b', 'c'] } } });
});
test('it should getUpdateFilter only once', async () => {
const updater = new UpdaterImpl<{
_id: string;
t: 'l';
a: {
b: string;
};
c?: number;
}>();
updater.set('a', {
b: 'set',
});
expect(updater.getUpdateFilter()).toEqual({ $set: { a: { b: 'set' } } });
expect(() => updater.getUpdateFilter()).toThrow();
});