mirror of
https://github.com/immich-app/immich.git
synced 2025-12-26 17:25:00 +03:00
fix: tag clean up query and add tests (#22633)
* fix delete empty tags query * rewrite as a single statement * create tag service medium test * single tag exists, connected to one asset, and is not deleted * do not delete parent tag if children have an asset * hierarchical tag tests * fix query to match 3 test * remove transaction and format:fix * remove transaction and format:fix * simplify query, handle nested empty tag * unused helper --------- Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
116
server/test/medium/specs/services/tag.service.spec.ts
Normal file
116
server/test/medium/specs/services/tag.service.spec.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { JobStatus } from 'src/enum';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { TagRepository } from 'src/repositories/tag.repository';
|
||||
import { DB } from 'src/schema';
|
||||
import { TagService } from 'src/services/tag.service';
|
||||
import { upsertTags } from 'src/utils/tag';
|
||||
import { newMediumService } from 'test/medium.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let defaultDatabase: Kysely<DB>;
|
||||
|
||||
const setup = (db?: Kysely<DB>) => {
|
||||
return newMediumService(TagService, {
|
||||
database: db || defaultDatabase,
|
||||
real: [TagRepository, AccessRepository],
|
||||
mock: [LoggingRepository],
|
||||
});
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
defaultDatabase = await getKyselyDB();
|
||||
});
|
||||
|
||||
describe(TagService.name, () => {
|
||||
describe('deleteEmptyTags', () => {
|
||||
it('single tag exists, not connected to any assets, and is deleted', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const tagRepo = ctx.get(TagRepository);
|
||||
const [tag] = await upsertTags(tagRepo, { userId: user.id, tags: ['tag-1'] });
|
||||
|
||||
await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id }));
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success);
|
||||
await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('single tag exists, connected to one asset, and is not deleted', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const tagRepo = ctx.get(TagRepository);
|
||||
const [tag] = await upsertTags(tagRepo, { userId: user.id, tags: ['tag-1'] });
|
||||
|
||||
await ctx.newTagAsset({ tagIds: [tag.id], assetIds: [asset.id] });
|
||||
|
||||
await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id }));
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success);
|
||||
await expect(tagRepo.getByValue(user.id, 'tag-1')).resolves.toEqual(expect.objectContaining({ id: tag.id }));
|
||||
});
|
||||
|
||||
it('hierarchical tag exists, and the parent is connected to an asset, and the child is deleted', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const tagRepo = ctx.get(TagRepository);
|
||||
const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] });
|
||||
|
||||
await ctx.newTagAsset({ tagIds: [parentTag.id], assetIds: [asset.id] });
|
||||
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual(
|
||||
expect.objectContaining({ id: parentTag.id }),
|
||||
);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual(
|
||||
expect.objectContaining({ id: childTag.id }),
|
||||
);
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual(
|
||||
expect.objectContaining({ id: parentTag.id }),
|
||||
);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('hierarchical tag exists, and only the child is connected to an asset, and nothing is deleted', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const tagRepo = ctx.get(TagRepository);
|
||||
const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] });
|
||||
|
||||
await ctx.newTagAsset({ tagIds: [childTag.id], assetIds: [asset.id] });
|
||||
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual(
|
||||
expect.objectContaining({ id: parentTag.id }),
|
||||
);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual(
|
||||
expect.objectContaining({ id: childTag.id }),
|
||||
);
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual(
|
||||
expect.objectContaining({ id: parentTag.id }),
|
||||
);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual(
|
||||
expect.objectContaining({ id: childTag.id }),
|
||||
);
|
||||
});
|
||||
|
||||
it('hierarchical tag exists, and neither parent nor child is connected to an asset, and both are deleted', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const tagRepo = ctx.get(TagRepository);
|
||||
const [parentTag, childTag] = await upsertTags(tagRepo, { userId: user.id, tags: ['parent', 'parent/child'] });
|
||||
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toEqual(
|
||||
expect.objectContaining({ id: parentTag.id }),
|
||||
);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toEqual(
|
||||
expect.objectContaining({ id: childTag.id }),
|
||||
);
|
||||
await expect(sut.handleTagCleanup()).resolves.toBe(JobStatus.Success);
|
||||
await expect(tagRepo.getByValue(user.id, 'parent/child')).resolves.toBeUndefined();
|
||||
await expect(tagRepo.getByValue(user.id, 'parent')).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user