immich/server/test/small.factory.ts

567 lines
12 KiB
TypeScript
Raw Normal View History

2025-04-08 10:52:54 -04:00
import {
Activity,
Album,
2025-04-08 10:52:54 -04:00
ApiKey,
AssetFace,
2026-01-09 17:59:52 -05:00
AssetFile,
2025-04-08 10:52:54 -04:00
AuthApiKey,
AuthSharedLink,
2025-04-08 10:52:54 -04:00
AuthUser,
Exif,
2025-04-08 10:52:54 -04:00
Library,
2025-04-08 12:40:03 -04:00
Memory,
2025-04-08 10:52:54 -04:00
Partner,
Person,
2025-04-09 10:24:38 -04:00
Session,
Stack,
Tag,
2025-04-08 10:52:54 -04:00
User,
UserAdmin,
} from 'src/database';
import { MapAsset } from 'src/dtos/asset-response.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { AssetEditAction, AssetEditActionItem, MirrorAxis } from 'src/dtos/editing.dto';
2025-11-25 08:19:40 -05:00
import { QueueStatisticsDto } from 'src/dtos/queue.dto';
import {
AssetFileType,
AssetOrder,
AssetStatus,
AssetType,
AssetVisibility,
MemoryType,
Permission,
SourceType,
UserMetadataKey,
UserStatus,
} from 'src/enum';
import { DeepPartial, OnThisDayData, UserMetadataItem } from 'src/types';
2025-09-03 18:50:27 -04:00
import { v4, v7 } from 'uuid';
2025-09-03 18:50:27 -04:00
export const newUuid = () => v4();
export const newUuids = () =>
Array.from({ length: 100 })
.fill(0)
.map(() => newUuid());
export const newDate = () => new Date();
2025-09-03 18:50:27 -04:00
export const newUuidV7 = () => v7();
2025-03-07 16:03:34 -05:00
export const newSha1 = () => Buffer.from('this is a fake hash');
export const newEmbedding = () => {
const embedding = Array.from({ length: 512 })
.fill(0)
.map(() => Math.random());
return '[' + embedding + ']';
};
2025-04-09 10:24:38 -04:00
const authFactory = ({
apiKey,
session,
sharedLink,
user,
}: {
apiKey?: Partial<AuthApiKey>;
session?: { id: string };
2025-04-15 15:54:23 -04:00
user?: Omit<
Partial<UserAdmin>,
'createdAt' | 'updatedAt' | 'deletedAt' | 'fileCreatedAt' | 'fileModifiedAt' | 'localDateTime' | 'profileChangedAt'
>;
sharedLink?: Partial<AuthSharedLink>;
} = {}) => {
const auth: AuthDto = {
user: authUserFactory(userAdminFactory(user ?? {})),
};
const userId = auth.user.id;
if (apiKey) {
auth.apiKey = authApiKeyFactory(apiKey);
}
2025-04-09 10:24:38 -04:00
if (session) {
2025-07-11 09:38:02 -04:00
auth.session = {
id: session.id,
hasElevatedPermission: false,
};
2025-04-09 10:24:38 -04:00
}
if (sharedLink) {
auth.sharedLink = authSharedLinkFactory({ ...sharedLink, userId });
}
return auth;
};
const authSharedLinkFactory = (sharedLink: Partial<AuthSharedLink> = {}) => {
const {
id = newUuid(),
expiresAt = null,
userId = newUuid(),
showExif = true,
allowUpload = false,
allowDownload = true,
password = null,
} = sharedLink;
return { id, expiresAt, userId, showExif, allowUpload, allowDownload, password };
};
const authApiKeyFactory = (apiKey: Partial<AuthApiKey> = {}) => ({
id: newUuid(),
2025-07-15 14:50:13 -04:00
permissions: [Permission.All],
...apiKey,
});
const authUserFactory = (authUser: Partial<AuthUser> = {}) => {
const {
id = newUuid(),
isAdmin = false,
name = 'Test User',
email = 'test@immich.cloud',
quotaUsageInBytes = 0,
quotaSizeInBytes = null,
} = authUser;
return { id, isAdmin, name, email, quotaUsageInBytes, quotaSizeInBytes };
};
const partnerFactory = (partner: Partial<Partner> = {}) => {
const sharedBy = userFactory(partner.sharedBy || {});
const sharedWith = userFactory(partner.sharedWith || {});
return {
sharedById: sharedBy.id,
sharedBy,
sharedWithId: sharedWith.id,
sharedWith,
createId: newUuidV7(),
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
inTimeline: true,
...partner,
};
};
2025-04-09 10:24:38 -04:00
const sessionFactory = (session: Partial<Session> = {}) => ({
2025-03-10 16:52:44 -04:00
id: newUuid(),
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
2025-03-10 16:52:44 -04:00
deviceOS: 'android',
deviceType: 'mobile',
token: 'abc123',
parentId: null,
2025-05-15 18:08:31 -04:00
expiresAt: null,
2025-03-10 16:52:44 -04:00
userId: newUuid(),
pinExpiresAt: newDate(),
2025-07-11 09:38:02 -04:00
isPendingSyncReset: false,
appVersion: session.appVersion ?? null,
2025-04-09 10:24:38 -04:00
...session,
2025-03-10 16:52:44 -04:00
});
2025-11-25 08:19:40 -05:00
const queueStatisticsFactory = (dto?: Partial<QueueStatisticsDto>) => ({
active: 0,
completed: 0,
failed: 0,
delayed: 0,
waiting: 0,
paused: 0,
...dto,
});
const stackFactory = ({ owner, assets, ...stack }: DeepPartial<Stack> = {}): Stack => {
const ownerId = newUuid();
return {
id: newUuid(),
primaryAssetId: assets?.[0].id ?? newUuid(),
ownerId,
owner: userFactory(owner ?? { id: ownerId }),
assets: assets?.map((asset) => assetFactory(asset)) ?? [],
...stack,
};
};
2025-03-10 16:52:44 -04:00
const userFactory = (user: Partial<User> = {}) => ({
id: newUuid(),
name: 'Test User',
email: 'test@immich.cloud',
2025-04-28 09:54:51 -04:00
avatarColor: null,
profileImagePath: '',
profileChangedAt: newDate(),
metadata: [
{
2025-07-15 14:50:13 -04:00
key: UserMetadataKey.Onboarding,
value: 'true',
},
] as UserMetadataItem[],
...user,
});
const userAdminFactory = (user: Partial<UserAdmin> = {}) => {
const {
id = newUuid(),
name = 'Test User',
email = 'test@immich.cloud',
profileImagePath = '',
profileChangedAt = newDate(),
storageLabel = null,
shouldChangePassword = false,
isAdmin = false,
2025-04-28 09:54:51 -04:00
avatarColor = null,
createdAt = newDate(),
updatedAt = newDate(),
deletedAt = null,
oauthId = '',
quotaSizeInBytes = null,
quotaUsageInBytes = 0,
2025-07-15 14:50:13 -04:00
status = UserStatus.Active,
metadata = [],
} = user;
return {
id,
name,
email,
profileImagePath,
profileChangedAt,
storageLabel,
shouldChangePassword,
isAdmin,
2025-04-28 09:54:51 -04:00
avatarColor,
createdAt,
updatedAt,
deletedAt,
oauthId,
quotaSizeInBytes,
quotaUsageInBytes,
status,
metadata,
};
};
const assetFactory = (
asset: Omit<DeepPartial<MapAsset>, 'exifInfo' | 'owner' | 'stack' | 'tags' | 'faces' | 'files' | 'edits'> = {},
) => {
return {
id: newUuid(),
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,
...asset,
};
};
const activityFactory = (activity: Partial<Activity> = {}) => {
const userId = activity.userId || newUuid();
return {
2025-03-07 16:03:34 -05:00
id: newUuid(),
comment: null,
isLiked: false,
userId,
user: userFactory({ id: userId }),
assetId: newUuid(),
albumId: newUuid(),
2025-03-07 16:03:34 -05:00
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
...activity,
};
};
const apiKeyFactory = (apiKey: Partial<ApiKey> = {}) => ({
id: newUuid(),
userId: newUuid(),
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
name: 'Api Key',
2025-07-15 14:50:13 -04:00
permissions: [Permission.All],
...apiKey,
});
const libraryFactory = (library: Partial<Library> = {}) => ({
id: newUuid(),
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
deletedAt: null,
refreshedAt: null,
name: 'Library',
assets: [],
ownerId: newUuid(),
importPaths: [],
exclusionPatterns: [],
...library,
});
2025-04-08 12:40:03 -04:00
const memoryFactory = (memory: Partial<Memory> = {}) => ({
id: newUuid(),
createdAt: newDate(),
updatedAt: newDate(),
updateId: newUuidV7(),
deletedAt: null,
ownerId: newUuid(),
2025-07-15 14:50:13 -04:00
type: MemoryType.OnThisDay,
data: { year: 2024 } as OnThisDayData,
isSaved: false,
memoryAt: newDate(),
seenAt: null,
showAt: newDate(),
hideAt: newDate(),
assets: [],
...memory,
});
2025-03-10 16:52:44 -04:00
const versionHistoryFactory = () => ({
id: newUuid(),
createdAt: newDate(),
version: '1.123.45',
});
const assetSidecarWriteFactory = () => {
const id = newUuid();
return {
id,
originalPath: '/path/to/original-path.jpg.xmp',
tags: [],
files: [
{
id: newUuid(),
path: '/path/to/original-path.jpg.xmp',
type: AssetFileType.Sidecar,
isEdited: false,
},
],
exifInfo: {
assetId: id,
description: 'this is a description',
latitude: 12,
longitude: 12,
dateTimeOriginal: '2023-11-22T04:56:12.196Z',
2026-02-05 18:43:03 +01:00
timeZone: 'UTC-6',
} as unknown as Exif,
};
};
2025-04-08 10:52:54 -04:00
const assetOcrFactory = (
ocr: {
id?: string;
assetId?: string;
x1?: number;
y1?: number;
x2?: number;
y2?: number;
x3?: number;
y3?: number;
x4?: number;
y4?: number;
boxScore?: number;
textScore?: number;
text?: string;
2026-01-09 17:59:52 -05:00
isVisible?: boolean;
} = {},
) => ({
id: newUuid(),
assetId: newUuid(),
x1: 0.1,
y1: 0.2,
x2: 0.3,
y2: 0.2,
x3: 0.3,
y3: 0.4,
x4: 0.1,
y4: 0.4,
boxScore: 0.95,
textScore: 0.92,
text: 'Sample Text',
2026-01-09 17:59:52 -05:00
isVisible: true,
...ocr,
});
const assetFileFactory = (file: Partial<AssetFile> = {}) => ({
2026-01-09 17:59:52 -05:00
id: newUuid(),
type: AssetFileType.Preview,
path: '/uploads/user-id/thumbs/path.jpg',
isEdited: false,
isProgressive: false,
2026-01-09 17:59:52 -05:00
...file,
});
const exifFactory = (exif: Partial<Exif> = {}) => ({
assetId: newUuid(),
autoStackId: null,
bitsPerSample: null,
city: 'Austin',
colorspace: null,
country: 'United States of America',
dateTimeOriginal: newDate(),
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: newDate(),
orientation: '1',
profileDescription: null,
projectionType: null,
rating: 4,
state: 'Texas',
tags: ['parent/child'],
timeZone: 'UTC-6',
...exif,
});
const tagFactory = (tag: Partial<Tag>): Tag => ({
id: newUuid(),
color: null,
createdAt: newDate(),
parentId: null,
updatedAt: newDate(),
value: `tag-${newUuid()}`,
...tag,
});
const faceFactory = ({ person, ...face }: DeepPartial<AssetFace> = {}): AssetFace => ({
assetId: newUuid(),
boundingBoxX1: 1,
boundingBoxX2: 2,
boundingBoxY1: 1,
boundingBoxY2: 2,
deletedAt: null,
id: newUuid(),
imageHeight: 420,
imageWidth: 42,
isVisible: true,
personId: null,
sourceType: SourceType.MachineLearning,
updatedAt: newDate(),
updateId: newUuidV7(),
person: person === null ? null : personFactory(person),
...face,
});
const assetEditFactory = (edit?: Partial<AssetEditActionItem>): AssetEditActionItem => {
switch (edit?.action) {
case AssetEditAction.Crop: {
return { action: AssetEditAction.Crop, parameters: { height: 42, width: 42, x: 0, y: 10 }, ...edit };
}
case AssetEditAction.Mirror: {
return { action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal }, ...edit };
}
case AssetEditAction.Rotate: {
return { action: AssetEditAction.Rotate, parameters: { angle: 90 }, ...edit };
}
default: {
return { action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } };
}
}
};
const personFactory = (person?: Partial<Person>): Person => ({
birthDate: newDate(),
color: null,
createdAt: newDate(),
faceAssetId: null,
id: newUuid(),
isFavorite: false,
isHidden: false,
name: 'person',
ownerId: newUuid(),
thumbnailPath: '/path/to/person/thumbnail.jpg',
updatedAt: newDate(),
updateId: newUuidV7(),
...person,
});
const albumFactory = (album?: Partial<Omit<Album, 'assets'>>) => ({
albumName: 'My Album',
albumThumbnailAssetId: null,
albumUsers: [],
assets: [],
createdAt: newDate(),
deletedAt: null,
description: 'Album description',
id: newUuid(),
isActivityEnabled: false,
order: AssetOrder.Desc,
ownerId: newUuid(),
sharedLinks: [],
updatedAt: newDate(),
updateId: newUuidV7(),
...album,
});
export const factory = {
activity: activityFactory,
apiKey: apiKeyFactory,
asset: assetFactory,
2026-01-09 17:59:52 -05:00
assetFile: assetFileFactory,
assetOcr: assetOcrFactory,
auth: authFactory,
authApiKey: authApiKeyFactory,
authUser: authUserFactory,
library: libraryFactory,
memory: memoryFactory,
partner: partnerFactory,
2025-11-25 08:19:40 -05:00
queueStatistics: queueStatisticsFactory,
2025-03-10 16:52:44 -04:00
session: sessionFactory,
stack: stackFactory,
user: userFactory,
userAdmin: userAdminFactory,
2025-03-10 16:52:44 -04:00
versionHistory: versionHistoryFactory,
2025-04-08 10:52:54 -04:00
jobAssets: {
sidecarWrite: assetSidecarWriteFactory,
},
exif: exifFactory,
face: faceFactory,
person: personFactory,
assetEdit: assetEditFactory,
tag: tagFactory,
album: albumFactory,
uuid: newUuid,
buffer: () => Buffer.from('this is a fake buffer'),
date: newDate,
responses: {
badRequest: (message: any = null) => ({
error: 'Bad Request',
statusCode: 400,
message: message ?? expect.anything(),
}),
},
};