import { Insertable, Kysely } from 'kysely'; import { randomBytes } from 'node:crypto'; import { Writable } from 'node:stream'; import { Assets, DB, Partners, Sessions, Users } from 'src/db'; import { AuthDto } from 'src/dtos/auth.dto'; import { AssetType } from 'src/enum'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumRepository } from 'src/repositories/album.repository'; import { ApiKeyRepository } from 'src/repositories/api-key.repository'; import { AssetRepository } from 'src/repositories/asset.repository'; import { AuditRepository } from 'src/repositories/audit.repository'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { MachineLearningRepository } from 'src/repositories/machine-learning.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MetadataRepository } from 'src/repositories/metadata.repository'; import { MoveRepository } from 'src/repositories/move.repository'; import { NotificationRepository } from 'src/repositories/notification.repository'; import { OAuthRepository } from 'src/repositories/oauth.repository'; import { PartnerRepository } from 'src/repositories/partner.repository'; import { PersonRepository } from 'src/repositories/person.repository'; import { ProcessRepository } from 'src/repositories/process.repository'; import { SearchRepository } from 'src/repositories/search.repository'; import { ServerInfoRepository } from 'src/repositories/server-info.repository'; import { SessionRepository } from 'src/repositories/session.repository'; import { SharedLinkRepository } from 'src/repositories/shared-link.repository'; import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SyncRepository } from 'src/repositories/sync.repository'; import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { TrashRepository } from 'src/repositories/trash.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; import { ViewRepository } from 'src/repositories/view-repository'; import { newLoggingRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { newUuid } from 'test/small.factory'; class CustomWritable extends Writable { private data = ''; _write(chunk: any, encoding: string, callback: () => void) { this.data += chunk.toString(); callback(); } getResponse() { const result = this.data; return result .split('\n') .filter((x) => x.length > 0) .map((x) => JSON.parse(x)); } } type Asset = Insertable; type User = Partial>; type Session = Omit, 'token'> & { token?: string }; type Partner = Insertable; export class TestFactory { private assets: Asset[] = []; private sessions: Session[] = []; private users: User[] = []; private partners: Partner[] = []; private constructor(private context: TestContext) {} static create(context: TestContext) { return new TestFactory(context); } static stream() { return new CustomWritable(); } static asset(asset: Asset) { const assetId = asset.id || newUuid(); const defaults: Insertable = { deviceAssetId: '', deviceId: '', originalFileName: '', checksum: randomBytes(32), type: AssetType.IMAGE, originalPath: '/path/to/something.jpg', ownerId: '@immich.cloud', isVisible: true, }; return { ...defaults, ...asset, id: assetId, }; } static auth(auth: { user: User; session?: Session }) { return auth as AuthDto; } static user(user: User = {}) { const userId = user.id || newUuid(); const defaults: Insertable = { email: `${userId}@immich.cloud`, name: `User ${userId}`, deletedAt: null, }; return { ...defaults, ...user, id: userId, }; } static session(session: Session) { const id = session.id || newUuid(); const defaults = { token: randomBytes(36).toString('base64url'), }; return { ...defaults, ...session, id, }; } static partner(partner: Partner) { const defaults = { inTimeline: true, }; return { ...defaults, ...partner, }; } withAsset(asset: Asset) { this.assets.push(asset); return this; } withSession(session: Session) { this.sessions.push(session); return this; } withUser(user: User = {}) { this.users.push(user); return this; } withPartner(partner: Partner) { this.partners.push(partner); return this; } async create() { for (const asset of this.assets) { await this.context.createAsset(asset); } for (const user of this.users) { await this.context.createUser(user); } for (const partner of this.partners) { await this.context.createPartner(partner); } for (const session of this.sessions) { await this.context.createSession(session); } return this.context; } } export class TestContext { access: AccessRepository; logger: LoggingRepository; activity: ActivityRepository; album: AlbumRepository; apiKey: ApiKeyRepository; asset: AssetRepository; audit: AuditRepository; config: ConfigRepository; library: LibraryRepository; machineLearning: MachineLearningRepository; media: MediaRepository; metadata: MetadataRepository; move: MoveRepository; notification: NotificationRepository; oauth: OAuthRepository; partner: PartnerRepository; person: PersonRepository; process: ProcessRepository; search: SearchRepository; serverInfo: ServerInfoRepository; session: SessionRepository; sharedLink: SharedLinkRepository; stack: StackRepository; storage: StorageRepository; sync: SyncRepository; telemetry: TelemetryRepository; trash: TrashRepository; user: UserRepository; versionHistory: VersionHistoryRepository; view: ViewRepository; private constructor(private db: Kysely) { const logger = newLoggingRepositoryMock() as unknown as LoggingRepository; const config = new ConfigRepository(); this.access = new AccessRepository(this.db); this.logger = logger; this.activity = new ActivityRepository(this.db); this.album = new AlbumRepository(this.db); this.apiKey = new ApiKeyRepository(this.db); this.asset = new AssetRepository(this.db); this.audit = new AuditRepository(this.db); this.config = config; this.library = new LibraryRepository(this.db); this.machineLearning = new MachineLearningRepository(logger); this.media = new MediaRepository(logger); this.metadata = new MetadataRepository(logger); this.move = new MoveRepository(this.db); this.notification = new NotificationRepository(logger); this.oauth = new OAuthRepository(logger); this.partner = new PartnerRepository(this.db); this.person = new PersonRepository(this.db); this.process = new ProcessRepository(logger); this.search = new SearchRepository(logger, this.db); this.serverInfo = new ServerInfoRepository(config, logger); this.session = new SessionRepository(this.db); this.sharedLink = new SharedLinkRepository(this.db); this.stack = new StackRepository(this.db); this.storage = new StorageRepository(logger); this.sync = new SyncRepository(this.db); this.telemetry = newTelemetryRepositoryMock() as unknown as TelemetryRepository; this.trash = new TrashRepository(this.db); this.user = new UserRepository(this.db); this.versionHistory = new VersionHistoryRepository(this.db); this.view = new ViewRepository(this.db); } static from(db: Kysely) { return new TestContext(db).getFactory(); } getFactory() { return TestFactory.create(this); } createUser(user: User = {}) { return this.user.create(TestFactory.user(user)); } createPartner(partner: Partner) { return this.partner.create(TestFactory.partner(partner)); } createAsset(asset: Asset) { return this.asset.create(TestFactory.asset(asset)); } createSession(session: Session) { return this.session.create(TestFactory.session(session)); } }