mirror of
https://github.com/immich-app/immich.git
synced 2026-02-12 11:58:15 +03:00
refactor: test factories (#25977)
This commit is contained in:
@@ -58,7 +58,7 @@ select
|
||||
from
|
||||
(
|
||||
select
|
||||
*
|
||||
"shared_link".*
|
||||
from
|
||||
"shared_link"
|
||||
where
|
||||
@@ -243,7 +243,7 @@ select
|
||||
from
|
||||
(
|
||||
select
|
||||
*
|
||||
"shared_link".*
|
||||
from
|
||||
"shared_link"
|
||||
where
|
||||
@@ -316,7 +316,7 @@ select
|
||||
from
|
||||
(
|
||||
select
|
||||
*
|
||||
"shared_link".*
|
||||
from
|
||||
"shared_link"
|
||||
where
|
||||
|
||||
@@ -44,9 +44,9 @@ const withAlbumUsers = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
};
|
||||
|
||||
const withSharedLink = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
return jsonArrayFrom(eb.selectFrom('shared_link').selectAll().whereRef('shared_link.albumId', '=', 'album.id')).as(
|
||||
'sharedLinks',
|
||||
);
|
||||
return jsonArrayFrom(
|
||||
eb.selectFrom('shared_link').selectAll('shared_link').whereRef('shared_link.albumId', '=', 'album.id'),
|
||||
).as('sharedLinks');
|
||||
};
|
||||
|
||||
const withAssets = (eb: ExpressionBuilder<DB, 'album'>) => {
|
||||
@@ -283,7 +283,7 @@ export class AlbumRepository {
|
||||
|
||||
return tx
|
||||
.selectFrom('album')
|
||||
.selectAll()
|
||||
.selectAll('album')
|
||||
.where('id', '=', newAlbum.id)
|
||||
.select(withOwner)
|
||||
.select(withAssets)
|
||||
|
||||
@@ -260,7 +260,7 @@ export class SharedLinkRepository {
|
||||
.selectAll('asset')
|
||||
.innerJoinLateral(
|
||||
(eb) =>
|
||||
eb.selectFrom('asset_exif').whereRef('asset_exif.assetId', '=', 'asset.id').selectAll().as('exif'),
|
||||
eb.selectFrom('asset_exif').whereRef('asset_exif.assetId', '=', 'asset.id').selectAll().as('exifInfo'),
|
||||
(join) => join.onTrue(),
|
||||
)
|
||||
.as('assets'),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
54
server/test/factories/album-user.factory.ts
Normal file
54
server/test/factories/album-user.factory.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { AlbumUserTable } from 'src/schema/tables/album-user.table';
|
||||
import { AlbumFactory } from 'test/factories/album.factory';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
import { AlbumUserLike, FactoryBuilder, UserLike } from 'test/factories/types';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
import { newDate, newUuid, newUuidV7 } from 'test/small.factory';
|
||||
|
||||
export class AlbumUserFactory {
|
||||
#user!: UserFactory;
|
||||
|
||||
private constructor(private readonly value: Selectable<AlbumUserTable>) {
|
||||
value.userId ??= newUuid();
|
||||
this.#user = UserFactory.from({ id: value.userId });
|
||||
}
|
||||
|
||||
static create(dto: AlbumUserLike = {}) {
|
||||
return AlbumUserFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: AlbumUserLike = {}) {
|
||||
return new AlbumUserFactory({
|
||||
albumId: newUuid(),
|
||||
userId: newUuid(),
|
||||
role: AlbumUserRole.Editor,
|
||||
createId: newUuidV7(),
|
||||
createdAt: newDate(),
|
||||
updateId: newUuidV7(),
|
||||
updatedAt: newDate(),
|
||||
...dto,
|
||||
});
|
||||
}
|
||||
|
||||
album(dto: AlbumUserLike = {}, builder?: FactoryBuilder<AlbumFactory>) {
|
||||
const album = build(AlbumFactory.from(dto), builder);
|
||||
this.value.albumId = album.build().id;
|
||||
return this;
|
||||
}
|
||||
|
||||
user(dto: UserLike = {}, builder?: FactoryBuilder<UserFactory>) {
|
||||
const user = build(UserFactory.from(dto), builder);
|
||||
this.value.userId = user.build().id;
|
||||
this.#user = user;
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
return {
|
||||
...this.value,
|
||||
user: this.#user.build(),
|
||||
};
|
||||
}
|
||||
}
|
||||
87
server/test/factories/album.factory.ts
Normal file
87
server/test/factories/album.factory.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AssetOrder } from 'src/enum';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
||||
import { AlbumUserFactory } from 'test/factories/album-user.factory';
|
||||
import { AssetFactory } from 'test/factories/asset.factory';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
import { AlbumLike, AlbumUserLike, AssetLike, FactoryBuilder, UserLike } from 'test/factories/types';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
import { newDate, newUuid, newUuidV7 } from 'test/small.factory';
|
||||
|
||||
export class AlbumFactory {
|
||||
#owner: UserFactory;
|
||||
#sharedLinks: Selectable<SharedLinkTable>[] = [];
|
||||
#albumUsers: AlbumUserFactory[] = [];
|
||||
#assets: AssetFactory[] = [];
|
||||
|
||||
private constructor(private readonly value: Selectable<AlbumTable>) {
|
||||
value.ownerId ??= newUuid();
|
||||
this.#owner = UserFactory.from({ id: value.ownerId });
|
||||
}
|
||||
|
||||
static create(dto: AlbumLike = {}) {
|
||||
return AlbumFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: AlbumLike = {}) {
|
||||
return new AlbumFactory({
|
||||
id: newUuid(),
|
||||
ownerId: newUuid(),
|
||||
albumName: 'My Album',
|
||||
albumThumbnailAssetId: null,
|
||||
createdAt: newDate(),
|
||||
deletedAt: null,
|
||||
description: 'Album description',
|
||||
isActivityEnabled: false,
|
||||
order: AssetOrder.Desc,
|
||||
updatedAt: newDate(),
|
||||
updateId: newUuidV7(),
|
||||
...dto,
|
||||
}).owner();
|
||||
}
|
||||
|
||||
owner(dto: UserLike = {}, builder?: FactoryBuilder<UserFactory>) {
|
||||
this.#owner = build(UserFactory.from(dto), builder);
|
||||
this.value.ownerId = this.#owner.build().id;
|
||||
return this;
|
||||
}
|
||||
|
||||
sharedLinks() {
|
||||
this.#sharedLinks = [];
|
||||
return this;
|
||||
}
|
||||
|
||||
albumUser(dto: AlbumUserLike = {}, builder?: FactoryBuilder<AlbumUserFactory>) {
|
||||
const albumUser = build(AlbumUserFactory.from(dto).album(this.value), builder);
|
||||
this.#albumUsers.push(albumUser);
|
||||
return this;
|
||||
}
|
||||
|
||||
asset(dto: AssetLike = {}, builder?: FactoryBuilder<AssetFactory>) {
|
||||
const asset = build(AssetFactory.from(dto), builder);
|
||||
|
||||
// use album owner by default
|
||||
if (!dto.ownerId) {
|
||||
asset.owner(this.#owner.build());
|
||||
}
|
||||
|
||||
if (!this.#assets) {
|
||||
this.#assets = [];
|
||||
}
|
||||
|
||||
this.#assets.push(asset);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
return {
|
||||
...this.value,
|
||||
owner: this.#owner.build(),
|
||||
assets: this.#assets.map((asset) => asset.build()),
|
||||
albumUsers: this.#albumUsers.map((albumUser) => albumUser.build()),
|
||||
sharedLinks: this.#sharedLinks,
|
||||
};
|
||||
}
|
||||
}
|
||||
55
server/test/factories/asset-exif.factory.ts
Normal file
55
server/test/factories/asset-exif.factory.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { AssetExifLike } from 'test/factories/types';
|
||||
import { factory } from 'test/small.factory';
|
||||
|
||||
export class AssetExifFactory {
|
||||
private constructor(private readonly value: Selectable<AssetExifTable>) {}
|
||||
|
||||
static create(dto: AssetExifLike = {}) {
|
||||
return AssetExifFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: AssetExifLike = {}) {
|
||||
return new AssetExifFactory({
|
||||
updatedAt: factory.date(),
|
||||
updateId: factory.uuid(),
|
||||
assetId: factory.uuid(),
|
||||
autoStackId: null,
|
||||
bitsPerSample: null,
|
||||
city: 'Austin',
|
||||
colorspace: null,
|
||||
country: 'United States of America',
|
||||
dateTimeOriginal: factory.date(),
|
||||
description: '',
|
||||
exifImageHeight: 420,
|
||||
exifImageWidth: 42,
|
||||
exposureTime: null,
|
||||
fileSizeInByte: 69,
|
||||
fNumber: 1.7,
|
||||
focalLength: 4.38,
|
||||
fps: null,
|
||||
iso: 947,
|
||||
latitude: 30.267_334_570_570_195,
|
||||
longitude: -97.789_833_534_282_07,
|
||||
lensModel: null,
|
||||
livePhotoCID: null,
|
||||
make: 'Google',
|
||||
model: 'Pixel 7',
|
||||
modifyDate: factory.date(),
|
||||
orientation: '1',
|
||||
profileDescription: null,
|
||||
projectionType: null,
|
||||
rating: 4,
|
||||
lockedProperties: [],
|
||||
state: 'Texas',
|
||||
tags: ['parent/child'],
|
||||
timeZone: 'UTC-6',
|
||||
...dto,
|
||||
});
|
||||
}
|
||||
|
||||
build() {
|
||||
return { ...this.value };
|
||||
}
|
||||
}
|
||||
79
server/test/factories/asset.factory.ts
Normal file
79
server/test/factories/asset.factory.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { AssetExifFactory } from 'test/factories/asset-exif.factory';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
import { AssetExifLike, AssetLike, FactoryBuilder, UserLike } from 'test/factories/types';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
import { factory, newDate, newSha1, newUuid, newUuidV7 } from 'test/small.factory';
|
||||
|
||||
export class AssetFactory {
|
||||
#assetExif?: AssetExifFactory;
|
||||
#owner!: UserFactory;
|
||||
|
||||
private constructor(private readonly value: Selectable<AssetTable>) {
|
||||
value.ownerId ??= newUuid();
|
||||
this.#owner = UserFactory.from({ id: value.ownerId });
|
||||
}
|
||||
|
||||
static create(dto: AssetLike = {}) {
|
||||
return AssetFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: AssetLike = {}) {
|
||||
return new AssetFactory({
|
||||
id: factory.uuid(),
|
||||
createdAt: newDate(),
|
||||
updatedAt: newDate(),
|
||||
deletedAt: null,
|
||||
updateId: newUuidV7(),
|
||||
status: AssetStatus.Active,
|
||||
checksum: newSha1(),
|
||||
deviceAssetId: '',
|
||||
deviceId: '',
|
||||
duplicateId: null,
|
||||
duration: null,
|
||||
encodedVideoPath: null,
|
||||
fileCreatedAt: newDate(),
|
||||
fileModifiedAt: newDate(),
|
||||
isExternal: false,
|
||||
isFavorite: false,
|
||||
isOffline: false,
|
||||
libraryId: null,
|
||||
livePhotoVideoId: null,
|
||||
localDateTime: newDate(),
|
||||
originalFileName: 'IMG_123.jpg',
|
||||
originalPath: `/data/12/34/IMG_123.jpg`,
|
||||
ownerId: newUuid(),
|
||||
stackId: null,
|
||||
thumbhash: null,
|
||||
type: AssetType.Image,
|
||||
visibility: AssetVisibility.Timeline,
|
||||
width: null,
|
||||
height: null,
|
||||
isEdited: false,
|
||||
...dto,
|
||||
});
|
||||
}
|
||||
|
||||
owner(dto: UserLike = {}, builder?: FactoryBuilder<UserFactory>) {
|
||||
this.#owner = build(UserFactory.from(dto), builder);
|
||||
this.value.ownerId = this.#owner.build().id;
|
||||
return this;
|
||||
}
|
||||
|
||||
exif(dto: AssetExifLike = {}, builder?: FactoryBuilder<AssetExifFactory>) {
|
||||
this.#assetExif = build(AssetExifFactory.from(dto), builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
const exif = this.#assetExif?.build();
|
||||
|
||||
return {
|
||||
...this.value,
|
||||
exifInfo: exif as NonNullable<typeof exif>,
|
||||
owner: this.#owner.build(),
|
||||
};
|
||||
}
|
||||
}
|
||||
48
server/test/factories/auth.factory.ts
Normal file
48
server/test/factories/auth.factory.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
import { SharedLinkFactory } from 'test/factories/shared-link.factory';
|
||||
import { FactoryBuilder, SharedLinkLike, UserLike } from 'test/factories/types';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
|
||||
export class AuthFactory {
|
||||
#user: UserFactory;
|
||||
#sharedLink?: SharedLinkFactory;
|
||||
|
||||
private constructor(user: UserFactory) {
|
||||
this.#user = user;
|
||||
}
|
||||
|
||||
static create(dto: UserLike = {}) {
|
||||
return AuthFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: UserLike = {}) {
|
||||
return new AuthFactory(UserFactory.from(dto));
|
||||
}
|
||||
|
||||
apiKey() {
|
||||
// TODO
|
||||
return this;
|
||||
}
|
||||
|
||||
sharedLink(dto: SharedLinkLike = {}, builder?: FactoryBuilder<SharedLinkFactory>) {
|
||||
this.#sharedLink = build(SharedLinkFactory.from(dto), builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
build(): AuthDto {
|
||||
const { id, isAdmin, name, email, quotaUsageInBytes, quotaSizeInBytes } = this.#user.build();
|
||||
|
||||
return {
|
||||
user: {
|
||||
id,
|
||||
isAdmin,
|
||||
name,
|
||||
email,
|
||||
quotaUsageInBytes,
|
||||
quotaSizeInBytes,
|
||||
},
|
||||
sharedLink: this.#sharedLink?.build(),
|
||||
};
|
||||
}
|
||||
}
|
||||
5
server/test/factories/builder.factory.ts
Normal file
5
server/test/factories/builder.factory.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { FactoryBuilder } from 'test/factories/types';
|
||||
|
||||
export const build = <T>(factory: T, builder?: FactoryBuilder<T>) => {
|
||||
return builder ? builder(factory) : factory;
|
||||
};
|
||||
63
server/test/factories/shared-link.factory.ts
Normal file
63
server/test/factories/shared-link.factory.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { SharedLinkType } from 'src/enum';
|
||||
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
||||
import { AlbumFactory } from 'test/factories/album.factory';
|
||||
import { build } from 'test/factories/builder.factory';
|
||||
import { AlbumLike, FactoryBuilder, SharedLinkLike, UserLike } from 'test/factories/types';
|
||||
import { UserFactory } from 'test/factories/user.factory';
|
||||
import { factory, newDate, newUuid } from 'test/small.factory';
|
||||
|
||||
export class SharedLinkFactory {
|
||||
#owner: UserFactory;
|
||||
#album?: AlbumFactory;
|
||||
|
||||
private constructor(private readonly value: Selectable<SharedLinkTable>) {
|
||||
value.userId ??= newUuid();
|
||||
this.#owner = UserFactory.from({ id: value.userId });
|
||||
}
|
||||
|
||||
static create(dto: SharedLinkLike = {}) {
|
||||
return SharedLinkFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: SharedLinkLike = {}) {
|
||||
const type = dto.type ?? SharedLinkType.Individual;
|
||||
const albumId = (dto.albumId ?? type === SharedLinkType.Album) ? newUuid() : null;
|
||||
|
||||
return new SharedLinkFactory({
|
||||
id: factory.uuid(),
|
||||
description: 'Shared link description',
|
||||
userId: newUuid(),
|
||||
key: factory.buffer(),
|
||||
type,
|
||||
albumId,
|
||||
createdAt: newDate(),
|
||||
expiresAt: null,
|
||||
allowUpload: true,
|
||||
allowDownload: true,
|
||||
showExif: true,
|
||||
password: null,
|
||||
slug: null,
|
||||
...dto,
|
||||
});
|
||||
}
|
||||
|
||||
owner(dto: UserLike = {}, builder?: FactoryBuilder<UserFactory>): SharedLinkFactory {
|
||||
this.#owner = build(UserFactory.from(dto), builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
album(dto: AlbumLike = {}, builder?: FactoryBuilder<AlbumFactory>) {
|
||||
this.#album = build(AlbumFactory.from(dto), builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
return {
|
||||
...this.value,
|
||||
owner: this.#owner.build(),
|
||||
album: this.#album?.build(),
|
||||
assets: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
16
server/test/factories/types.ts
Normal file
16
server/test/factories/types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { AlbumUserTable } from 'src/schema/tables/album-user.table';
|
||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
|
||||
export type FactoryBuilder<T, R extends T = T> = (builder: T) => R;
|
||||
|
||||
export type AssetLike = Partial<Selectable<AssetTable>>;
|
||||
export type AssetExifLike = Partial<Selectable<AssetExifTable>>;
|
||||
export type AlbumLike = Partial<Selectable<AlbumTable>>;
|
||||
export type AlbumUserLike = Partial<Selectable<AlbumUserTable>>;
|
||||
export type SharedLinkLike = Partial<Selectable<SharedLinkTable>>;
|
||||
export type UserLike = Partial<Selectable<UserTable>>;
|
||||
46
server/test/factories/user.factory.ts
Normal file
46
server/test/factories/user.factory.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Selectable } from 'kysely';
|
||||
import { UserStatus } from 'src/enum';
|
||||
import { UserMetadataTable } from 'src/schema/tables/user-metadata.table';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { UserLike } from 'test/factories/types';
|
||||
import { newDate, newUuid, newUuidV7 } from 'test/small.factory';
|
||||
|
||||
export class UserFactory {
|
||||
private constructor(private value: Selectable<UserTable>) {}
|
||||
|
||||
static create(dto: UserLike = {}) {
|
||||
return UserFactory.from(dto).build();
|
||||
}
|
||||
|
||||
static from(dto: UserLike = {}) {
|
||||
return new UserFactory({
|
||||
id: newUuid(),
|
||||
email: 'test@immich.cloud',
|
||||
password: '',
|
||||
pinCode: null,
|
||||
createdAt: newDate(),
|
||||
profileImagePath: '',
|
||||
isAdmin: false,
|
||||
shouldChangePassword: false,
|
||||
avatarColor: null,
|
||||
deletedAt: null,
|
||||
oauthId: '',
|
||||
updatedAt: newDate(),
|
||||
storageLabel: null,
|
||||
name: 'Test User',
|
||||
quotaSizeInBytes: null,
|
||||
quotaUsageInBytes: 0,
|
||||
status: UserStatus.Active,
|
||||
profileChangedAt: newDate(),
|
||||
updateId: newUuidV7(),
|
||||
...dto,
|
||||
});
|
||||
}
|
||||
|
||||
build() {
|
||||
return {
|
||||
...this.value,
|
||||
metadata: [] as UserMetadataTable[],
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user