mirror of
https://github.com/immich-app/immich.git
synced 2025-12-17 01:11:13 +03:00
refactor: medium tests (#19537)
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { ClassConstructor } from 'class-transformer';
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||
import { Insertable, Kysely } from 'kysely';
|
||||
import { DateTime } from 'luxon';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { Writable } from 'node:stream';
|
||||
import { AssetFace } from 'src/database';
|
||||
import { Albums, AssetJobStatus, Assets, DB, FaceSearch, Person, Sessions } from 'src/db';
|
||||
import { Albums, AssetJobStatus, Assets, DB, Exif, FaceSearch, Person, Sessions } from 'src/db';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { AssetType, AssetVisibility, SourceType, SyncRequestType } from 'src/enum';
|
||||
import { AlbumUserRole, AssetType, AssetVisibility, SourceType, SyncRequestType } from 'src/enum';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||
@@ -31,298 +31,263 @@ import { SystemMetadataRepository } from 'src/repositories/system-metadata.repos
|
||||
import { UserRepository } from 'src/repositories/user.repository';
|
||||
import { VersionHistoryRepository } from 'src/repositories/version-history.repository';
|
||||
import { UserTable } from 'src/schema/tables/user.table';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { BASE_SERVICE_DEPENDENCIES, BaseService } from 'src/services/base.service';
|
||||
import { SyncService } from 'src/services/sync.service';
|
||||
import { RepositoryInterface } from 'src/types';
|
||||
import { factory, newDate, newEmbedding, newUuid } from 'test/small.factory';
|
||||
import { automock, ServiceOverrides, wait } from 'test/utils';
|
||||
import { automock, wait } from 'test/utils';
|
||||
import { Mocked } from 'vitest';
|
||||
|
||||
const sha256 = (value: string) => createHash('sha256').update(value).digest('base64');
|
||||
interface ClassConstructor<T = any> extends Function {
|
||||
new (...args: any[]): T;
|
||||
}
|
||||
|
||||
// type Repositories = Omit<ServiceOverrides, 'access' | 'telemetry'>;
|
||||
type RepositoriesTypes = {
|
||||
access: AccessRepository;
|
||||
activity: ActivityRepository;
|
||||
album: AlbumRepository;
|
||||
albumUser: AlbumUserRepository;
|
||||
asset: AssetRepository;
|
||||
assetJob: AssetJobRepository;
|
||||
config: ConfigRepository;
|
||||
crypto: CryptoRepository;
|
||||
database: DatabaseRepository;
|
||||
email: EmailRepository;
|
||||
job: JobRepository;
|
||||
user: UserRepository;
|
||||
logger: LoggingRepository;
|
||||
memory: MemoryRepository;
|
||||
notification: NotificationRepository;
|
||||
partner: PartnerRepository;
|
||||
person: PersonRepository;
|
||||
search: SearchRepository;
|
||||
session: SessionRepository;
|
||||
storage: StorageRepository;
|
||||
sync: SyncRepository;
|
||||
systemMetadata: SystemMetadataRepository;
|
||||
versionHistory: VersionHistoryRepository;
|
||||
};
|
||||
type RepositoryMocks = { [K in keyof RepositoriesTypes]: Mocked<RepositoryInterface<RepositoriesTypes[K]>> };
|
||||
type RepositoryOptions = Partial<{ [K in keyof RepositoriesTypes]: 'mock' | 'real' }>;
|
||||
|
||||
type ContextRepositoryMocks<R extends RepositoryOptions> = {
|
||||
[K in keyof RepositoriesTypes as R[K] extends 'mock' ? K : never]: Mocked<RepositoryInterface<RepositoriesTypes[K]>>;
|
||||
type MediumTestOptions = {
|
||||
mock: ClassConstructor<any>[];
|
||||
real: ClassConstructor<any>[];
|
||||
database: Kysely<DB>;
|
||||
};
|
||||
|
||||
type ContextRepositories<R extends RepositoryOptions> = {
|
||||
[K in keyof RepositoriesTypes as R[K] extends 'real' ? K : never]: RepositoriesTypes[K];
|
||||
export const newMediumService = <S extends BaseService>(Service: ClassConstructor<S>, options: MediumTestOptions) => {
|
||||
const ctx = new MediumTestContext(Service, options);
|
||||
return { sut: ctx.sut, ctx };
|
||||
};
|
||||
|
||||
export type Context<R extends RepositoryOptions, S extends BaseService> = {
|
||||
export class MediumTestContext<S extends BaseService = BaseService> {
|
||||
private repoCache: Record<string, any> = {};
|
||||
private sutDeps: any[];
|
||||
|
||||
sut: S;
|
||||
mocks: ContextRepositoryMocks<R>;
|
||||
repos: ContextRepositories<R>;
|
||||
getRepository<T extends keyof RepositoriesTypes>(key: T): RepositoriesTypes[T];
|
||||
};
|
||||
database: Kysely<DB>;
|
||||
|
||||
export type SyncTestOptions = {
|
||||
db: Kysely<DB>;
|
||||
};
|
||||
constructor(
|
||||
Service: ClassConstructor<S>,
|
||||
private options: MediumTestOptions,
|
||||
) {
|
||||
this.sutDeps = this.makeDeps(options);
|
||||
this.sut = new Service(...this.sutDeps);
|
||||
this.database = options.database;
|
||||
}
|
||||
|
||||
export const newSyncAuthUser = () => {
|
||||
const user = mediumFactory.userInsert();
|
||||
const session = mediumFactory.sessionInsert({ userId: user.id });
|
||||
private makeDeps(options: MediumTestOptions) {
|
||||
const deps = BASE_SERVICE_DEPENDENCIES;
|
||||
|
||||
const auth = factory.auth({
|
||||
session,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
});
|
||||
for (const dep of options.mock) {
|
||||
if (!deps.includes(dep)) {
|
||||
throw new Error(`Mocked repository ${dep.name} is not a valid dependency`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
auth,
|
||||
session,
|
||||
user,
|
||||
create: async (db: Kysely<DB>) => {
|
||||
await new UserRepository(db).create(user);
|
||||
await new SessionRepository(db).create(session);
|
||||
},
|
||||
};
|
||||
};
|
||||
for (const dep of options.real) {
|
||||
if (!deps.includes(dep)) {
|
||||
throw new Error(`Real repository ${dep.name} is not a valid dependency`);
|
||||
}
|
||||
}
|
||||
return (deps as ClassConstructor<any>[]).map((dep) => {
|
||||
if (options.real.includes(dep)) {
|
||||
return this.get(dep);
|
||||
}
|
||||
|
||||
export const newSyncTest = (options: SyncTestOptions) => {
|
||||
const { sut, mocks, repos, getRepository } = newMediumService(SyncService, {
|
||||
database: options.db,
|
||||
repos: {
|
||||
sync: 'real',
|
||||
session: 'real',
|
||||
},
|
||||
});
|
||||
if (options.mock.includes(dep)) {
|
||||
return newMockRepository(dep);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const testSync = async (auth: AuthDto, types: SyncRequestType[]) => {
|
||||
get<T>(key: ClassConstructor<T>): T {
|
||||
if (!this.repoCache[key.name]) {
|
||||
const real = newRealRepository(key, this.options.database);
|
||||
this.repoCache[key.name] = real;
|
||||
}
|
||||
|
||||
return this.repoCache[key.name];
|
||||
}
|
||||
|
||||
getMock<T, R = Mocked<T>>(key: ClassConstructor<T>): R {
|
||||
const index = BASE_SERVICE_DEPENDENCIES.indexOf(key as any);
|
||||
if (index === -1 || !this.options.mock.includes(key)) {
|
||||
throw new Error(`getMock called with a key that is not a mock: ${key.name}`);
|
||||
}
|
||||
|
||||
return this.sutDeps[index] as R;
|
||||
}
|
||||
|
||||
async newUser(dto: Partial<Insertable<UserTable>> = {}) {
|
||||
const user = mediumFactory.userInsert(dto);
|
||||
const result = await this.get(UserRepository).create(user);
|
||||
return { user, result };
|
||||
}
|
||||
|
||||
async newPartner(dto: { sharedById: string; sharedWithId: string; inTimeline?: boolean }) {
|
||||
const partner = { inTimeline: true, ...dto };
|
||||
const result = await this.get(PartnerRepository).create(partner);
|
||||
return { partner, result };
|
||||
}
|
||||
|
||||
async newAsset(dto: Partial<Insertable<Assets>> = {}) {
|
||||
const asset = mediumFactory.assetInsert(dto);
|
||||
const result = await this.get(AssetRepository).create(asset);
|
||||
return { asset, result };
|
||||
}
|
||||
|
||||
async newExif(dto: Insertable<Exif>) {
|
||||
const result = await this.get(AssetRepository).upsertExif(dto);
|
||||
return { result };
|
||||
}
|
||||
|
||||
async newAlbum(dto: Insertable<Albums>) {
|
||||
const album = mediumFactory.albumInsert(dto);
|
||||
const result = await this.get(AlbumRepository).create(album, [], []);
|
||||
return { album, result };
|
||||
}
|
||||
|
||||
async newAlbumAsset(albumAsset: { albumId: string; assetId: string }) {
|
||||
const result = await this.get(AlbumRepository).addAssetIds(albumAsset.albumId, [albumAsset.assetId]);
|
||||
return { albumAsset, result };
|
||||
}
|
||||
|
||||
async newAlbumUser(dto: { albumId: string; userId: string; role?: AlbumUserRole }) {
|
||||
const { albumId, userId, role = AlbumUserRole.EDITOR } = dto;
|
||||
const result = await this.get(AlbumUserRepository).create({ albumsId: albumId, usersId: userId, role });
|
||||
return { albumUser: { albumId, userId, role }, result };
|
||||
}
|
||||
|
||||
async newJobStatus(dto: Partial<Insertable<AssetJobStatus>> & { assetId: string }) {
|
||||
const jobStatus = mediumFactory.assetJobStatusInsert({ assetId: dto.assetId });
|
||||
const result = await this.get(AssetRepository).upsertJobStatus(jobStatus);
|
||||
return { jobStatus, result };
|
||||
}
|
||||
|
||||
async newPerson(dto: Partial<Insertable<Person>> & { ownerId: string }) {
|
||||
const person = mediumFactory.personInsert(dto);
|
||||
const result = await this.get(PersonRepository).create(person);
|
||||
return { person, result };
|
||||
}
|
||||
|
||||
async newSession(dto: Partial<Insertable<Sessions>> & { userId: string }) {
|
||||
const session = mediumFactory.sessionInsert(dto);
|
||||
const result = await this.get(SessionRepository).create(session);
|
||||
return { session, result };
|
||||
}
|
||||
|
||||
async newSyncAuthUser() {
|
||||
const { user } = await this.newUser();
|
||||
const { session } = await this.newSession({ userId: user.id });
|
||||
const auth = factory.auth({
|
||||
session,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
auth,
|
||||
session,
|
||||
user,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncTestContext extends MediumTestContext<SyncService> {
|
||||
constructor(database: Kysely<DB>) {
|
||||
super(SyncService, { database, real: [SyncRepository, SessionRepository], mock: [LoggingRepository] });
|
||||
}
|
||||
|
||||
async syncStream(auth: AuthDto, types: SyncRequestType[]) {
|
||||
const stream = mediumFactory.syncStream();
|
||||
// Wait for 2ms to ensure all updates are available and account for setTimeout inaccuracy
|
||||
await wait(2);
|
||||
await sut.stream(auth, stream, { types });
|
||||
await this.sut.stream(auth, stream, { types });
|
||||
|
||||
return stream.getResponse();
|
||||
};
|
||||
|
||||
return {
|
||||
sut,
|
||||
mocks,
|
||||
repos,
|
||||
getRepository,
|
||||
testSync,
|
||||
};
|
||||
};
|
||||
|
||||
export const newMediumService = <R extends RepositoryOptions, S extends BaseService>(
|
||||
Service: ClassConstructor<S>,
|
||||
options: {
|
||||
database: Kysely<DB>;
|
||||
repos: R;
|
||||
},
|
||||
): Context<R, S> => {
|
||||
const repos: Partial<RepositoriesTypes> = {};
|
||||
const mocks: Partial<RepositoryMocks> = {};
|
||||
|
||||
const loggerMock = getRepositoryMock('logger') as Mocked<LoggingRepository>;
|
||||
loggerMock.setContext.mockImplementation(() => {});
|
||||
repos.logger = loggerMock;
|
||||
|
||||
for (const [_key, type] of Object.entries(options.repos)) {
|
||||
if (type === 'real') {
|
||||
const key = _key as keyof RepositoriesTypes;
|
||||
repos[key] = getRepository(key, options.database) as any;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'mock') {
|
||||
const key = _key as keyof RepositoryMocks;
|
||||
mocks[key] = getRepositoryMock(key) as any;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const makeRepository = <K extends keyof RepositoriesTypes>(key: K) => {
|
||||
return repos[key] || getRepository(key, options.database);
|
||||
};
|
||||
async syncAckAll(auth: AuthDto, response: Array<{ type: string; ack: string }>) {
|
||||
const acks: Record<string, string> = {};
|
||||
for (const { type, ack } of response) {
|
||||
acks[type] = ack;
|
||||
}
|
||||
|
||||
const deps = asDeps({ ...mocks, ...repos } as ServiceOverrides);
|
||||
const sut = new Service(...deps);
|
||||
await this.sut.setAcks(auth, { acks: Object.values(acks) });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sut,
|
||||
mocks,
|
||||
repos,
|
||||
getRepository: makeRepository,
|
||||
} as Context<R, S>;
|
||||
};
|
||||
|
||||
export const getRepository = <K extends keyof RepositoriesTypes>(key: K, db: Kysely<DB>) => {
|
||||
const newRealRepository = <T>(key: ClassConstructor<T>, db: Kysely<DB>): T => {
|
||||
switch (key) {
|
||||
case 'access': {
|
||||
return new AccessRepository(db);
|
||||
case AccessRepository:
|
||||
case AlbumRepository:
|
||||
case AlbumUserRepository:
|
||||
case ActivityRepository:
|
||||
case AssetRepository:
|
||||
case AssetJobRepository:
|
||||
case MemoryRepository:
|
||||
case NotificationRepository:
|
||||
case PartnerRepository:
|
||||
case PersonRepository:
|
||||
case SearchRepository:
|
||||
case SessionRepository:
|
||||
case SyncRepository:
|
||||
case SystemMetadataRepository:
|
||||
case UserRepository:
|
||||
case VersionHistoryRepository: {
|
||||
return new key(db);
|
||||
}
|
||||
|
||||
case 'activity': {
|
||||
return new ActivityRepository(db);
|
||||
case ConfigRepository:
|
||||
case CryptoRepository: {
|
||||
return new key();
|
||||
}
|
||||
|
||||
case 'album': {
|
||||
return new AlbumRepository(db);
|
||||
case DatabaseRepository: {
|
||||
return new key(db, LoggingRepository.create(), new ConfigRepository());
|
||||
}
|
||||
|
||||
case 'albumUser': {
|
||||
return new AlbumUserRepository(db);
|
||||
case EmailRepository: {
|
||||
return new key(LoggingRepository.create());
|
||||
}
|
||||
|
||||
case 'asset': {
|
||||
return new AssetRepository(db);
|
||||
}
|
||||
|
||||
case 'assetJob': {
|
||||
return new AssetJobRepository(db);
|
||||
}
|
||||
|
||||
case 'config': {
|
||||
return new ConfigRepository();
|
||||
}
|
||||
|
||||
case 'crypto': {
|
||||
return new CryptoRepository();
|
||||
}
|
||||
|
||||
case 'database': {
|
||||
return new DatabaseRepository(db, LoggingRepository.create(), new ConfigRepository());
|
||||
}
|
||||
|
||||
case 'email': {
|
||||
return new EmailRepository(LoggingRepository.create());
|
||||
}
|
||||
|
||||
case 'logger': {
|
||||
return LoggingRepository.create();
|
||||
}
|
||||
|
||||
case 'memory': {
|
||||
return new MemoryRepository(db);
|
||||
}
|
||||
|
||||
case 'notification': {
|
||||
return new NotificationRepository(db);
|
||||
}
|
||||
|
||||
case 'partner': {
|
||||
return new PartnerRepository(db);
|
||||
}
|
||||
|
||||
case 'person': {
|
||||
return new PersonRepository(db);
|
||||
}
|
||||
|
||||
case 'search': {
|
||||
return new SearchRepository(db);
|
||||
}
|
||||
|
||||
case 'session': {
|
||||
return new SessionRepository(db);
|
||||
}
|
||||
|
||||
case 'sync': {
|
||||
return new SyncRepository(db);
|
||||
}
|
||||
|
||||
case 'systemMetadata': {
|
||||
return new SystemMetadataRepository(db);
|
||||
}
|
||||
|
||||
case 'user': {
|
||||
return new UserRepository(db);
|
||||
}
|
||||
|
||||
case 'versionHistory': {
|
||||
return new VersionHistoryRepository(db);
|
||||
case LoggingRepository as unknown as ClassConstructor<LoggingRepository>: {
|
||||
return new key() as unknown as T;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Invalid repository key: ${key}`);
|
||||
throw new Error(`Unable to create repository instance for key: ${key?.name || key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getRepositoryMock = <K extends keyof RepositoryMocks>(key: K) => {
|
||||
const newMockRepository = <T>(key: ClassConstructor<T>) => {
|
||||
switch (key) {
|
||||
case 'activity': {
|
||||
return automock(ActivityRepository) as Mocked<RepositoryInterface<ActivityRepository>>;
|
||||
case ActivityRepository:
|
||||
case AlbumRepository:
|
||||
case AssetRepository:
|
||||
case AssetJobRepository:
|
||||
case ConfigRepository:
|
||||
case CryptoRepository:
|
||||
case MemoryRepository:
|
||||
case NotificationRepository:
|
||||
case PartnerRepository:
|
||||
case PersonRepository:
|
||||
case SessionRepository:
|
||||
case SyncRepository:
|
||||
case SystemMetadataRepository:
|
||||
case UserRepository:
|
||||
case VersionHistoryRepository: {
|
||||
return automock(key);
|
||||
}
|
||||
|
||||
case 'album': {
|
||||
return automock(AlbumRepository);
|
||||
}
|
||||
|
||||
case 'asset': {
|
||||
return automock(AssetRepository);
|
||||
}
|
||||
|
||||
case 'assetJob': {
|
||||
return automock(AssetJobRepository);
|
||||
}
|
||||
|
||||
case 'config': {
|
||||
return automock(ConfigRepository);
|
||||
}
|
||||
|
||||
case 'crypto': {
|
||||
return automock(CryptoRepository);
|
||||
}
|
||||
|
||||
case 'database': {
|
||||
case DatabaseRepository: {
|
||||
return automock(DatabaseRepository, {
|
||||
args: [
|
||||
undefined,
|
||||
{
|
||||
setContext: () => {},
|
||||
},
|
||||
{ getEnv: () => ({ database: { vectorExtension: '' } }) },
|
||||
],
|
||||
args: [undefined, { setContext: () => {} }, { getEnv: () => ({ database: { vectorExtension: '' } }) }],
|
||||
});
|
||||
}
|
||||
|
||||
case 'email': {
|
||||
return automock(EmailRepository, {
|
||||
args: [
|
||||
{
|
||||
setContext: () => {},
|
||||
},
|
||||
],
|
||||
});
|
||||
case EmailRepository: {
|
||||
return automock(EmailRepository, { args: [{ setContext: () => {} }] });
|
||||
}
|
||||
|
||||
case 'job': {
|
||||
case JobRepository: {
|
||||
return automock(JobRepository, {
|
||||
args: [
|
||||
undefined,
|
||||
@@ -335,106 +300,21 @@ const getRepositoryMock = <K extends keyof RepositoryMocks>(key: K) => {
|
||||
});
|
||||
}
|
||||
|
||||
case 'logger': {
|
||||
case LoggingRepository as unknown as ClassConstructor<T>: {
|
||||
const configMock = { getEnv: () => ({ noColor: false }) };
|
||||
return automock(LoggingRepository, { args: [undefined, configMock], strict: false });
|
||||
}
|
||||
|
||||
case 'memory': {
|
||||
return automock(MemoryRepository);
|
||||
}
|
||||
|
||||
case 'notification': {
|
||||
return automock(NotificationRepository);
|
||||
}
|
||||
|
||||
case 'partner': {
|
||||
return automock(PartnerRepository);
|
||||
}
|
||||
|
||||
case 'person': {
|
||||
return automock(PersonRepository);
|
||||
}
|
||||
|
||||
case 'session': {
|
||||
return automock(SessionRepository);
|
||||
}
|
||||
|
||||
case 'storage': {
|
||||
case StorageRepository: {
|
||||
return automock(StorageRepository, { args: [{ setContext: () => {} }] });
|
||||
}
|
||||
|
||||
case 'sync': {
|
||||
return automock(SyncRepository);
|
||||
}
|
||||
|
||||
case 'systemMetadata': {
|
||||
return automock(SystemMetadataRepository);
|
||||
}
|
||||
|
||||
case 'user': {
|
||||
return automock(UserRepository);
|
||||
}
|
||||
|
||||
case 'versionHistory': {
|
||||
return automock(VersionHistoryRepository);
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Invalid repository key: ${key}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const asDeps = (repositories: ServiceOverrides) => {
|
||||
return [
|
||||
repositories.logger || getRepositoryMock('logger'), // logger
|
||||
repositories.access, // access
|
||||
repositories.activity || getRepositoryMock('activity'),
|
||||
repositories.album || getRepositoryMock('album'),
|
||||
repositories.albumUser,
|
||||
repositories.apiKey,
|
||||
repositories.asset || getRepositoryMock('asset'),
|
||||
repositories.assetJob || getRepositoryMock('assetJob'),
|
||||
repositories.audit,
|
||||
repositories.config || getRepositoryMock('config'),
|
||||
repositories.cron,
|
||||
repositories.crypto || getRepositoryMock('crypto'),
|
||||
repositories.database || getRepositoryMock('database'),
|
||||
repositories.downloadRepository,
|
||||
repositories.duplicateRepository,
|
||||
repositories.email || getRepositoryMock('email'),
|
||||
repositories.event,
|
||||
repositories.job || getRepositoryMock('job'),
|
||||
repositories.library,
|
||||
repositories.machineLearning,
|
||||
repositories.map,
|
||||
repositories.media,
|
||||
repositories.memory || getRepositoryMock('memory'),
|
||||
repositories.metadata,
|
||||
repositories.move,
|
||||
repositories.notification || getRepositoryMock('notification'),
|
||||
repositories.oauth,
|
||||
repositories.partner || getRepositoryMock('partner'),
|
||||
repositories.person || getRepositoryMock('person'),
|
||||
repositories.process,
|
||||
repositories.search,
|
||||
repositories.serverInfo,
|
||||
repositories.session || getRepositoryMock('session'),
|
||||
repositories.sharedLink,
|
||||
repositories.stack,
|
||||
repositories.storage || getRepositoryMock('storage'),
|
||||
repositories.sync || getRepositoryMock('sync'),
|
||||
repositories.systemMetadata || getRepositoryMock('systemMetadata'),
|
||||
repositories.tag,
|
||||
repositories.telemetry,
|
||||
repositories.trash,
|
||||
repositories.user,
|
||||
repositories.versionHistory || getRepositoryMock('versionHistory'),
|
||||
repositories.view,
|
||||
];
|
||||
};
|
||||
|
||||
const assetInsert = (asset: Partial<Insertable<Assets>> = {}) => {
|
||||
const id = asset.id || newUuid();
|
||||
const now = newDate();
|
||||
@@ -544,6 +424,8 @@ const personInsert = (person: Partial<Insertable<Person>> & { ownerId: string })
|
||||
};
|
||||
};
|
||||
|
||||
const sha256 = (value: string) => createHash('sha256').update(value).digest('base64');
|
||||
|
||||
const sessionInsert = ({ id = newUuid(), userId, ...session }: Partial<Insertable<Sessions>> & { userId: string }) => {
|
||||
const defaults: Insertable<Sessions> = {
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user