mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
fix(server): Error when loading album with deleted owner (#4086)
* soft delete albums when user gets soft deleted * fix wrong intl openapi version * fix tests * ability to restore albums, automatically restore when user restored * (e2e) tests for shared albums via link and with user * (e2e) test deletion of users and linked albums * (e2e) fix share album with owner test * fix: deletedAt * chore: fix restore order * fix: use timezone date column * chore: cleanup e2e tests * (e2e) fix user delete test --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
parent
7d07aaeba3
commit
f1c98ac9e6
12 changed files with 149 additions and 21 deletions
|
@ -23,6 +23,8 @@ export interface IAlbumRepository {
|
||||||
getOwned(ownerId: string): Promise<AlbumEntity[]>;
|
getOwned(ownerId: string): Promise<AlbumEntity[]>;
|
||||||
getShared(ownerId: string): Promise<AlbumEntity[]>;
|
getShared(ownerId: string): Promise<AlbumEntity[]>;
|
||||||
getNotShared(ownerId: string): Promise<AlbumEntity[]>;
|
getNotShared(ownerId: string): Promise<AlbumEntity[]>;
|
||||||
|
restoreAll(userId: string): Promise<void>;
|
||||||
|
softDeleteAll(userId: string): Promise<void>;
|
||||||
deleteAll(userId: string): Promise<void>;
|
deleteAll(userId: string): Promise<void>;
|
||||||
getAll(): Promise<AlbumEntity[]>;
|
getAll(): Promise<AlbumEntity[]>;
|
||||||
create(album: Partial<AlbumEntity>): Promise<AlbumEntity>;
|
create(album: Partial<AlbumEntity>): Promise<AlbumEntity>;
|
||||||
|
|
|
@ -91,6 +91,7 @@ export class UserService {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new BadRequestException('User not found');
|
throw new BadRequestException('User not found');
|
||||||
}
|
}
|
||||||
|
await this.albumRepository.softDeleteAll(userId);
|
||||||
const deletedUser = await this.userCore.deleteUser(authUser, user);
|
const deletedUser = await this.userCore.deleteUser(authUser, user);
|
||||||
return mapUser(deletedUser);
|
return mapUser(deletedUser);
|
||||||
}
|
}
|
||||||
|
@ -101,6 +102,7 @@ export class UserService {
|
||||||
throw new BadRequestException('User not found');
|
throw new BadRequestException('User not found');
|
||||||
}
|
}
|
||||||
const updatedUser = await this.userCore.restoreUser(authUser, user);
|
const updatedUser = await this.userCore.restoreUser(authUser, user);
|
||||||
|
await this.albumRepository.restoreAll(userId);
|
||||||
return mapUser(updatedUser);
|
return mapUser(updatedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
DeleteDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
|
@ -36,6 +37,9 @@ export class AlbumEntity {
|
||||||
@UpdateDateColumn({ type: 'timestamptz' })
|
@UpdateDateColumn({ type: 'timestamptz' })
|
||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
|
|
||||||
|
@DeleteDateColumn({ type: 'timestamptz' })
|
||||||
|
deletedAt!: Date | null;
|
||||||
|
|
||||||
@ManyToOne(() => AssetEntity, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' })
|
@ManyToOne(() => AssetEntity, { nullable: true, onDelete: 'SET NULL', onUpdate: 'CASCADE' })
|
||||||
albumThumbnailAsset!: AssetEntity | null;
|
albumThumbnailAsset!: AssetEntity | null;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddDeletedAtToAlbums1694638413248 implements MigrationInterface {
|
||||||
|
name = 'AddDeletedAtToAlbums1694638413248';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "albums" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "deletedAt"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -142,6 +142,14 @@ export class AlbumRepository implements IAlbumRepository {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async restoreAll(userId: string): Promise<void> {
|
||||||
|
await this.repository.restore({ ownerId: userId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async softDeleteAll(userId: string): Promise<void> {
|
||||||
|
await this.repository.softDelete({ ownerId: userId });
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAll(userId: string): Promise<void> {
|
async deleteAll(userId: string): Promise<void> {
|
||||||
await this.repository.delete({ ownerId: userId });
|
await this.repository.delete({ ownerId: userId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ const user2NotShared = 'user2NotShared';
|
||||||
describe(`${AlbumController.name} (e2e)`, () => {
|
describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
let app: INestApplication;
|
let app: INestApplication;
|
||||||
let server: any;
|
let server: any;
|
||||||
|
let admin: LoginResponseDto;
|
||||||
let user1: LoginResponseDto;
|
let user1: LoginResponseDto;
|
||||||
let user1Asset: AssetFileUploadResponseDto;
|
let user1Asset: AssetFileUploadResponseDto;
|
||||||
let user1Albums: AlbumResponseDto[];
|
let user1Albums: AlbumResponseDto[];
|
||||||
|
@ -37,7 +38,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await db.reset();
|
await db.reset();
|
||||||
await api.authApi.adminSignUp(server);
|
await api.authApi.adminSignUp(server);
|
||||||
const admin = await api.authApi.adminLogin(server);
|
admin = await api.authApi.adminLogin(server);
|
||||||
|
|
||||||
await api.userApi.create(server, admin.accessToken, {
|
await api.userApi.create(server, admin.accessToken, {
|
||||||
email: 'user1@immich.app',
|
email: 'user1@immich.app',
|
||||||
|
@ -105,7 +106,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get('/album?shared=invalid')
|
.get('/album?shared=invalid')
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorStub.badRequest);
|
expect(body).toEqual(errorStub.badRequest);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -113,13 +114,28 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get('/album?assetId=invalid')
|
.get('/album?assetId=invalid')
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(errorStub.badRequest);
|
expect(body).toEqual(errorStub.badRequest);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not return shared albums with a deleted owner', async () => {
|
||||||
|
await api.userApi.delete(server, admin.accessToken, user1.userId);
|
||||||
|
const { status, body } = await request(server)
|
||||||
|
.get('/album?shared=true')
|
||||||
|
.set('Authorization', `Bearer ${user2.accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toHaveLength(1);
|
||||||
|
expect(body).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ ownerId: user2.userId, albumName: user2SharedLink, shared: true }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the album collection including owned and shared', async () => {
|
it('should return the album collection including owned and shared', async () => {
|
||||||
const { status, body } = await request(server).get('/album').set('Authorization', `Bearer ${user1.accessToken}`);
|
const { status, body } = await request(server).get('/album').set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(3);
|
expect(body).toHaveLength(3);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
|
@ -134,7 +150,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get('/album?shared=true')
|
.get('/album?shared=true')
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(3);
|
expect(body).toHaveLength(3);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
|
@ -149,7 +165,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get('/album?shared=false')
|
.get('/album?shared=false')
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(1);
|
expect(body).toHaveLength(1);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
|
@ -164,7 +180,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get(`/album?assetId=${asset.id}`)
|
.get(`/album?assetId=${asset.id}`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(1);
|
expect(body).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -172,7 +188,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get(`/album?shared=true&assetId=${user1Asset.id}`)
|
.get(`/album?shared=true&assetId=${user1Asset.id}`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(4);
|
expect(body).toHaveLength(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -180,7 +196,7 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
.get(`/album?shared=false&assetId=${user1Asset.id}`)
|
.get(`/album?shared=false&assetId=${user1Asset.id}`)
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
expect(status).toEqual(200);
|
expect(status).toBe(200);
|
||||||
expect(body).toHaveLength(4);
|
expect(body).toHaveLength(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -390,15 +406,15 @@ describe(`${AlbumController.name} (e2e)`, () => {
|
||||||
expect(body).toEqual(expect.objectContaining({ sharedUsers: [expect.objectContaining({ id: user2.userId })] }));
|
expect(body).toEqual(expect.objectContaining({ sharedUsers: [expect.objectContaining({ id: user2.userId })] }));
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('should not be able to share album with owner', async () => {
|
it('should not be able to share album with owner', async () => {
|
||||||
// const { status, body } = await request(server)
|
const { status, body } = await request(server)
|
||||||
// .put(`/album/${album.id}/users`)
|
.put(`/album/${album.id}/users`)
|
||||||
// .set('Authorization', `Bearer ${user1.accessToken}`)
|
.set('Authorization', `Bearer ${user1.accessToken}`)
|
||||||
// .send({ sharedUserIds: [user2.userId] });
|
.send({ sharedUserIds: [user1.userId] });
|
||||||
|
|
||||||
// expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
// expect(body).toEqual(errorStub.badRequest);
|
expect(body).toEqual({ ...errorStub.badRequest, message: 'Cannot be shared with owner' });
|
||||||
// });
|
});
|
||||||
|
|
||||||
it('should not be able to add existing user to shared album', async () => {
|
it('should not be able to add existing user to shared album', async () => {
|
||||||
await request(server)
|
await request(server)
|
||||||
|
|
|
@ -99,7 +99,21 @@ describe(`${PartnerController.name} (e2e)`, () => {
|
||||||
.query({ key: sharedLink.key + 'foo' });
|
.query({ key: sharedLink.key + 'foo' });
|
||||||
|
|
||||||
expect(status).toBe(401);
|
expect(status).toBe(401);
|
||||||
expect(body).toEqual(expect.objectContaining({ message: 'Invalid share key' }));
|
expect(body).toEqual(errorStub.invalidShareKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return unauthorized if target has been soft deleted', async () => {
|
||||||
|
const softDeletedAlbum = await api.albumApi.create(server, user1.accessToken, { albumName: 'shared with link' });
|
||||||
|
const softDeletedAlbumLink = await api.sharedLinkApi.create(server, user1.accessToken, {
|
||||||
|
type: SharedLinkType.ALBUM,
|
||||||
|
albumId: softDeletedAlbum.id,
|
||||||
|
});
|
||||||
|
await api.userApi.delete(server, accessToken, user1.userId);
|
||||||
|
|
||||||
|
const { status, body } = await request(server).get('/shared-link/me').query({ key: softDeletedAlbumLink.key });
|
||||||
|
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorStub.invalidShareKey);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
import { LoginResponseDto } from '@app/domain';
|
import { LoginResponseDto, UserResponseDto, UserService } from '@app/domain';
|
||||||
import { AppModule, UserController } from '@app/immich';
|
import { AppModule, UserController } from '@app/immich';
|
||||||
|
import { UserEntity } from '@app/infra/entities';
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from '@nestjs/common';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { api } from '@test/api';
|
import { api } from '@test/api';
|
||||||
import { db } from '@test/db';
|
import { db } from '@test/db';
|
||||||
import { errorStub, userSignupStub, userStub } from '@test/fixtures';
|
import { errorStub, userSignupStub, userStub } from '@test/fixtures';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
describe(`${UserController.name}`, () => {
|
describe(`${UserController.name}`, () => {
|
||||||
let app: INestApplication;
|
let app: INestApplication;
|
||||||
let server: any;
|
let server: any;
|
||||||
let loginResponse: LoginResponseDto;
|
let loginResponse: LoginResponseDto;
|
||||||
let accessToken: string;
|
let accessToken: string;
|
||||||
|
let userService: UserService;
|
||||||
|
let userRepository: Repository<UserEntity>;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
|
@ -19,6 +23,7 @@ describe(`${UserController.name}`, () => {
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
app = await moduleFixture.createNestApplication().init();
|
app = await moduleFixture.createNestApplication().init();
|
||||||
|
userRepository = moduleFixture.get('UserEntityRepository');
|
||||||
server = app.getHttpServer();
|
server = app.getHttpServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,6 +32,8 @@ describe(`${UserController.name}`, () => {
|
||||||
await api.authApi.adminSignUp(server);
|
await api.authApi.adminSignUp(server);
|
||||||
loginResponse = await api.authApi.adminLogin(server);
|
loginResponse = await api.authApi.adminLogin(server);
|
||||||
accessToken = loginResponse.accessToken;
|
accessToken = loginResponse.accessToken;
|
||||||
|
|
||||||
|
userService = app.get<UserService>(UserService);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
|
@ -173,6 +180,50 @@ describe(`${UserController.name}`, () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('DELETE /user/:id', () => {
|
||||||
|
let userToDelete: UserResponseDto;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
userToDelete = await api.userApi.create(server, accessToken, {
|
||||||
|
email: userStub.user1.email,
|
||||||
|
firstName: userStub.user1.firstName,
|
||||||
|
lastName: userStub.user1.lastName,
|
||||||
|
password: 'superSecurePassword',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require authentication', async () => {
|
||||||
|
const { status, body } = await request(server).delete(`/user/${userToDelete.id}`);
|
||||||
|
expect(status).toBe(401);
|
||||||
|
expect(body).toEqual(errorStub.unauthorized);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete user', async () => {
|
||||||
|
const deleteRequest = await request(server)
|
||||||
|
.delete(`/user/${userToDelete.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
|
||||||
|
expect(deleteRequest.status).toBe(200);
|
||||||
|
expect(deleteRequest.body).toEqual({
|
||||||
|
...userToDelete,
|
||||||
|
updatedAt: expect.any(String),
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
});
|
||||||
|
|
||||||
|
await userRepository.save({ id: deleteRequest.body.id, deletedAt: new Date('1970-01-01').toISOString() });
|
||||||
|
|
||||||
|
await userService.handleUserDelete({ id: userToDelete.id });
|
||||||
|
|
||||||
|
const { status, body } = await request(server)
|
||||||
|
.get('/user')
|
||||||
|
.query({ isAll: false })
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
|
||||||
|
expect(status).toBe(200);
|
||||||
|
expect(body).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('PUT /user', () => {
|
describe('PUT /user', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(server).put(`/user`);
|
const { status, body } = await request(server).put(`/user`);
|
||||||
|
@ -237,9 +288,9 @@ describe(`${UserController.name}`, () => {
|
||||||
lastName: 'Last Name',
|
lastName: 'Last Name',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(after).toMatchObject({
|
expect(after).toEqual({
|
||||||
...before,
|
...before,
|
||||||
updatedAt: expect.anything(),
|
updatedAt: expect.any(String),
|
||||||
firstName: 'First Name',
|
firstName: 'First Name',
|
||||||
lastName: 'Last Name',
|
lastName: 'Last Name',
|
||||||
});
|
});
|
||||||
|
|
10
server/test/fixtures/album.stub.ts
vendored
10
server/test/fixtures/album.stub.ts
vendored
|
@ -15,6 +15,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -29,6 +30,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.user1],
|
sharedUsers: [userStub.user1],
|
||||||
}),
|
}),
|
||||||
|
@ -43,6 +45,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.user1, userStub.user2],
|
sharedUsers: [userStub.user1, userStub.user2],
|
||||||
}),
|
}),
|
||||||
|
@ -57,6 +60,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [userStub.admin],
|
sharedUsers: [userStub.admin],
|
||||||
}),
|
}),
|
||||||
|
@ -71,6 +75,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -85,6 +90,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: assetStub.image.id,
|
albumThumbnailAssetId: assetStub.image.id,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -99,6 +105,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: assetStub.image.id,
|
albumThumbnailAssetId: assetStub.image.id,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -113,6 +120,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -127,6 +135,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: assetStub.livePhotoMotionAsset.id,
|
albumThumbnailAssetId: assetStub.livePhotoMotionAsset.id,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
@ -141,6 +150,7 @@ export const albumStub = {
|
||||||
albumThumbnailAssetId: assetStub.image.id,
|
albumThumbnailAssetId: assetStub.image.id,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
deletedAt: null,
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
}),
|
}),
|
||||||
|
|
5
server/test/fixtures/error.stub.ts
vendored
5
server/test/fixtures/error.stub.ts
vendored
|
@ -19,6 +19,11 @@ export const errorStub = {
|
||||||
statusCode: 401,
|
statusCode: 401,
|
||||||
message: 'Invalid user token',
|
message: 'Invalid user token',
|
||||||
},
|
},
|
||||||
|
invalidShareKey: {
|
||||||
|
error: 'Unauthorized',
|
||||||
|
statusCode: 401,
|
||||||
|
message: 'Invalid share key',
|
||||||
|
},
|
||||||
badRequest: {
|
badRequest: {
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
|
1
server/test/fixtures/shared-link.stub.ts
vendored
1
server/test/fixtures/shared-link.stub.ts
vendored
|
@ -151,6 +151,7 @@ export const sharedLinkStub = {
|
||||||
description: '',
|
description: '',
|
||||||
createdAt: today,
|
createdAt: today,
|
||||||
updatedAt: today,
|
updatedAt: today,
|
||||||
|
deletedAt: null,
|
||||||
albumThumbnailAsset: null,
|
albumThumbnailAsset: null,
|
||||||
albumThumbnailAssetId: null,
|
albumThumbnailAssetId: null,
|
||||||
sharedUsers: [],
|
sharedUsers: [],
|
||||||
|
|
|
@ -10,6 +10,8 @@ export const newAlbumRepositoryMock = (): jest.Mocked<IAlbumRepository> => {
|
||||||
getOwned: jest.fn(),
|
getOwned: jest.fn(),
|
||||||
getShared: jest.fn(),
|
getShared: jest.fn(),
|
||||||
getNotShared: jest.fn(),
|
getNotShared: jest.fn(),
|
||||||
|
restoreAll: jest.fn(),
|
||||||
|
softDeleteAll: jest.fn(),
|
||||||
deleteAll: jest.fn(),
|
deleteAll: jest.fn(),
|
||||||
getAll: jest.fn(),
|
getAll: jest.fn(),
|
||||||
removeAsset: jest.fn(),
|
removeAsset: jest.fn(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue