test: auth.service & auth.resolver
test: auth.resolver
This commit is contained in:
parent
86f29b5754
commit
7f58f19416
8 changed files with 167 additions and 8 deletions
|
@ -32,7 +32,7 @@ const Login: React.FC = () => {
|
|||
const { data } = await login({ variables: { input: { username: values.email, password: values.password } } });
|
||||
|
||||
if (data?.login?.token) {
|
||||
await localStorage.setItem('token', data.login.token);
|
||||
localStorage.setItem('token', data.login.token);
|
||||
}
|
||||
|
||||
await client.refetchQueries({ include: ['Me'] });
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { faker } from '@faker-js/faker';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { DataSource } from 'typeorm';
|
||||
import TipiCache from '../../../config/TipiCache';
|
||||
import { getConfig } from '../../../core/config/TipiConfig';
|
||||
import { setupConnection, teardownConnection } from '../../../test/connection';
|
||||
import { gcall } from '../../../test/gcall';
|
||||
import { loginMutation, registerMutation } from '../../../test/mutations';
|
||||
import { isConfiguredQuery, MeQuery } from '../../../test/queries';
|
||||
import { isConfiguredQuery, MeQuery, refreshTokenQuery } from '../../../test/queries';
|
||||
import User from '../../auth/user.entity';
|
||||
import { TokenResponse } from '../auth.types';
|
||||
import { createUser } from './user.factory';
|
||||
|
@ -194,3 +195,39 @@ describe('Test: isConfigured', () => {
|
|||
expect(data?.isConfigured).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: refreshToken', () => {
|
||||
const email = faker.internet.email();
|
||||
let user1: User;
|
||||
|
||||
beforeEach(async () => {
|
||||
user1 = await createUser(email);
|
||||
});
|
||||
|
||||
it('should return a new token', async () => {
|
||||
// Arrange
|
||||
const session = faker.datatype.uuid();
|
||||
await TipiCache.set(session, user1.id.toString());
|
||||
|
||||
// Act
|
||||
const { data } = await gcall<{ refreshToken: TokenResponse }>({
|
||||
source: refreshTokenQuery,
|
||||
userId: user1.id,
|
||||
session: session,
|
||||
});
|
||||
const decoded = jwt.verify(data?.refreshToken?.token || '', getConfig().jwtSecret) as jwt.JwtPayload;
|
||||
|
||||
// Assert
|
||||
expect(data?.refreshToken).toBeDefined();
|
||||
expect(data?.refreshToken?.token).toBeDefined();
|
||||
expect(decoded).toBeDefined();
|
||||
expect(decoded).not.toBeNull();
|
||||
expect(decoded).toHaveProperty('id');
|
||||
expect(decoded).toHaveProperty('iat');
|
||||
expect(decoded).toHaveProperty('exp');
|
||||
expect(decoded).toHaveProperty('session');
|
||||
|
||||
expect(decoded.id).toEqual(user1.id.toString());
|
||||
expect(decoded.session).not.toEqual(session);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,10 +7,13 @@ import { faker } from '@faker-js/faker';
|
|||
import { setupConnection, teardownConnection } from '../../../test/connection';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { setConfig } from '../../../core/config/TipiConfig';
|
||||
import TipiCache from '../../../config/TipiCache';
|
||||
|
||||
let db: DataSource | null = null;
|
||||
const TEST_SUITE = 'authservice';
|
||||
|
||||
jest.mock('redis');
|
||||
|
||||
beforeAll(async () => {
|
||||
setConfig('jwtSecret', 'test');
|
||||
db = await setupConnection(TEST_SUITE);
|
||||
|
@ -117,4 +120,113 @@ describe('Register', () => {
|
|||
// Assert
|
||||
expect(isPasswordValid).toBe(true);
|
||||
});
|
||||
|
||||
it('Should throw if email is invalid', async () => {
|
||||
await expect(AuthService.register({ username: 'test', password: 'test' })).rejects.toThrowError('Invalid username');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: logout', () => {
|
||||
it('Should return true if there is no session to delete', async () => {
|
||||
// Act
|
||||
const result = await AuthService.logout();
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('Should delete session from cache', async () => {
|
||||
// Arrange
|
||||
const session = faker.random.alphaNumeric(32);
|
||||
await TipiCache.set(session, 'test');
|
||||
expect(await TipiCache.get(session)).toBe('test');
|
||||
|
||||
// Act
|
||||
const result = await AuthService.logout(session);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true);
|
||||
expect(await TipiCache.get('session')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: refreshToken', () => {
|
||||
it('Should return null if session is not provided', async () => {
|
||||
// Act
|
||||
const result = await AuthService.refreshToken();
|
||||
|
||||
// Assert
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('Should return null if session is not found in cache', async () => {
|
||||
// Act
|
||||
const result = await AuthService.refreshToken('test');
|
||||
|
||||
// Assert
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('Should return a new token if session is found in cache', async () => {
|
||||
// Arrange
|
||||
const session = faker.random.alphaNumeric(32);
|
||||
await TipiCache.set(session, 'test');
|
||||
|
||||
// Act
|
||||
const result = await AuthService.refreshToken(session);
|
||||
|
||||
// Assert
|
||||
expect(result).not.toBeNull();
|
||||
expect(result).toHaveProperty('token');
|
||||
expect(result?.token).not.toBe(session);
|
||||
});
|
||||
|
||||
it('Should delete old session from cache', async () => {
|
||||
// Arrange
|
||||
const session = faker.random.alphaNumeric(32);
|
||||
await TipiCache.set(session, '1');
|
||||
|
||||
// Act
|
||||
const result = await AuthService.refreshToken(session);
|
||||
|
||||
// Assert
|
||||
expect(result).not.toBeNull();
|
||||
expect(result).toHaveProperty('token');
|
||||
expect(result?.token).not.toBe(session);
|
||||
expect(await TipiCache.get(session)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: me', () => {
|
||||
it('Should return null if userId is not provided', async () => {
|
||||
// Act
|
||||
const result = await AuthService.me();
|
||||
|
||||
// Assert
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('Should return null if user does not exist', async () => {
|
||||
// Act
|
||||
const result = await AuthService.me(1);
|
||||
|
||||
// Assert
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('Should return user if user exists', async () => {
|
||||
// Arrange
|
||||
const email = faker.internet.email();
|
||||
const user = await createUser(email);
|
||||
|
||||
// Act
|
||||
const result = await AuthService.me(user.id);
|
||||
|
||||
// Assert
|
||||
expect(result).not.toBeNull();
|
||||
expect(result).toHaveProperty('id');
|
||||
expect(result).toHaveProperty('username');
|
||||
expect(result).toHaveProperty('createdAt');
|
||||
expect(result).toHaveProperty('updatedAt');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,7 +28,10 @@ export default class AuthResolver {
|
|||
|
||||
@Mutation(() => Boolean)
|
||||
async logout(@Ctx() { req }: MyContext): Promise<boolean> {
|
||||
await AuthService.logout(req.session?.id);
|
||||
if (req.session.id) {
|
||||
await AuthService.logout(req.session?.id);
|
||||
}
|
||||
|
||||
req.session.userId = undefined;
|
||||
req.session.id = undefined;
|
||||
|
||||
|
|
|
@ -69,9 +69,7 @@ const me = async (userId?: number): Promise<User | null> => {
|
|||
return user;
|
||||
};
|
||||
|
||||
const logout = async (session?: string): Promise<boolean> => {
|
||||
if (!session) return false;
|
||||
|
||||
const logout = async (session: string): Promise<boolean> => {
|
||||
await TipiCache.del(session);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -8,11 +8,12 @@ interface Options {
|
|||
[key: string]: any;
|
||||
}>;
|
||||
userId?: number;
|
||||
session?: string;
|
||||
}
|
||||
|
||||
let schema: GraphQLSchema | null = null;
|
||||
|
||||
export const gcall = async <T>({ source, variableValues, userId }: Options): Promise<ExecutionResult<T, { [key: string]: any }>> => {
|
||||
export const gcall = async <T>({ source, variableValues, userId, session }: Options): Promise<ExecutionResult<T, { [key: string]: any }>> => {
|
||||
if (!schema) {
|
||||
schema = await createSchema();
|
||||
}
|
||||
|
@ -21,6 +22,6 @@ export const gcall = async <T>({ source, variableValues, userId }: Options): Pro
|
|||
schema,
|
||||
source,
|
||||
variableValues,
|
||||
contextValue: { req: { session: { userId } } },
|
||||
contextValue: { req: { session: { userId, id: session } } },
|
||||
}) as any;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ import * as Me from './me.graphql';
|
|||
import * as isConfigured from './isConfigured.graphql';
|
||||
import * as systemInfo from './systemInfo.graphql';
|
||||
import * as version from './version.graphql';
|
||||
import * as refreshToken from './refreshToken.graphql';
|
||||
|
||||
export const listAppInfosQuery = print(listAppInfos);
|
||||
export const getAppQuery = print(getApp);
|
||||
|
@ -17,3 +18,4 @@ export const MeQuery = print(Me);
|
|||
export const isConfiguredQuery = print(isConfigured);
|
||||
export const systemInfoQuery = print(systemInfo);
|
||||
export const versionQuery = print(version);
|
||||
export const refreshTokenQuery = print(refreshToken);
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Write your query or mutation here
|
||||
query RefreshToken {
|
||||
refreshToken {
|
||||
token
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue