246 lines
9.6 KiB
TypeScript
246 lines
9.6 KiB
TypeScript
import { Kysely } from 'kysely';
|
|
import { AssetFileType } from 'src/enum.js';
|
|
import { LoggingRepository } from 'src/repositories/logging.repository.js';
|
|
import { PersonRepository } from 'src/repositories/person.repository.js';
|
|
import { DB } from 'src/schema/index.js';
|
|
import { BaseService } from 'src/services/base.service.js';
|
|
import { newMediumService } from 'test/medium.factory.js';
|
|
import { getKyselyDB } from 'test/utils.js';
|
|
|
|
let defaultDatabase: Kysely<DB>;
|
|
|
|
const setup = (db?: Kysely<DB>) => {
|
|
const { ctx } = newMediumService(BaseService, {
|
|
database: db || defaultDatabase,
|
|
real: [],
|
|
mock: [LoggingRepository],
|
|
});
|
|
return { ctx, sut: ctx.get(PersonRepository) };
|
|
};
|
|
|
|
beforeAll(async () => {
|
|
defaultDatabase = await getKyselyDB();
|
|
});
|
|
|
|
describe(PersonRepository.name, () => {
|
|
describe('createAll', () => {
|
|
it('should create people in the groups they were given', async () => {
|
|
const { ctx, sut } = setup();
|
|
const [{ user: user1 }, { user: user2 }] = [await ctx.newUser(), await ctx.newUser()];
|
|
|
|
const [group1, group2] = await sut.createGroups([
|
|
{ clusterGroupId: user1.clusterGroupId },
|
|
{ clusterGroupId: user1.clusterGroupId },
|
|
]);
|
|
const group3 = await sut.createGroup(user2.id);
|
|
|
|
const people = await sut.createAll([
|
|
{ ownerId: user1.id, name: 'Alice', personGroupId: group1.id },
|
|
{ ownerId: user1.id, name: 'Bob', personGroupId: group2.id },
|
|
{ ownerId: user2.id, name: 'Carol', personGroupId: group3.id },
|
|
]);
|
|
|
|
expect(people.map(({ personGroupId }) => personGroupId)).toEqual([group1.id, group2.id, group3.id]);
|
|
|
|
const groups = await ctx.database
|
|
.selectFrom('person')
|
|
.innerJoin('person_group', 'person_group.id', 'person.personGroupId')
|
|
.innerJoin('user', 'user.id', 'person.ownerId')
|
|
.select(['person.name', 'person_group.clusterGroupId', 'user.clusterGroupId as ownerClusterGroupId'])
|
|
.where(
|
|
'person.personGroupId',
|
|
'in',
|
|
people.map(({ personGroupId }) => personGroupId),
|
|
)
|
|
.execute();
|
|
|
|
expect(groups).toHaveLength(3);
|
|
for (const group of groups) {
|
|
expect(group.clusterGroupId).toBe(group.ownerClusterGroupId);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('createGroup', () => {
|
|
it('should create a group in the owner cluster group', async () => {
|
|
const { ctx, sut } = setup();
|
|
const { user } = await ctx.newUser();
|
|
|
|
const group = await sut.createGroup(user.id);
|
|
|
|
const owner = await ctx.database
|
|
.selectFrom('person_group')
|
|
.innerJoin('user', 'user.clusterGroupId', 'person_group.clusterGroupId')
|
|
.select('user.id')
|
|
.where('person_group.id', '=', group.id)
|
|
.executeTakeFirstOrThrow();
|
|
|
|
expect(owner.id).toBe(user.id);
|
|
});
|
|
|
|
it('should put people created with the same group into that group', async () => {
|
|
const { ctx, sut } = setup(await getKyselyDB());
|
|
const [{ user: user1 }, { user: user2 }] = [await ctx.newUser(), await ctx.newUser()];
|
|
|
|
const group = await sut.createGroup(user1.id);
|
|
const person1 = await sut.create({ ownerId: user1.id, name: 'Alice', personGroupId: group.id });
|
|
const person2 = await sut.create({ ownerId: user2.id, name: 'Alice', personGroupId: group.id });
|
|
|
|
expect(person1.personGroupId).toBe(group.id);
|
|
expect(person2.personGroupId).toBe(group.id);
|
|
|
|
const groups = await ctx.database.selectFrom('person_group').select('person_group.id').execute();
|
|
expect(groups.map(({ id }) => id)).toEqual([group.id]);
|
|
});
|
|
});
|
|
|
|
describe('getByGroupId', () => {
|
|
it('should not return a person owned by another user', async () => {
|
|
const { ctx, sut } = setup();
|
|
const [{ user: user1 }, { user: user2 }] = [await ctx.newUser(), await ctx.newUser()];
|
|
const group = await sut.createGroup(user1.id);
|
|
|
|
const person1 = await sut.create({ ownerId: user1.id, name: 'Alice', personGroupId: group.id });
|
|
const person2 = await ctx.database
|
|
.insertInto('person')
|
|
.values({ ownerId: user2.id, name: 'Alice', personGroupId: person1.personGroupId })
|
|
.returningAll()
|
|
.executeTakeFirstOrThrow();
|
|
|
|
await expect(sut.getByGroupId({ ownerId: user1.id, personGroupId: person1.personGroupId })).resolves.toEqual(
|
|
expect.objectContaining({ personGroupId: person1.personGroupId, ownerId: user1.id }),
|
|
);
|
|
await expect(sut.getByGroupId({ ownerId: user2.id, personGroupId: person1.personGroupId })).resolves.toEqual(
|
|
expect.objectContaining({ personGroupId: person2.personGroupId, ownerId: user2.id }),
|
|
);
|
|
});
|
|
|
|
it('should return nothing when the group belongs to another user', async () => {
|
|
const { ctx, sut } = setup();
|
|
const [{ user: user1 }, { user: user2 }] = [await ctx.newUser(), await ctx.newUser()];
|
|
const group = await sut.createGroup(user1.id);
|
|
|
|
const person = await sut.create({ ownerId: user1.id, name: 'Alice', personGroupId: group.id });
|
|
|
|
await expect(
|
|
sut.getByGroupId({ ownerId: user2.id, personGroupId: person.personGroupId }),
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('deleteEmptyGroups', () => {
|
|
it('should delete groups that no longer have any people', async () => {
|
|
const { ctx, sut } = setup(await getKyselyDB());
|
|
const { user } = await ctx.newUser();
|
|
const [keptGroup, emptiedGroup] = await sut.createGroups([
|
|
{ clusterGroupId: user.clusterGroupId },
|
|
{ clusterGroupId: user.clusterGroupId },
|
|
]);
|
|
|
|
const kept = await sut.create({ ownerId: user.id, name: 'Alice', personGroupId: keptGroup.id });
|
|
const emptied = await sut.create({ ownerId: user.id, name: 'Bob', personGroupId: emptiedGroup.id });
|
|
await ctx.database
|
|
.deleteFrom('person')
|
|
.where('person.ownerId', '=', emptied.ownerId)
|
|
.where('person.personGroupId', '=', emptied.personGroupId)
|
|
.execute();
|
|
|
|
await expect(sut.deleteEmptyGroups()).resolves.toBe(1);
|
|
|
|
const groups = await ctx.database.selectFrom('person_group').select('person_group.id').execute();
|
|
expect(groups.map(({ id }) => id)).toEqual([kept.personGroupId]);
|
|
});
|
|
});
|
|
|
|
describe('deleteOrphanedClusterGroups', () => {
|
|
it('should delete cluster groups that no longer belong to a user, along with their people', async () => {
|
|
const { ctx, sut } = setup(await getKyselyDB());
|
|
const [{ user: kept }, { user: removed }] = [await ctx.newUser(), await ctx.newUser()];
|
|
const keptGroup = await sut.createGroup(kept.id);
|
|
const removedGroup = await sut.createGroup(removed.id);
|
|
|
|
const keptPerson = await sut.create({ ownerId: kept.id, name: 'Alice', personGroupId: keptGroup.id });
|
|
await sut.create({ ownerId: removed.id, name: 'Bob', personGroupId: removedGroup.id });
|
|
const { clusterGroupId } = await ctx.database
|
|
.selectFrom('user')
|
|
.select('user.clusterGroupId')
|
|
.where('user.id', '=', kept.id)
|
|
.executeTakeFirstOrThrow();
|
|
await ctx.database.deleteFrom('user').where('user.id', '=', removed.id).execute();
|
|
|
|
await expect(sut.deleteOrphanedClusterGroups()).resolves.toBe(1);
|
|
|
|
const clusterGroups = await ctx.database.selectFrom('cluster_group').select('cluster_group.id').execute();
|
|
expect(clusterGroups.map(({ id }) => id)).toEqual([clusterGroupId]);
|
|
|
|
const groups = await ctx.database.selectFrom('person_group').select('person_group.id').execute();
|
|
expect(groups.map(({ id }) => id)).toEqual([keptPerson.personGroupId]);
|
|
});
|
|
});
|
|
|
|
describe('getDataForThumbnailGenerationJob', () => {
|
|
it('should not return the edited preview path', async () => {
|
|
const { ctx, sut } = setup();
|
|
const { user } = await ctx.newUser();
|
|
|
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
|
const { person } = await ctx.newPerson({ ownerId: user.id });
|
|
|
|
const { assetFace } = await ctx.newAssetFace({
|
|
assetId: asset.id,
|
|
personGroupId: person.personGroupId,
|
|
boundingBoxX1: 10,
|
|
boundingBoxY1: 10,
|
|
boundingBoxX2: 90,
|
|
boundingBoxY2: 90,
|
|
});
|
|
|
|
// there's a circular dependency between assetFace and person, so we need to update the person after creating the assetFace
|
|
await ctx.database
|
|
.updateTable('person')
|
|
.set({ faceAssetId: assetFace.id })
|
|
.where('ownerId', '=', person.ownerId)
|
|
.where('personGroupId', '=', person.personGroupId)
|
|
.execute();
|
|
|
|
await ctx.newAssetFile({
|
|
assetId: asset.id,
|
|
type: AssetFileType.Preview,
|
|
path: 'preview_edited.jpg',
|
|
isEdited: true,
|
|
});
|
|
await ctx.newAssetFile({
|
|
assetId: asset.id,
|
|
type: AssetFileType.Preview,
|
|
path: 'preview_unedited.jpg',
|
|
isEdited: false,
|
|
});
|
|
|
|
const result = await sut.getDataForThumbnailGenerationJob({
|
|
ownerId: person.ownerId,
|
|
personGroupId: person.personGroupId,
|
|
});
|
|
|
|
expect(result).toEqual(
|
|
expect.objectContaining({
|
|
previewPath: 'preview_unedited.jpg',
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getForFeatureFaceUpdate', () => {
|
|
it('should ignore soft deleted faces', async () => {
|
|
const { ctx, sut } = setup();
|
|
const { user } = await ctx.newUser();
|
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
|
const { person } = await ctx.newPerson({ ownerId: user.id });
|
|
await ctx.newAssetFace({ assetId: asset.id, deletedAt: new Date(), personGroupId: person.personGroupId });
|
|
|
|
await expect(
|
|
sut.getForFeatureFaceUpdate({ personGroupId: person.personGroupId, assetId: asset.id }),
|
|
).resolves.toEqual(undefined);
|
|
});
|
|
});
|
|
});
|