refactor: more database types (#17490)

This commit is contained in:
Jason Rasmussen
2025-04-09 10:24:38 -04:00
committed by GitHub
parent 04b03f2924
commit 8943ec23ba
12 changed files with 123 additions and 148 deletions

View File

@@ -1,10 +1,10 @@
import { BadRequestException, ForbiddenException, UnauthorizedException } from '@nestjs/common';
import { DateTime } from 'luxon';
import { AuthDto, SignUpDto } from 'src/dtos/auth.dto';
import { UserMetadataEntity } from 'src/entities/user-metadata.entity';
import { UserEntity } from 'src/entities/user.entity';
import { AuthType, Permission } from 'src/enum';
import { AuthService } from 'src/services/auth.service';
import { sessionStub } from 'test/fixtures/session.stub';
import { sharedLinkStub } from 'test/fixtures/shared-link.stub';
import { systemConfigStub } from 'test/fixtures/system-config.stub';
import { userStub } from 'test/fixtures/user.stub';
@@ -97,17 +97,19 @@ describe('AuthService', () => {
});
it('should successfully log the user in', async () => {
mocks.user.getByEmail.mockResolvedValue(userStub.user1);
mocks.session.create.mockResolvedValue(sessionStub.valid);
const user = { ...factory.user(), password: 'immich_password' } as UserEntity;
const session = factory.session();
mocks.user.getByEmail.mockResolvedValue(user);
mocks.session.create.mockResolvedValue(session);
await expect(sut.login(fixtures.login, loginDetails)).resolves.toEqual({
accessToken: 'cmFuZG9tLWJ5dGVz',
userId: 'user-id',
userEmail: 'immich@test.com',
name: 'immich_name',
profileImagePath: '',
isAdmin: false,
shouldChangePassword: false,
userId: user.id,
userEmail: user.email,
name: user.name,
profileImagePath: user.profileImagePath,
isAdmin: user.isAdmin,
shouldChangePassword: user.shouldChangePassword,
});
expect(mocks.user.getByEmail).toHaveBeenCalledTimes(1);
@@ -256,8 +258,14 @@ describe('AuthService', () => {
});
it('should validate using authorization header', async () => {
mocks.user.get.mockResolvedValue(userStub.user1);
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
const session = factory.session();
const sessionWithToken = {
id: session.id,
updatedAt: session.updatedAt,
user: factory.authUser(),
};
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
await expect(
sut.authenticate({
@@ -266,8 +274,8 @@ describe('AuthService', () => {
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
}),
).resolves.toEqual({
user: userStub.user1,
session: sessionStub.valid,
user: sessionWithToken.user,
session: { id: session.id },
});
});
});
@@ -371,7 +379,14 @@ describe('AuthService', () => {
});
it('should return an auth dto', async () => {
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
const session = factory.session();
const sessionWithToken = {
id: session.id,
updatedAt: session.updatedAt,
user: factory.authUser(),
};
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
await expect(
sut.authenticate({
@@ -380,13 +395,20 @@ describe('AuthService', () => {
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
}),
).resolves.toEqual({
user: userStub.user1,
session: sessionStub.valid,
user: sessionWithToken.user,
session: { id: session.id },
});
});
it('should throw if admin route and not an admin', async () => {
mocks.session.getByToken.mockResolvedValue(sessionStub.valid as any);
const session = factory.session();
const sessionWithToken = {
id: session.id,
updatedAt: session.updatedAt,
user: factory.authUser(),
};
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
await expect(
sut.authenticate({
@@ -398,8 +420,15 @@ describe('AuthService', () => {
});
it('should update when access time exceeds an hour', async () => {
mocks.session.getByToken.mockResolvedValue(sessionStub.inactive as any);
mocks.session.update.mockResolvedValue(sessionStub.valid);
const session = factory.session({ updatedAt: DateTime.now().minus({ hours: 2 }).toJSDate() });
const sessionWithToken = {
id: session.id,
updatedAt: session.updatedAt,
user: factory.authUser(),
};
mocks.session.getByToken.mockResolvedValue(sessionWithToken);
mocks.session.update.mockResolvedValue(session);
await expect(
sut.authenticate({
@@ -408,7 +437,8 @@ describe('AuthService', () => {
metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' },
}),
).resolves.toBeDefined();
expect(mocks.session.update.mock.calls[0][1]).toMatchObject({ id: 'not_active', updatedAt: expect.any(Date) });
expect(mocks.session.update).toHaveBeenCalled();
});
});
@@ -506,7 +536,7 @@ describe('AuthService', () => {
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthEnabled);
mocks.user.getByEmail.mockResolvedValue(userStub.user1);
mocks.user.update.mockResolvedValue(userStub.user1);
mocks.session.create.mockResolvedValue(sessionStub.valid);
mocks.session.create.mockResolvedValue(factory.session());
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
oauthResponse,
@@ -535,7 +565,7 @@ describe('AuthService', () => {
mocks.user.getByEmail.mockResolvedValue(void 0);
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
mocks.user.create.mockResolvedValue(userStub.user1);
mocks.session.create.mockResolvedValue(sessionStub.valid);
mocks.session.create.mockResolvedValue(factory.session());
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).resolves.toEqual(
oauthResponse,
@@ -550,7 +580,7 @@ describe('AuthService', () => {
mocks.user.getByEmail.mockResolvedValue(void 0);
mocks.user.getAdmin.mockResolvedValue(userStub.user1);
mocks.user.create.mockResolvedValue(userStub.user1);
mocks.session.create.mockResolvedValue(sessionStub.valid);
mocks.session.create.mockResolvedValue(factory.session());
mocks.oauth.getProfile.mockResolvedValue({ sub, email: undefined });
await expect(sut.callback({ url: 'http://immich/auth/login?code=abc123' }, loginDetails)).rejects.toBeInstanceOf(
@@ -572,7 +602,7 @@ describe('AuthService', () => {
it(`should use the mobile redirect override for a url of ${url}`, async () => {
mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.oauthWithMobileOverride);
mocks.user.getByOAuthId.mockResolvedValue(userStub.user1);
mocks.session.create.mockResolvedValue(sessionStub.valid);
mocks.session.create.mockResolvedValue(factory.session());
await sut.callback({ url }, loginDetails);

View File

@@ -338,7 +338,9 @@ export class AuthService extends BaseService {
return {
user: session.user,
session,
session: {
id: session.id,
},
};
}

View File

@@ -1,7 +1,7 @@
import { JobStatus } from 'src/enum';
import { SessionService } from 'src/services/session.service';
import { authStub } from 'test/fixtures/auth.stub';
import { sessionStub } from 'test/fixtures/session.stub';
import { factory } from 'test/small.factory';
import { newTestService, ServiceMocks } from 'test/utils';
describe('SessionService', () => {
@@ -45,40 +45,35 @@ describe('SessionService', () => {
describe('getAll', () => {
it('should get the devices', async () => {
mocks.session.getByUserId.mockResolvedValue([sessionStub.valid as any, sessionStub.inactive]);
await expect(sut.getAll(authStub.user1)).resolves.toEqual([
{
createdAt: '2021-01-01T00:00:00.000Z',
current: true,
deviceOS: '',
deviceType: '',
id: 'token-id',
updatedAt: expect.any(String),
},
{
createdAt: '2021-01-01T00:00:00.000Z',
current: false,
deviceOS: 'Android',
deviceType: 'Mobile',
id: 'not_active',
updatedAt: expect.any(String),
},
const currentSession = factory.session();
const otherSession = factory.session();
const auth = factory.auth({ session: currentSession });
mocks.session.getByUserId.mockResolvedValue([currentSession, otherSession]);
await expect(sut.getAll(auth)).resolves.toEqual([
expect.objectContaining({ current: true, id: currentSession.id }),
expect.objectContaining({ current: false, id: otherSession.id }),
]);
expect(mocks.session.getByUserId).toHaveBeenCalledWith(authStub.user1.user.id);
expect(mocks.session.getByUserId).toHaveBeenCalledWith(auth.user.id);
});
});
describe('logoutDevices', () => {
it('should logout all devices', async () => {
mocks.session.getByUserId.mockResolvedValue([sessionStub.inactive, sessionStub.valid] as any[]);
const currentSession = factory.session();
const otherSession = factory.session();
const auth = factory.auth({ session: currentSession });
mocks.session.getByUserId.mockResolvedValue([currentSession, otherSession]);
mocks.session.delete.mockResolvedValue();
await sut.deleteAll(authStub.user1);
await sut.deleteAll(auth);
expect(mocks.session.getByUserId).toHaveBeenCalledWith(authStub.user1.user.id);
expect(mocks.session.delete).toHaveBeenCalledWith('not_active');
expect(mocks.session.delete).not.toHaveBeenCalledWith('token-id');
expect(mocks.session.getByUserId).toHaveBeenCalledWith(auth.user.id);
expect(mocks.session.delete).toHaveBeenCalledWith(otherSession.id);
expect(mocks.session.delete).not.toHaveBeenCalledWith(currentSession.id);
});
});