mirror of
https://github.com/immich-app/immich.git
synced 2026-03-23 22:59:40 +03:00
Merge branch 'main' into feat/mobile-ocr
This commit is contained in:
@@ -2,7 +2,7 @@ import { Kysely } from 'kysely';
|
||||
import { DateTime } from 'luxon';
|
||||
import { resolve } from 'node:path';
|
||||
import { DB } from 'src/schema';
|
||||
import { ExifTestContext } from 'test/medium.factory';
|
||||
import { ExifTestContext, testAssetsDir } from 'test/medium.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let database: Kysely<DB>;
|
||||
@@ -11,7 +11,7 @@ const setup = async (testAssetPath: string) => {
|
||||
const ctx = new ExifTestContext(database);
|
||||
|
||||
const { user } = await ctx.newUser();
|
||||
const originalPath = resolve(`../e2e/test-assets/${testAssetPath}`);
|
||||
const originalPath = resolve(testAssetsDir, testAssetPath);
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id, originalPath });
|
||||
|
||||
return { ctx, sut: ctx.sut, asset };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { resolve } from 'node:path';
|
||||
import { DB } from 'src/schema';
|
||||
import { ExifTestContext } from 'test/medium.factory';
|
||||
import { ExifTestContext, testAssetsDir } from 'test/medium.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let database: Kysely<DB>;
|
||||
@@ -10,7 +10,7 @@ const setup = async (testAssetPath: string) => {
|
||||
const ctx = new ExifTestContext(database);
|
||||
|
||||
const { user } = await ctx.newUser();
|
||||
const originalPath = resolve(`../e2e/test-assets/${testAssetPath}`);
|
||||
const originalPath = resolve(testAssetsDir, testAssetPath);
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id, originalPath });
|
||||
|
||||
return { ctx, sut: ctx.sut, asset };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { resolve } from 'node:path';
|
||||
import { DB } from 'src/schema';
|
||||
import { ExifTestContext } from 'test/medium.factory';
|
||||
import { ExifTestContext, testAssetsDir } from 'test/medium.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let database: Kysely<DB>;
|
||||
@@ -10,7 +10,7 @@ const setup = async (testAssetPath: string) => {
|
||||
const ctx = new ExifTestContext(database);
|
||||
|
||||
const { user } = await ctx.newUser();
|
||||
const originalPath = resolve(`../e2e/test-assets/${testAssetPath}`);
|
||||
const originalPath = resolve(testAssetsDir, testAssetPath);
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id, originalPath });
|
||||
|
||||
return { ctx, sut: ctx.sut, asset };
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { AssetOrder, AssetVisibility } from 'src/enum';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||
import { DB } from 'src/schema';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { newMediumService } from 'test/medium.factory';
|
||||
import { factory } from 'test/small.factory';
|
||||
import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let defaultDatabase: Kysely<DB>;
|
||||
@@ -22,6 +24,61 @@ beforeAll(async () => {
|
||||
});
|
||||
|
||||
describe(AssetRepository.name, () => {
|
||||
describe('getTimeBucket', () => {
|
||||
it('should order assets by local day first and fileCreatedAt within each day', async () => {
|
||||
const { ctx, sut } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const [{ asset: previousLocalDayAsset }, { asset: nextLocalDayEarlierAsset }, { asset: nextLocalDayLaterAsset }] =
|
||||
await Promise.all([
|
||||
ctx.newAsset({
|
||||
ownerId: user.id,
|
||||
fileCreatedAt: new Date('2026-03-09T00:30:00.000Z'),
|
||||
localDateTime: new Date('2026-03-08T22:30:00.000Z'),
|
||||
}),
|
||||
ctx.newAsset({
|
||||
ownerId: user.id,
|
||||
fileCreatedAt: new Date('2026-03-08T23:30:00.000Z'),
|
||||
localDateTime: new Date('2026-03-09T01:30:00.000Z'),
|
||||
}),
|
||||
ctx.newAsset({
|
||||
ownerId: user.id,
|
||||
fileCreatedAt: new Date('2026-03-08T23:45:00.000Z'),
|
||||
localDateTime: new Date('2026-03-09T01:45:00.000Z'),
|
||||
}),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
ctx.newExif({ assetId: previousLocalDayAsset.id, timeZone: 'UTC-2' }),
|
||||
ctx.newExif({ assetId: nextLocalDayEarlierAsset.id, timeZone: 'UTC+2' }),
|
||||
ctx.newExif({ assetId: nextLocalDayLaterAsset.id, timeZone: 'UTC+2' }),
|
||||
]);
|
||||
|
||||
const descendingBucket = await sut.getTimeBucket(
|
||||
'2026-03-01',
|
||||
{ order: AssetOrder.Desc, userIds: [user.id], visibility: AssetVisibility.Timeline },
|
||||
auth,
|
||||
);
|
||||
expect(JSON.parse(descendingBucket.assets)).toEqual(
|
||||
expect.objectContaining({
|
||||
id: [nextLocalDayLaterAsset.id, nextLocalDayEarlierAsset.id, previousLocalDayAsset.id],
|
||||
}),
|
||||
);
|
||||
|
||||
const ascendingBucket = await sut.getTimeBucket(
|
||||
'2026-03-01',
|
||||
{ order: AssetOrder.Asc, userIds: [user.id], visibility: AssetVisibility.Timeline },
|
||||
auth,
|
||||
);
|
||||
expect(JSON.parse(ascendingBucket.assets)).toEqual(
|
||||
expect.objectContaining({
|
||||
id: [previousLocalDayAsset.id, nextLocalDayEarlierAsset.id, nextLocalDayLaterAsset.id],
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upsertExif', () => {
|
||||
it('should append to locked columns', async () => {
|
||||
const { ctx, sut } = setup();
|
||||
|
||||
@@ -88,4 +88,24 @@ describe(SearchService.name, () => {
|
||||
expect(result).toEqual({ total: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('withStacked option', () => {
|
||||
it('should exclude stacked assets when withStacked is false', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
|
||||
const { asset: primaryAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const { asset: stackedAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const { asset: unstackedAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
|
||||
await ctx.newStack({ ownerId: user.id }, [primaryAsset.id, stackedAsset.id]);
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const response = await sut.searchMetadata(auth, { withStacked: false });
|
||||
|
||||
expect(response.assets.items.length).toBe(1);
|
||||
expect(response.assets.items[0].id).toBe(unstackedAsset.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,6 +95,469 @@ describe(SharedLinkService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAll', () => {
|
||||
it('should return all shared links even when they share the same createdAt', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sameTimestamp = '2024-01-01T00:00:00.000Z';
|
||||
|
||||
const link1 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
createdAt: sameTimestamp,
|
||||
});
|
||||
|
||||
const link2 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
createdAt: sameTimestamp,
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(2);
|
||||
const ids = result.map((r) => r.id);
|
||||
expect(ids).toContain(link1.id);
|
||||
expect(ids).toContain(link2.id);
|
||||
});
|
||||
|
||||
it('should return shared links sorted by createdAt in descending order', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
const link1 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
createdAt: '2021-01-01T00:00:00.000Z',
|
||||
});
|
||||
|
||||
const link2 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
createdAt: '2023-01-01T00:00:00.000Z',
|
||||
});
|
||||
|
||||
const link3 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
createdAt: '2022-01-01T00:00:00.000Z',
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result.map((r) => r.id)).toEqual([link2.id, link3.id, link1.id]);
|
||||
});
|
||||
|
||||
it('should not return shared links belonging to other users', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
|
||||
const { user: userA } = await ctx.newUser();
|
||||
const { user: userB } = await ctx.newUser();
|
||||
const authA = factory.auth({ user: userA });
|
||||
const authB = factory.auth({ user: userB });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
const linkA = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: userA.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
});
|
||||
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: userB.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
});
|
||||
|
||||
const resultA = await sut.getAll(authA, {});
|
||||
expect(resultA).toHaveLength(1);
|
||||
expect(resultA[0].id).toBe(linkA.id);
|
||||
|
||||
const resultB = await sut.getAll(authB, {});
|
||||
expect(resultB).toHaveLength(1);
|
||||
expect(resultB[0].id).not.toBe(linkA.id);
|
||||
});
|
||||
|
||||
it('should filter by albumId', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { album: album1 } = await ctx.newAlbum({ ownerId: user.id });
|
||||
const { album: album2 } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
const link1 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album1.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album2.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, { albumId: album1.id });
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].id).toBe(link1.id);
|
||||
});
|
||||
|
||||
it('should return album shared links with album data', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].album).toBeDefined();
|
||||
expect(result[0].album!.id).toBe(album.id);
|
||||
});
|
||||
|
||||
it('should return multiple album shared links without sql error from json group by', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { album: album1 } = await ctx.newAlbum({ ownerId: user.id });
|
||||
const { album: album2 } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
const link1 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album1.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const link2 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album2.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(2);
|
||||
const ids = result.map((r) => r.id);
|
||||
expect(ids).toContain(link1.id);
|
||||
expect(ids).toContain(link2.id);
|
||||
expect(result[0].album).toBeDefined();
|
||||
expect(result[1].album).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return mixed album and individual shared links together', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
const albumLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const albumLink2 = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
const individualLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
assetIds: [asset.id],
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(3);
|
||||
const ids = result.map((r) => r.id);
|
||||
expect(ids).toContain(albumLink.id);
|
||||
expect(ids).toContain(albumLink2.id);
|
||||
expect(ids).toContain(individualLink.id);
|
||||
});
|
||||
|
||||
it('should return only the first asset as cover for an individual shared link', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const assets = await Promise.all([
|
||||
ctx.newAsset({ ownerId: user.id, fileCreatedAt: '2021-01-01T00:00:00.000Z' }),
|
||||
ctx.newAsset({ ownerId: user.id, fileCreatedAt: '2023-01-01T00:00:00.000Z' }),
|
||||
ctx.newAsset({ ownerId: user.id, fileCreatedAt: '2022-01-01T00:00:00.000Z' }),
|
||||
]);
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
assetIds: assets.map(({ asset }) => asset.id),
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].assets).toHaveLength(1);
|
||||
expect(result[0].assets[0].id).toBe(assets[0].asset.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it('should not return trashed assets for an individual shared link', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { asset: visibleAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: visibleAsset.id, make: 'Canon' });
|
||||
|
||||
const { asset: trashedAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: trashedAsset.id, make: 'Canon' });
|
||||
await ctx.softDeleteAsset(trashedAsset.id);
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sharedLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
assetIds: [visibleAsset.id, trashedAsset.id],
|
||||
});
|
||||
|
||||
const result = await sut.get(auth, sharedLink.id);
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.assets).toHaveLength(1);
|
||||
expect(result!.assets[0].id).toBe(visibleAsset.id);
|
||||
});
|
||||
|
||||
it('should return empty assets when all individually shared assets are trashed', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
||||
await ctx.softDeleteAsset(asset.id);
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sharedLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
assetIds: [asset.id],
|
||||
});
|
||||
|
||||
await expect(sut.get(auth, sharedLink.id)).resolves.toMatchObject({
|
||||
assets: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return trashed assets in a shared album', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const { asset: visibleAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: visibleAsset.id, make: 'Canon' });
|
||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: visibleAsset.id });
|
||||
|
||||
const { asset: trashedAsset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: trashedAsset.id, make: 'Canon' });
|
||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: trashedAsset.id });
|
||||
await ctx.softDeleteAsset(trashedAsset.id);
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sharedLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: true,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
await expect(sut.get(auth, sharedLink.id)).resolves.toMatchObject({
|
||||
album: expect.objectContaining({ assetCount: 1 }),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an empty asset count when all album assets are trashed', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, make: 'Canon' });
|
||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||
await ctx.softDeleteAsset(asset.id);
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sharedLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
await expect(sut.get(auth, sharedLink.id)).resolves.toMatchObject({
|
||||
album: expect.objectContaining({ assetCount: 0 }),
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return an album shared link when the album is trashed', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
const sharedLink = await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
await ctx.softDeleteAlbum(album.id);
|
||||
|
||||
await expect(sut.get(auth, sharedLink.id)).rejects.toThrow('Shared link not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAll', () => {
|
||||
it('should not return trashed assets as cover for an individual shared link', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
|
||||
const { asset: trashedAsset } = await ctx.newAsset({
|
||||
ownerId: user.id,
|
||||
fileCreatedAt: '2020-01-01T00:00:00.000Z',
|
||||
});
|
||||
await ctx.softDeleteAsset(trashedAsset.id);
|
||||
|
||||
const { asset: visibleAsset } = await ctx.newAsset({
|
||||
ownerId: user.id,
|
||||
fileCreatedAt: '2021-01-01T00:00:00.000Z',
|
||||
});
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Individual,
|
||||
assetIds: [trashedAsset.id, visibleAsset.id],
|
||||
});
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].assets).toHaveLength(1);
|
||||
expect(result[0].assets[0].id).toBe(visibleAsset.id);
|
||||
});
|
||||
|
||||
it('should not return an album shared link when the album is trashed', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const auth = factory.auth({ user });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
|
||||
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||
await sharedLinkRepo.create({
|
||||
key: randomBytes(16),
|
||||
id: factory.uuid(),
|
||||
userId: user.id,
|
||||
albumId: album.id,
|
||||
allowUpload: false,
|
||||
type: SharedLinkType.Album,
|
||||
});
|
||||
|
||||
await ctx.softDeleteAlbum(album.id);
|
||||
|
||||
const result = await sut.getAll(auth, {});
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove individually shared asset', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user