1
0
mirror of https://github.com/immich-app/immich.git synced 2025-08-08 23:07:06 +02:00

feat: UserMetadata sync (#19882)

* feat: UserMetadata sync

* refactor: sync table filters (#19887)
This commit is contained in:
Daniel Dietzler
2025-07-11 20:19:53 +02:00
committed by GitHub
parent 9e48ae3052
commit df581cc0d5
20 changed files with 653 additions and 50 deletions

View File

@ -0,0 +1,123 @@
import { Kysely } from 'kysely';
import { SyncEntityType, SyncRequestType, UserMetadataKey } from 'src/enum';
import { UserRepository } from 'src/repositories/user.repository';
import { DB } from 'src/schema';
import { SyncTestContext } from 'test/medium.factory';
import { getKyselyDB } from 'test/utils';
let defaultDatabase: Kysely<DB>;
const setup = async (db?: Kysely<DB>) => {
const ctx = new SyncTestContext(db || defaultDatabase);
const { auth, user, session } = await ctx.newSyncAuthUser();
return { auth, user, session, ctx };
};
beforeAll(async () => {
defaultDatabase = await getKyselyDB();
});
describe(SyncEntityType.UserMetadataV1, () => {
it('should detect and sync new user metadata', async () => {
const { auth, user, ctx } = await setup();
const userRepo = ctx.get(UserRepository);
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.ONBOARDING, value: { isOnboarded: true } });
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
expect(response).toHaveLength(1);
expect(response).toEqual([
{
ack: expect.any(String),
data: {
key: UserMetadataKey.ONBOARDING,
userId: user.id,
value: { isOnboarded: true },
},
type: 'UserMetadataV1',
},
]);
await ctx.syncAckAll(auth, response);
await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]);
});
it('should update user metadata', async () => {
const { auth, user, ctx } = await setup();
const userRepo = ctx.get(UserRepository);
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.ONBOARDING, value: { isOnboarded: true } });
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
expect(response).toHaveLength(1);
expect(response).toEqual([
{
ack: expect.any(String),
data: {
key: UserMetadataKey.ONBOARDING,
userId: user.id,
value: { isOnboarded: true },
},
type: 'UserMetadataV1',
},
]);
await ctx.syncAckAll(auth, response);
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.ONBOARDING, value: { isOnboarded: false } });
const updatedResponse = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
expect(updatedResponse).toEqual([
{
ack: expect.any(String),
data: {
key: UserMetadataKey.ONBOARDING,
userId: user.id,
value: { isOnboarded: false },
},
type: 'UserMetadataV1',
},
]);
await ctx.syncAckAll(auth, updatedResponse);
await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([]);
});
});
describe(SyncEntityType.UserMetadataDeleteV1, () => {
it('should delete and sync user metadata', async () => {
const { auth, user, ctx } = await setup();
const userRepo = ctx.get(UserRepository);
await userRepo.upsertMetadata(user.id, { key: UserMetadataKey.ONBOARDING, value: { isOnboarded: true } });
const response = await ctx.syncStream(auth, [SyncRequestType.UserMetadataV1]);
expect(response).toHaveLength(1);
expect(response).toEqual([
{
ack: expect.any(String),
data: {
key: UserMetadataKey.ONBOARDING,
userId: user.id,
value: { isOnboarded: true },
},
type: 'UserMetadataV1',
},
]);
await ctx.syncAckAll(auth, response);
await userRepo.deleteMetadata(auth.user.id, UserMetadataKey.ONBOARDING);
await expect(ctx.syncStream(auth, [SyncRequestType.UserMetadataV1])).resolves.toEqual([
{
ack: expect.any(String),
data: {
userId: user.id,
key: UserMetadataKey.ONBOARDING,
},
type: 'UserMetadataDeleteV1',
},
]);
});
});