refactor(server): e2e (#7265)

* refactor: activity e2e

* refactor: person e2e

* refactor: shared link e2e
This commit is contained in:
Jason Rasmussen 2024-02-21 08:28:03 -05:00 committed by GitHub
parent a1bc74cdd6
commit f798e037d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 573 additions and 459 deletions

View file

@ -1,402 +0,0 @@
import { AlbumResponseDto, LoginResponseDto, ReactionType } from '@app/domain';
import { ActivityController } from '@app/immich';
import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto';
import { ActivityEntity } from '@app/infra/entities';
import { errorStub, userDto, uuidStub } from '@test/fixtures';
import request from 'supertest';
import { api } from '../../client';
import { testApp } from '../utils';
describe(`${ActivityController.name} (e2e)`, () => {
let server: any;
let admin: LoginResponseDto;
let asset: AssetFileUploadResponseDto;
let album: AlbumResponseDto;
let nonOwner: LoginResponseDto;
beforeAll(async () => {
server = (await testApp.create()).getHttpServer();
await testApp.reset();
await api.authApi.adminSignUp(server);
admin = await api.authApi.adminLogin(server);
asset = await api.assetApi.upload(server, admin.accessToken, 'example');
await api.userApi.create(server, admin.accessToken, userDto.user1);
nonOwner = await api.authApi.login(server, userDto.user1);
album = await api.albumApi.create(server, admin.accessToken, {
albumName: 'Album 1',
assetIds: [asset.id],
sharedWithUserIds: [nonOwner.userId],
});
});
afterAll(async () => {
await testApp.teardown();
});
beforeEach(async () => {
await testApp.reset({ entities: [ActivityEntity] });
});
describe('GET /activity', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).get('/activity');
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should require an albumId', async () => {
const { status, body } = await request(server)
.get('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400);
expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
});
it('should reject an invalid albumId', async () => {
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: uuidStub.invalid })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400);
expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
});
it('should reject an invalid assetId', async () => {
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: uuidStub.notFound, assetId: uuidStub.invalid })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400);
expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['assetId must be a UUID'])));
});
it('should start off empty', async () => {
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: album.id })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toEqual([]);
expect(status).toEqual(200);
});
it('should filter by album id', async () => {
const album2 = await api.albumApi.create(server, admin.accessToken, {
albumName: 'Album 2',
assetIds: [asset.id],
});
const [reaction] = await Promise.all([
api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.LIKE,
}),
api.activityApi.create(server, admin.accessToken, {
albumId: album2.id,
type: ReactionType.LIKE,
}),
]);
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: album.id })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200);
expect(body.length).toBe(1);
expect(body[0]).toEqual(reaction);
});
it('should filter by type=comment', async () => {
const [reaction] = await Promise.all([
api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'comment',
}),
api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }),
]);
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: album.id, type: 'comment' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200);
expect(body.length).toBe(1);
expect(body[0]).toEqual(reaction);
});
it('should filter by type=like', async () => {
const [reaction] = await Promise.all([
api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }),
api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'comment',
}),
]);
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: album.id, type: 'like' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200);
expect(body.length).toBe(1);
expect(body[0]).toEqual(reaction);
});
it('should filter by userId', async () => {
const [reaction] = await Promise.all([
api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }),
]);
const response1 = await request(server)
.get('/activity')
.query({ albumId: album.id, userId: uuidStub.notFound })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(response1.status).toEqual(200);
expect(response1.body.length).toBe(0);
const response2 = await request(server)
.get('/activity')
.query({ albumId: album.id, userId: admin.userId })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(response2.status).toEqual(200);
expect(response2.body.length).toBe(1);
expect(response2.body[0]).toEqual(reaction);
});
it('should filter by assetId', async () => {
const [reaction] = await Promise.all([
api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
assetId: asset.id,
type: ReactionType.LIKE,
}),
api.activityApi.create(server, admin.accessToken, { albumId: album.id, type: ReactionType.LIKE }),
]);
const { status, body } = await request(server)
.get('/activity')
.query({ albumId: album.id, assetId: asset.id })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200);
expect(body.length).toBe(1);
expect(body[0]).toEqual(reaction);
});
});
describe('POST /activity', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).post('/activity');
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should require an albumId', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: uuidStub.invalid });
expect(status).toEqual(400);
expect(body).toEqual(errorStub.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
});
it('should require a comment when type is comment', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: uuidStub.notFound, type: 'comment', comment: null });
expect(status).toEqual(400);
expect(body).toEqual(errorStub.badRequest(['comment must be a string', 'comment should not be empty']));
});
it('should add a comment to an album', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'comment', comment: 'This is my first comment' });
expect(status).toEqual(201);
expect(body).toEqual({
id: expect.any(String),
assetId: null,
createdAt: expect.any(String),
type: 'comment',
comment: 'This is my first comment',
user: expect.objectContaining({ email: admin.userEmail }),
});
});
it('should add a like to an album', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' });
expect(status).toEqual(201);
expect(body).toEqual({
id: expect.any(String),
assetId: null,
createdAt: expect.any(String),
type: 'like',
comment: null,
user: expect.objectContaining({ email: admin.userEmail }),
});
});
it('should return a 200 for a duplicate like on the album', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.LIKE,
});
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' });
expect(status).toEqual(200);
expect(body).toEqual(reaction);
});
it('should not confuse an album like with an asset like', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
assetId: asset.id,
type: ReactionType.LIKE,
});
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' });
expect(status).toEqual(201);
expect(body.id).not.toEqual(reaction.id);
});
it('should add a comment to an asset', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, assetId: asset.id, type: 'comment', comment: 'This is my first comment' });
expect(status).toEqual(201);
expect(body).toEqual({
id: expect.any(String),
assetId: asset.id,
createdAt: expect.any(String),
type: 'comment',
comment: 'This is my first comment',
user: expect.objectContaining({ email: admin.userEmail }),
});
});
it('should add a like to an asset', async () => {
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, assetId: asset.id, type: 'like' });
expect(status).toEqual(201);
expect(body).toEqual({
id: expect.any(String),
assetId: asset.id,
createdAt: expect.any(String),
type: 'like',
comment: null,
user: expect.objectContaining({ email: admin.userEmail }),
});
});
it('should return a 200 for a duplicate like on an asset', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
assetId: asset.id,
type: ReactionType.LIKE,
});
const { status, body } = await request(server)
.post('/activity')
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, assetId: asset.id, type: 'like' });
expect(status).toEqual(200);
expect(body).toEqual(reaction);
});
});
describe('DELETE /activity/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).delete(`/activity/${uuidStub.notFound}`);
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should require a valid uuid', async () => {
const { status, body } = await request(server)
.delete(`/activity/${uuidStub.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest(['id must be a UUID']));
});
it('should remove a comment from an album', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'This is a test comment',
});
const { status } = await request(server)
.delete(`/activity/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204);
});
it('should remove a like from an album', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.LIKE,
});
const { status } = await request(server)
.delete(`/activity/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204);
});
it('should let the owner remove a comment by another user', async () => {
const reaction = await api.activityApi.create(server, nonOwner.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'This is a test comment',
});
const { status } = await request(server)
.delete(`/activity/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204);
});
it('should not let a user remove a comment by another user', async () => {
const reaction = await api.activityApi.create(server, admin.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'This is a test comment',
});
const { status, body } = await request(server)
.delete(`/activity/${reaction.id}`)
.set('Authorization', `Bearer ${nonOwner.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest('Not found or no activity.delete access'));
});
it('should let a non-owner remove their own comment', async () => {
const reaction = await api.activityApi.create(server, nonOwner.accessToken, {
albumId: album.id,
type: ReactionType.COMMENT,
comment: 'This is a test comment',
});
const { status } = await request(server)
.delete(`/activity/${reaction.id}`)
.set('Authorization', `Bearer ${nonOwner.accessToken}`);
expect(status).toBe(204);
});
});
});

View file

@ -1,191 +0,0 @@
import { IPersonRepository, LoginResponseDto } from '@app/domain';
import { PersonController } from '@app/immich';
import { PersonEntity } from '@app/infra/entities';
import { INestApplication } from '@nestjs/common';
import { errorStub, uuidStub } from '@test/fixtures';
import request from 'supertest';
import { api } from '../../client';
import { testApp } from '../utils';
describe(`${PersonController.name}`, () => {
let app: INestApplication;
let server: any;
let loginResponse: LoginResponseDto;
let accessToken: string;
let personRepository: IPersonRepository;
let visiblePerson: PersonEntity;
let hiddenPerson: PersonEntity;
beforeAll(async () => {
app = await testApp.create();
server = app.getHttpServer();
personRepository = app.get<IPersonRepository>(IPersonRepository);
});
afterAll(async () => {
await testApp.teardown();
});
beforeEach(async () => {
await testApp.reset();
await api.authApi.adminSignUp(server);
loginResponse = await api.authApi.adminLogin(server);
accessToken = loginResponse.accessToken;
const faceAsset = await api.assetApi.upload(server, accessToken, 'face_asset');
visiblePerson = await personRepository.create({
ownerId: loginResponse.userId,
name: 'visible_person',
thumbnailPath: '/thumbnail/face_asset',
});
await personRepository.createFaces([
{
assetId: faceAsset.id,
personId: visiblePerson.id,
embedding: Array.from({ length: 512 }, Math.random),
},
]);
hiddenPerson = await personRepository.create({
ownerId: loginResponse.userId,
name: 'hidden_person',
isHidden: true,
thumbnailPath: '/thumbnail/face_asset',
});
await personRepository.createFaces([
{
assetId: faceAsset.id,
personId: hiddenPerson.id,
embedding: Array.from({ length: 512 }, Math.random),
},
]);
});
describe('GET /person', () => {
beforeEach(async () => {});
it('should require authentication', async () => {
const { status, body } = await request(server).get('/person');
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should return all people (including hidden)', async () => {
const { status, body } = await request(server)
.get('/person')
.set('Authorization', `Bearer ${accessToken}`)
.query({ withHidden: true });
expect(status).toBe(200);
expect(body).toEqual({
total: 2,
people: [
expect.objectContaining({ name: 'visible_person' }),
expect.objectContaining({ name: 'hidden_person' }),
],
});
});
it('should return only visible people', async () => {
const { status, body } = await request(server).get('/person').set('Authorization', `Bearer ${accessToken}`);
expect(status).toBe(200);
expect(body).toEqual({
total: 2,
people: [expect.objectContaining({ name: 'visible_person' })],
});
});
});
describe('GET /person/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).get(`/person/${uuidStub.notFound}`);
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should throw error if person with id does not exist', async () => {
const { status, body } = await request(server)
.get(`/person/${uuidStub.notFound}`)
.set('Authorization', `Bearer ${accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest());
});
it('should return person information', async () => {
const { status, body } = await request(server)
.get(`/person/${visiblePerson.id}`)
.set('Authorization', `Bearer ${accessToken}`);
expect(status).toBe(200);
expect(body).toEqual(expect.objectContaining({ id: visiblePerson.id }));
});
});
describe('PUT /person/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).put(`/person/${uuidStub.notFound}`);
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
for (const { key, type } of [
{ key: 'name', type: 'string' },
{ key: 'featureFaceAssetId', type: 'string' },
{ key: 'isHidden', type: 'boolean value' },
]) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(server)
.put(`/person/${visiblePerson.id}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({ [key]: null });
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest([`${key} must be a ${type}`]));
});
}
it('should not accept invalid birth dates', async () => {
for (const { birthDate, response } of [
{ birthDate: false, response: 'Not found or no person.write access' },
{ birthDate: 'false', response: ['birthDate must be a Date instance'] },
{ birthDate: '123567', response: 'Not found or no person.write access' },
{ birthDate: 123567, response: 'Not found or no person.write access' },
]) {
const { status, body } = await request(server)
.put(`/person/${uuidStub.notFound}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({ birthDate });
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest(response));
}
});
it('should update a date of birth', async () => {
const { status, body } = await request(server)
.put(`/person/${visiblePerson.id}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({ birthDate: '1990-01-01T05:00:00.000Z' });
expect(status).toBe(200);
expect(body).toMatchObject({ birthDate: '1990-01-01' });
});
it('should clear a date of birth', async () => {
const person = await personRepository.create({
birthDate: new Date('1990-01-01'),
ownerId: loginResponse.userId,
});
expect(person.birthDate).toBeDefined();
const { status, body } = await request(server)
.put(`/person/${person.id}`)
.set('Authorization', `Bearer ${accessToken}`)
.send({ birthDate: null });
expect(status).toBe(200);
expect(body).toMatchObject({ birthDate: null });
});
});
});

View file

@ -1,422 +0,0 @@
import {
AlbumResponseDto,
AssetResponseDto,
IAssetRepository,
LoginResponseDto,
SharedLinkResponseDto,
} from '@app/domain';
import { SharedLinkController } from '@app/immich';
import { SharedLinkType } from '@app/infra/entities';
import { INestApplication } from '@nestjs/common';
import { errorStub, userDto, uuidStub } from '@test/fixtures';
import { DateTime } from 'luxon';
import request from 'supertest';
import { api } from '../../client';
import { testApp } from '../utils';
describe(`${SharedLinkController.name} (e2e)`, () => {
let server: any;
let admin: LoginResponseDto;
let asset1: AssetResponseDto;
let asset2: AssetResponseDto;
let user1: LoginResponseDto;
let user2: LoginResponseDto;
let album: AlbumResponseDto;
let metadataAlbum: AlbumResponseDto;
let deletedAlbum: AlbumResponseDto;
let linkWithDeletedAlbum: SharedLinkResponseDto;
let linkWithPassword: SharedLinkResponseDto;
let linkWithAlbum: SharedLinkResponseDto;
let linkWithAssets: SharedLinkResponseDto;
let linkWithMetadata: SharedLinkResponseDto;
let linkWithoutMetadata: SharedLinkResponseDto;
let app: INestApplication<any>;
beforeAll(async () => {
app = await testApp.create();
server = app.getHttpServer();
const assetRepository = app.get<IAssetRepository>(IAssetRepository);
await testApp.reset();
await api.authApi.adminSignUp(server);
admin = await api.authApi.adminLogin(server);
await Promise.all([
api.userApi.create(server, admin.accessToken, userDto.user1),
api.userApi.create(server, admin.accessToken, userDto.user2),
]);
[user1, user2] = await Promise.all([
api.authApi.login(server, userDto.user1),
api.authApi.login(server, userDto.user2),
]);
[asset1, asset2] = await Promise.all([
api.assetApi.create(server, user1.accessToken),
api.assetApi.create(server, user1.accessToken),
]);
await assetRepository.upsertExif({
assetId: asset1.id,
longitude: -108.400968333333,
latitude: 39.115,
orientation: '1',
dateTimeOriginal: DateTime.fromISO('2022-01-10T19:15:44.310Z').toJSDate(),
timeZone: 'UTC-4',
state: 'Mesa County, Colorado',
country: 'United States of America',
});
[album, deletedAlbum, metadataAlbum] = await Promise.all([
api.albumApi.create(server, user1.accessToken, { albumName: 'album' }),
api.albumApi.create(server, user2.accessToken, { albumName: 'deleted album' }),
api.albumApi.create(server, user1.accessToken, { albumName: 'metadata album', assetIds: [asset1.id] }),
]);
[linkWithDeletedAlbum, linkWithAlbum, linkWithAssets, linkWithPassword, linkWithMetadata, linkWithoutMetadata] =
await Promise.all([
api.sharedLinkApi.create(server, user2.accessToken, {
type: SharedLinkType.ALBUM,
albumId: deletedAlbum.id,
}),
api.sharedLinkApi.create(server, user1.accessToken, {
type: SharedLinkType.ALBUM,
albumId: album.id,
}),
api.sharedLinkApi.create(server, user1.accessToken, {
type: SharedLinkType.INDIVIDUAL,
assetIds: [asset1.id],
}),
api.sharedLinkApi.create(server, user1.accessToken, {
type: SharedLinkType.ALBUM,
albumId: album.id,
password: 'foo',
}),
api.sharedLinkApi.create(server, user1.accessToken, {
type: SharedLinkType.ALBUM,
albumId: metadataAlbum.id,
showMetadata: true,
}),
api.sharedLinkApi.create(server, user1.accessToken, {
type: SharedLinkType.ALBUM,
albumId: metadataAlbum.id,
showMetadata: false,
}),
]);
await api.userApi.delete(server, admin.accessToken, user2.userId);
});
afterAll(async () => {
await testApp.teardown();
});
describe('GET /shared-link', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).get('/shared-link');
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should get all shared links created by user', async () => {
const { status, body } = await request(server)
.get('/shared-link')
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(5);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: linkWithAlbum.id }),
expect.objectContaining({ id: linkWithAssets.id }),
expect.objectContaining({ id: linkWithPassword.id }),
expect.objectContaining({ id: linkWithMetadata.id }),
expect.objectContaining({ id: linkWithoutMetadata.id }),
]),
);
});
it('should not get shared links created by other users', async () => {
const { status, body } = await request(server)
.get('/shared-link')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual([]);
});
});
describe('GET /shared-link/me', () => {
it('should not require admin authentication', async () => {
const { status } = await request(server)
.get('/shared-link/me')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(403);
});
it('should get data for correct shared link', async () => {
const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithAlbum.key });
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({
album,
userId: user1.userId,
type: SharedLinkType.ALBUM,
}),
);
});
it('should return unauthorized for incorrect shared link', async () => {
const { status, body } = await request(server)
.get('/shared-link/me')
.query({ key: linkWithAlbum.key + 'foo' });
expect(status).toBe(401);
expect(body).toEqual(errorStub.invalidShareKey);
});
it('should return unauthorized if target has been soft deleted', async () => {
const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithDeletedAlbum.key });
expect(status).toBe(401);
expect(body).toEqual(errorStub.invalidShareKey);
});
it('should return unauthorized for password protected link', async () => {
const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithPassword.key });
expect(status).toBe(401);
expect(body).toEqual(errorStub.invalidSharePassword);
});
it('should get data for correct password protected link', async () => {
const { status, body } = await request(server)
.get('/shared-link/me')
.query({ key: linkWithPassword.key, password: 'foo' });
expect(status).toBe(200);
expect(body).toEqual(expect.objectContaining({ album, userId: user1.userId, type: SharedLinkType.ALBUM }));
});
it('should return metadata for album shared link', async () => {
const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithMetadata.key });
expect(status).toBe(200);
expect(body.assets).toHaveLength(1);
expect(body.assets[0]).toEqual(
expect.objectContaining({
originalFileName: 'example',
localDateTime: expect.any(String),
fileCreatedAt: expect.any(String),
exifInfo: expect.objectContaining({
longitude: -108.400968333333,
latitude: 39.115,
orientation: '1',
dateTimeOriginal: expect.any(String),
timeZone: 'UTC-4',
state: 'Mesa County, Colorado',
country: 'United States of America',
}),
}),
);
expect(body.album).toBeDefined();
});
it('should not return metadata for album shared link without metadata', async () => {
const { status, body } = await request(server).get('/shared-link/me').query({ key: linkWithoutMetadata.key });
expect(status).toBe(200);
expect(body.assets).toHaveLength(1);
expect(body.album).toBeDefined();
const asset = body.assets[0];
expect(asset).not.toHaveProperty('exifInfo');
expect(asset).not.toHaveProperty('fileCreatedAt');
expect(asset).not.toHaveProperty('originalFilename');
expect(asset).not.toHaveProperty('originalPath');
});
});
describe('GET /shared-link/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).get(`/shared-link/${linkWithAlbum.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should get shared link by id', async () => {
const { status, body } = await request(server)
.get(`/shared-link/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual(expect.objectContaining({ album, userId: user1.userId, type: SharedLinkType.ALBUM }));
});
it('should not get shared link by id if user has not created the link or it does not exist', async () => {
const { status, body } = await request(server)
.get(`/shared-link/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(expect.objectContaining({ message: 'Shared link not found' }));
});
});
describe('POST /shared-link', () => {
it('should require authentication', async () => {
const { status, body } = await request(server)
.post('/shared-link')
.send({ type: SharedLinkType.ALBUM, albumId: uuidStub.notFound });
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should require a type and the correspondent asset/album id', async () => {
const { status, body } = await request(server)
.post('/shared-link')
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest());
});
it('should require an asset/album id', async () => {
const { status, body } = await request(server)
.post('/shared-link')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.ALBUM });
expect(status).toBe(400);
expect(body).toEqual(expect.objectContaining({ message: 'Invalid albumId' }));
});
it('should require a valid asset id', async () => {
const { status, body } = await request(server)
.post('/shared-link')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.INDIVIDUAL, assetId: uuidStub.notFound });
expect(status).toBe(400);
expect(body).toEqual(expect.objectContaining({ message: 'Invalid assetIds' }));
});
it('should create a shared link', async () => {
const { status, body } = await request(server)
.post('/shared-link')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.ALBUM, albumId: album.id });
expect(status).toBe(201);
expect(body).toEqual(expect.objectContaining({ type: SharedLinkType.ALBUM, userId: user1.userId }));
});
});
describe('PATCH /shared-link/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server)
.patch(`/shared-link/${linkWithAlbum.id}`)
.send({ description: 'foo' });
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should fail if invalid link', async () => {
const { status, body } = await request(server)
.patch(`/shared-link/${uuidStub.notFound}`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ description: 'foo' });
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest());
});
it('should update shared link', async () => {
const { status, body } = await request(server)
.patch(`/shared-link/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ description: 'foo' });
expect(status).toBe(200);
expect(body).toEqual(
expect.objectContaining({ type: SharedLinkType.ALBUM, userId: user1.userId, description: 'foo' }),
);
});
});
describe('PUT /shared-link/:id/assets', () => {
it('should not add assets to shared link (album)', async () => {
const { status, body } = await request(server)
.put(`/shared-link/${linkWithAlbum.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] });
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest('Invalid shared link type'));
});
it('should add an assets to a shared link (individual)', async () => {
const { status, body } = await request(server)
.put(`/shared-link/${linkWithAssets.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] });
expect(body).toEqual([{ assetId: asset2.id, success: true }]);
expect(status).toBe(200);
});
});
describe('DELETE /shared-link/:id/assets', () => {
it('should not remove assets from a shared link (album)', async () => {
const { status, body } = await request(server)
.delete(`/shared-link/${linkWithAlbum.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] });
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest('Invalid shared link type'));
});
it('should remove assets from a shared link (individual)', async () => {
const { status, body } = await request(server)
.delete(`/shared-link/${linkWithAssets.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] });
expect(body).toEqual([{ assetId: asset2.id, success: true }]);
expect(status).toBe(200);
});
});
describe('DELETE /shared-link/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(server).delete(`/shared-link/${linkWithAlbum.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorStub.unauthorized);
});
it('should fail if invalid link', async () => {
const { status, body } = await request(server)
.delete(`/shared-link/${uuidStub.notFound}`)
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorStub.badRequest());
});
it('should delete a shared link', async () => {
const { status } = await request(server)
.delete(`/shared-link/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200);
});
});
});