Skip to content

Commit 001a8aa

Browse files
committed
Add Auth module tests
1 parent e6c1333 commit 001a8aa

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { UnauthorizedException } from '@nestjs/common';
3+
import { type PoolClient } from 'pg';
4+
5+
import { PgPoolService } from 'src/modules/pg-pool/pg-pool.service';
6+
7+
import { AuthController } from './auth.controller';
8+
import { SignInWithPasscodeBody, SignInWithPasscodeResponse } from './dto';
9+
import { AuthService } from './auth.service';
10+
11+
describe('Test AuthController', () => {
12+
const VALID_PASSCODE = 'valid';
13+
const INVALID_PASSCODE = 'invalid';
14+
const TOKEN = 'token';
15+
16+
const MOCK_POOL_CLIENT = {} as PoolClient;
17+
18+
let testModule: TestingModule;
19+
let controller: AuthController;
20+
21+
beforeEach(async () => {
22+
testModule = await Test.createTestingModule({
23+
controllers: [AuthController],
24+
})
25+
.useMocker((token) => {
26+
switch (token) {
27+
case AuthService:
28+
return {
29+
signInWithAdminPasscode: async (
30+
client: unknown,
31+
passcode: string,
32+
) => {
33+
if (passcode === VALID_PASSCODE) {
34+
return TOKEN;
35+
}
36+
37+
throw new UnauthorizedException();
38+
},
39+
};
40+
case PgPoolService:
41+
return {
42+
runInTransaction: <R>(
43+
func: (client: PoolClient) => Promise<R>,
44+
) => {
45+
return func(MOCK_POOL_CLIENT);
46+
},
47+
};
48+
}
49+
})
50+
.compile();
51+
52+
controller = testModule.get(AuthController);
53+
});
54+
55+
it('throws exception if invalid passcode is provided', () => {
56+
const body = new SignInWithPasscodeBody();
57+
body.passcode = INVALID_PASSCODE;
58+
expect(controller.signInWithPasscode(body)).rejects.toBeInstanceOf(
59+
UnauthorizedException,
60+
);
61+
});
62+
63+
it('throws exception if invalid request provided', () => {
64+
expect(
65+
controller.signInWithPasscode({
66+
foo: 'bar',
67+
} as unknown as SignInWithPasscodeBody),
68+
).rejects.toBeInstanceOf(UnauthorizedException);
69+
});
70+
71+
it('returns response if valid passcode is provided', () => {
72+
const body = new SignInWithPasscodeBody();
73+
body.passcode = VALID_PASSCODE;
74+
expect(controller.signInWithPasscode(body)).resolves.toStrictEqual(
75+
new SignInWithPasscodeResponse(TOKEN),
76+
);
77+
});
78+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { type ExecutionContext, UnauthorizedException } from '@nestjs/common';
2+
import { JwtService } from '@nestjs/jwt';
3+
4+
import { AuthGuard } from './auth.guard';
5+
import { type JWTPayload } from './types';
6+
7+
const VALID_TOKEN = 'VALID';
8+
const INVALID_TOKEN = 'INVALID';
9+
const JWT_PAYLOAD: JWTPayload = {
10+
userId: '1',
11+
};
12+
13+
const getMockExecutionContext = (token?: string) => {
14+
const request = {
15+
headers: {
16+
authorization: token ? `Bearer ${token}` : undefined,
17+
},
18+
};
19+
20+
return {
21+
switchToHttp: () => ({
22+
getRequest: () => request,
23+
}),
24+
} as ExecutionContext;
25+
};
26+
27+
describe('Test AuthGuard', () => {
28+
let guard: AuthGuard;
29+
30+
beforeEach(() => {
31+
guard = new AuthGuard({
32+
verifyAsync: async (token: string) => {
33+
if (token === VALID_TOKEN) {
34+
return JWT_PAYLOAD;
35+
} else {
36+
throw new UnauthorizedException();
37+
}
38+
},
39+
} as JwtService);
40+
});
41+
42+
it('throws error if no token provided in request', async () => {
43+
await expect(
44+
guard.canActivate(getMockExecutionContext()),
45+
).rejects.toBeInstanceOf(UnauthorizedException);
46+
});
47+
48+
it('throws error if invalid token provided in request', async () => {
49+
await expect(
50+
guard.canActivate(getMockExecutionContext(INVALID_TOKEN)),
51+
).rejects.toBeInstanceOf(UnauthorizedException);
52+
});
53+
54+
it('returns true if valid token provided in request, sets payload to request object', async () => {
55+
const ctx = getMockExecutionContext(VALID_TOKEN);
56+
await expect(guard.canActivate(ctx)).resolves.toBe(true);
57+
expect(ctx.switchToHttp().getRequest().userId).toBe(JWT_PAYLOAD.userId);
58+
});
59+
});
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { UnauthorizedException } from '@nestjs/common';
2+
import { JwtService } from '@nestjs/jwt';
3+
import { Test, TestingModule } from '@nestjs/testing';
4+
import { type PoolClient } from 'pg';
5+
6+
import { AdminPasscodeService } from 'src/modules/admin-passcode/admin-passcode.service';
7+
8+
import { AuthService } from './auth.service';
9+
import { type JWTPayload } from './types';
10+
11+
const MOCK_POOL_CLIENT = {} as PoolClient;
12+
13+
describe('Test AuthService', () => {
14+
describe('Test signInWithAdminPasscode', () => {
15+
const VALID_PASSCODE = 'pass';
16+
const INVALID_PASSCODE = 'no pass';
17+
const USER_ID = '1';
18+
19+
let testModule: TestingModule;
20+
let service: AuthService;
21+
22+
beforeEach(async () => {
23+
testModule = await Test.createTestingModule({
24+
providers: [AuthService],
25+
})
26+
.useMocker((token) => {
27+
switch (token) {
28+
case JwtService:
29+
return {
30+
signAsync: async (payload: JWTPayload) => payload,
31+
};
32+
case AdminPasscodeService:
33+
return {
34+
getUserId: async (client: unknown, passcode: string) =>
35+
passcode === VALID_PASSCODE ? USER_ID : undefined,
36+
};
37+
}
38+
})
39+
.compile();
40+
41+
service = testModule.get(AuthService);
42+
});
43+
44+
it('throws exception if invalid passcode provided', () => {
45+
expect(
46+
service.signInWithAdminPasscode(MOCK_POOL_CLIENT, INVALID_PASSCODE),
47+
).rejects.toBeInstanceOf(UnauthorizedException);
48+
});
49+
50+
it('returns payload if valid passcode provided', () => {
51+
expect(
52+
service.signInWithAdminPasscode(MOCK_POOL_CLIENT, VALID_PASSCODE),
53+
).resolves.toBe(JSON.stringify({ userId: USER_ID }));
54+
});
55+
});
56+
57+
describe('Test verifyJwt', () => {
58+
const VALID_TOKEN = 'VALID';
59+
const INVALID_TOKEN = 'INVALID';
60+
const JWT_PAYLOAD: JWTPayload = {
61+
userId: '1',
62+
};
63+
64+
let testModule: TestingModule;
65+
let service: AuthService;
66+
67+
beforeEach(async () => {
68+
testModule = await Test.createTestingModule({
69+
providers: [AuthService],
70+
})
71+
.useMocker((token) => {
72+
switch (token) {
73+
case JwtService:
74+
return {
75+
verifyAsync: async (token: string): Promise<JWTPayload> => {
76+
if (token === VALID_TOKEN) {
77+
return JWT_PAYLOAD;
78+
} else {
79+
throw new UnauthorizedException();
80+
}
81+
},
82+
};
83+
case AdminPasscodeService:
84+
return {};
85+
}
86+
})
87+
.compile();
88+
89+
service = testModule.get(AuthService);
90+
});
91+
92+
it('throws exception if invalid token is provided', () => {
93+
expect(service.verifyJwt(INVALID_TOKEN)).rejects.toBeInstanceOf(
94+
UnauthorizedException,
95+
);
96+
});
97+
98+
it('returns payload if valid token is provided', () => {
99+
expect(service.verifyJwt(VALID_TOKEN)).resolves.toBe(JWT_PAYLOAD);
100+
});
101+
});
102+
});

0 commit comments

Comments
 (0)