mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-16 16:14:43 -04:00
Tests: Rewrote AuthService unit test
The unit test now uses per test mocking of the necessary functions instead of one mock in the beforeEach call. Also some tests got expanded to cover more error cases. Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
41d6121d51
commit
ba553f28da
1 changed files with 169 additions and 79 deletions
|
@ -13,86 +13,47 @@ import { UsersModule } from '../users/users.module';
|
||||||
import { Identity } from '../users/identity.entity';
|
import { Identity } from '../users/identity.entity';
|
||||||
import { LoggerModule } from '../logger/logger.module';
|
import { LoggerModule } from '../logger/logger.module';
|
||||||
import { AuthToken } from './auth-token.entity';
|
import { AuthToken } from './auth-token.entity';
|
||||||
import { TokenNotValidError } from '../errors/errors';
|
import { NotInDBError, TokenNotValidError } from '../errors/errors';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
describe('AuthService', () => {
|
describe('AuthService', () => {
|
||||||
let service: AuthService;
|
let service: AuthService;
|
||||||
let user: User;
|
let user: User;
|
||||||
let authToken: AuthToken;
|
let authToken: AuthToken;
|
||||||
|
let userRepo: Repository<User>;
|
||||||
|
let authTokenRepo: Repository<AuthToken>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = {
|
|
||||||
authTokens: [],
|
|
||||||
createdAt: new Date(),
|
|
||||||
displayName: 'hardcoded',
|
|
||||||
id: '1',
|
|
||||||
identities: [],
|
|
||||||
ownedNotes: [],
|
|
||||||
historyEntries: [],
|
|
||||||
updatedAt: new Date(),
|
|
||||||
userName: 'Testy',
|
|
||||||
};
|
|
||||||
|
|
||||||
authToken = {
|
|
||||||
accessTokenHash: '',
|
|
||||||
createdAt: new Date(),
|
|
||||||
id: 1,
|
|
||||||
label: 'testIdentifier',
|
|
||||||
keyId: 'abc',
|
|
||||||
lastUsed: null,
|
|
||||||
user: null,
|
|
||||||
validUntil: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
AuthService,
|
AuthService,
|
||||||
{
|
{
|
||||||
provide: getRepositoryToken(AuthToken),
|
provide: getRepositoryToken(AuthToken),
|
||||||
useValue: {},
|
useClass: Repository,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
imports: [PassportModule, UsersModule, LoggerModule],
|
imports: [PassportModule, UsersModule, LoggerModule],
|
||||||
})
|
})
|
||||||
.overrideProvider(getRepositoryToken(AuthToken))
|
|
||||||
.useValue({
|
|
||||||
findOne: (): AuthToken => {
|
|
||||||
return {
|
|
||||||
...authToken,
|
|
||||||
user: user,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
save: async (entity: AuthToken) => {
|
|
||||||
if (entity.lastUsed === undefined) {
|
|
||||||
expect(entity.lastUsed).toBeUndefined();
|
|
||||||
} else {
|
|
||||||
expect(entity.lastUsed.getTime()).toBeLessThanOrEqual(
|
|
||||||
new Date().getTime(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return entity;
|
|
||||||
},
|
|
||||||
remove: async (entity: AuthToken) => {
|
|
||||||
expect(entity).toEqual({
|
|
||||||
...authToken,
|
|
||||||
user: user,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.overrideProvider(getRepositoryToken(Identity))
|
.overrideProvider(getRepositoryToken(Identity))
|
||||||
.useValue({})
|
.useValue({})
|
||||||
.overrideProvider(getRepositoryToken(User))
|
.overrideProvider(getRepositoryToken(User))
|
||||||
.useValue({
|
.useClass(Repository)
|
||||||
findOne: (): User => {
|
|
||||||
return {
|
|
||||||
...user,
|
|
||||||
authTokens: [authToken],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.compile();
|
.compile();
|
||||||
|
|
||||||
service = module.get<AuthService>(AuthService);
|
service = module.get<AuthService>(AuthService);
|
||||||
|
userRepo = module.get<Repository<User>>(getRepositoryToken(User));
|
||||||
|
authTokenRepo = module.get<Repository<AuthToken>>(
|
||||||
|
getRepositoryToken(AuthToken),
|
||||||
|
);
|
||||||
|
|
||||||
|
user = User.create('hardcoded', 'Testy') as User;
|
||||||
|
authToken = AuthToken.create(
|
||||||
|
user,
|
||||||
|
'testToken',
|
||||||
|
'testKeyId',
|
||||||
|
'abc',
|
||||||
|
new Date(new Date().getTime() + 60000), // make this AuthToken valid for 1min
|
||||||
|
) as AuthToken;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
|
@ -121,6 +82,9 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('getTokensByUsername', () => {
|
describe('getTokensByUsername', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
|
jest
|
||||||
|
.spyOn(userRepo, 'findOne')
|
||||||
|
.mockResolvedValueOnce({ ...user, authTokens: [authToken] });
|
||||||
const tokens = await service.getTokensByUsername(user.userName);
|
const tokens = await service.getTokensByUsername(user.userName);
|
||||||
expect(tokens).toHaveLength(1);
|
expect(tokens).toHaveLength(1);
|
||||||
expect(tokens).toEqual([authToken]);
|
expect(tokens).toEqual([authToken]);
|
||||||
|
@ -128,9 +92,14 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAuthToken', () => {
|
describe('getAuthToken', () => {
|
||||||
|
const token = 'testToken';
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const token = 'testToken';
|
const accessTokenHash = await service.hashPassword(token);
|
||||||
authToken.accessTokenHash = await service.hashPassword(token);
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
accessTokenHash: accessTokenHash,
|
||||||
|
});
|
||||||
const authTokenFromCall = await service.getAuthTokenAndValidate(
|
const authTokenFromCall = await service.getAuthTokenAndValidate(
|
||||||
authToken.keyId,
|
authToken.keyId,
|
||||||
token,
|
token,
|
||||||
|
@ -138,12 +107,61 @@ describe('AuthService', () => {
|
||||||
expect(authTokenFromCall).toEqual({
|
expect(authTokenFromCall).toEqual({
|
||||||
...authToken,
|
...authToken,
|
||||||
user: user,
|
user: user,
|
||||||
|
accessTokenHash: accessTokenHash,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('fails:', () => {
|
||||||
|
it('AuthToken could not be found', async () => {
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(undefined);
|
||||||
|
try {
|
||||||
|
await service.getAuthTokenAndValidate(authToken.keyId, token);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeInstanceOf(NotInDBError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('AuthToken has wrong hash', async () => {
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
accessTokenHash: 'the wrong hash',
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await service.getAuthTokenAndValidate(authToken.keyId, token);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeInstanceOf(TokenNotValidError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('AuthToken has wrong validUntil Date', async () => {
|
||||||
|
const accessTokenHash = await service.hashPassword(token);
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
accessTokenHash: accessTokenHash,
|
||||||
|
validUntil: new Date(1549312452000),
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await service.getAuthTokenAndValidate(authToken.keyId, token);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toBeInstanceOf(TokenNotValidError);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setLastUsedToken', () => {
|
describe('setLastUsedToken', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
lastUsed: new Date(1549312452000),
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(authTokenRepo, 'save')
|
||||||
|
.mockImplementationOnce(async (authTokenSaved, _) => {
|
||||||
|
expect(authTokenSaved.keyId).toEqual(authToken.keyId);
|
||||||
|
expect(authTokenSaved.lastUsed).not.toEqual(1549312452000);
|
||||||
|
return authToken;
|
||||||
|
});
|
||||||
await service.setLastUsedToken(authToken.keyId);
|
await service.setLastUsedToken(authToken.keyId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -151,7 +169,21 @@ describe('AuthService', () => {
|
||||||
describe('validateToken', () => {
|
describe('validateToken', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const token = 'testToken';
|
const token = 'testToken';
|
||||||
authToken.accessTokenHash = await service.hashPassword(token);
|
const accessTokenHash = await service.hashPassword(token);
|
||||||
|
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...user,
|
||||||
|
authTokens: [authToken],
|
||||||
|
});
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValue({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
accessTokenHash: accessTokenHash,
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(authTokenRepo, 'save')
|
||||||
|
.mockImplementationOnce(async (_, __) => {
|
||||||
|
return authToken;
|
||||||
|
});
|
||||||
const userByToken = await service.validateToken(
|
const userByToken = await service.validateToken(
|
||||||
`${authToken.keyId}.${token}`,
|
`${authToken.keyId}.${token}`,
|
||||||
);
|
);
|
||||||
|
@ -160,34 +192,88 @@ describe('AuthService', () => {
|
||||||
authTokens: [authToken],
|
authTokens: [authToken],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('fails on too long token', () => {
|
describe('fails:', () => {
|
||||||
expect(
|
it('the secret is missing', () => {
|
||||||
service.validateToken(`${authToken.keyId}.${'a'.repeat(73)}`),
|
expect(
|
||||||
).rejects.toBeInstanceOf(TokenNotValidError);
|
service.validateToken(`${authToken.keyId}`),
|
||||||
|
).rejects.toBeInstanceOf(TokenNotValidError);
|
||||||
|
});
|
||||||
|
it('the secret is too long', () => {
|
||||||
|
expect(
|
||||||
|
service.validateToken(`${authToken.keyId}.${'a'.repeat(73)}`),
|
||||||
|
).rejects.toBeInstanceOf(TokenNotValidError);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('removeToken', () => {
|
describe('removeToken', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValue({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(authTokenRepo, 'remove')
|
||||||
|
.mockImplementationOnce(async (token, __) => {
|
||||||
|
expect(token).toEqual({
|
||||||
|
...authToken,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
return authToken;
|
||||||
|
});
|
||||||
await service.removeToken(authToken.keyId);
|
await service.removeToken(authToken.keyId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createTokenForUser', () => {
|
describe('createTokenForUser', () => {
|
||||||
it('works', async () => {
|
describe('works', () => {
|
||||||
const identifier = 'identifier2';
|
const identifier = 'testIdentifier';
|
||||||
const token = await service.createTokenForUser(
|
it('with validUntil 0', async () => {
|
||||||
user.userName,
|
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce({
|
||||||
identifier,
|
...user,
|
||||||
0,
|
authTokens: [authToken],
|
||||||
);
|
});
|
||||||
expect(token.label).toEqual(identifier);
|
jest
|
||||||
expect(
|
.spyOn(authTokenRepo, 'save')
|
||||||
token.validUntil.getTime() -
|
.mockImplementationOnce(async (authTokenSaved: AuthToken, _) => {
|
||||||
(new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000),
|
expect(authTokenSaved.lastUsed).toBeUndefined();
|
||||||
).toBeLessThanOrEqual(10000);
|
return authTokenSaved;
|
||||||
expect(token.lastUsed).toBeNull();
|
});
|
||||||
expect(token.secret.startsWith(token.keyId)).toBeTruthy();
|
const token = await service.createTokenForUser(
|
||||||
|
user.userName,
|
||||||
|
identifier,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
expect(token.label).toEqual(identifier);
|
||||||
|
expect(
|
||||||
|
token.validUntil.getTime() -
|
||||||
|
(new Date().getTime() + 2 * 365 * 24 * 60 * 60 * 1000),
|
||||||
|
).toBeLessThanOrEqual(10000);
|
||||||
|
expect(token.lastUsed).toBeNull();
|
||||||
|
expect(token.secret.startsWith(token.keyId)).toBeTruthy();
|
||||||
|
});
|
||||||
|
it('with validUntil not 0', async () => {
|
||||||
|
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce({
|
||||||
|
...user,
|
||||||
|
authTokens: [authToken],
|
||||||
|
});
|
||||||
|
jest
|
||||||
|
.spyOn(authTokenRepo, 'save')
|
||||||
|
.mockImplementationOnce(async (authTokenSaved: AuthToken, _) => {
|
||||||
|
expect(authTokenSaved.lastUsed).toBeUndefined();
|
||||||
|
return authTokenSaved;
|
||||||
|
});
|
||||||
|
const validUntil = new Date().getTime() + 30000;
|
||||||
|
const token = await service.createTokenForUser(
|
||||||
|
user.userName,
|
||||||
|
identifier,
|
||||||
|
validUntil,
|
||||||
|
);
|
||||||
|
expect(token.label).toEqual(identifier);
|
||||||
|
expect(token.validUntil.getTime()).toEqual(validUntil);
|
||||||
|
expect(token.lastUsed).toBeNull();
|
||||||
|
expect(token.secret.startsWith(token.keyId)).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -203,6 +289,10 @@ describe('AuthService', () => {
|
||||||
|
|
||||||
describe('toAuthTokenDto', () => {
|
describe('toAuthTokenDto', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
|
const authToken = new AuthToken();
|
||||||
|
authToken.keyId = 'testKeyId';
|
||||||
|
authToken.label = 'testLabel';
|
||||||
|
authToken.createdAt = new Date();
|
||||||
const tokenDto = await service.toAuthTokenDto(authToken);
|
const tokenDto = await service.toAuthTokenDto(authToken);
|
||||||
expect(tokenDto.keyId).toEqual(authToken.keyId);
|
expect(tokenDto.keyId).toEqual(authToken.keyId);
|
||||||
expect(tokenDto.lastUsed).toBeNull();
|
expect(tokenDto.lastUsed).toBeNull();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue