Skip to content

Commit ab612a3

Browse files
Add backend API unit tests (#21)
* setting up test structure * . * added playwright config file, deleted original playwright folder and moved "some.test" file * continued test structure setup * Updating test folder structure * Added database seeding script and backend testing folder structure * removed the database test * Replaced db seeding script * Updated userInformation.ts to use values from choices.tsx * merge prep * removing extra unit test, moving api test to correct folder * Pushing to get help with sql Unit test * Updating get-profiles unit tests * Added more unit tests * . * Added more unit tests * Added getSupabaseToken unit test * . * excluding supabase token test so ci can pass * . * Seperated the seedDatabase func into its own file so it can be accessed seperatly * Fixed failing test * . * . * Fix tests * Fix lint * Clean --------- Co-authored-by: MartinBraquet <[email protected]>
1 parent f323034 commit ab612a3

26 files changed

+1447
-168
lines changed

.vscode/launch.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Debug Jest Tests",
9+
"type": "node",
10+
"request": "launch",
11+
"runtimeArgs": [
12+
"--inspect-brk",
13+
"${workspaceRoot}/node_modules/.bin/jest",
14+
"--runInBand"
15+
],
16+
"console": "integratedTerminal",
17+
"internalConsoleOptions": "neverOpen"
18+
}
19+
// {
20+
// "type": "node",
21+
// "request": "launch",
22+
// "name": "Launch Program",
23+
// "skipFiles": [
24+
// "<node_internals>/**"
25+
// ],
26+
// "program": "${workspaceFolder}/backend/api/tests/unit/get-profiles.unit.test.ts",
27+
// "outFiles": [
28+
// "${workspaceFolder}/**/*.js"
29+
// ]
30+
// }
31+
]
32+
}

backend/api/.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
parser: '@typescript-eslint/parser',
1414
parserOptions: {
1515
tsconfigRootDir: __dirname,
16-
project: ['./tsconfig.json'],
16+
project: ['./tsconfig.json', './tsconfig.test.json'],
1717
},
1818
rules: {
1919
'@typescript-eslint/ban-types': [

backend/api/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,6 @@ docker rmi -f $(docker images -aq)
168168
### Documentation
169169

170170
The API doc is available at https://api.compassmeet.com. It's dynamically prepared in [app.ts](src/app.ts).
171+
172+
### Todo (Tests)
173+
- [ ] Finish get-supabase-token unit test when endpoint is implemented

backend/api/src/get-profiles.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,26 @@ export type profileQueryType = {
1010
after?: string | undefined,
1111
// Search and filter parameters
1212
name?: string | undefined,
13-
genders?: String[] | undefined,
14-
education_levels?: String[] | undefined,
15-
pref_gender?: String[] | undefined,
13+
genders?: string[] | undefined,
14+
education_levels?: string[] | undefined,
15+
pref_gender?: string[] | undefined,
1616
pref_age_min?: number | undefined,
1717
pref_age_max?: number | undefined,
1818
drinks_min?: number | undefined,
1919
drinks_max?: number | undefined,
20-
pref_relation_styles?: String[] | undefined,
21-
pref_romantic_styles?: String[] | undefined,
22-
diet?: String[] | undefined,
23-
political_beliefs?: String[] | undefined,
24-
mbti?: String[] | undefined,
25-
relationship_status?: String[] | undefined,
26-
languages?: String[] | undefined,
27-
religion?: String[] | undefined,
20+
pref_relation_styles?: string[] | undefined,
21+
pref_romantic_styles?: string[] | undefined,
22+
diet?: string[] | undefined,
23+
political_beliefs?: string[] | undefined,
24+
mbti?: string[] | undefined,
25+
relationship_status?: string[] | undefined,
26+
languages?: string[] | undefined,
27+
religion?: string[] | undefined,
2828
wants_kids_strength?: number | undefined,
2929
has_kids?: number | undefined,
3030
is_smoker?: boolean | undefined,
3131
shortBio?: boolean | undefined,
32-
geodbCityIds?: String[] | undefined,
32+
geodbCityIds?: string[] | undefined,
3333
lat?: number | undefined,
3434
lon?: number | undefined,
3535
radius?: number | undefined,

backend/api/src/get-user.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { toUserAPIResponse } from 'common/api/user-types'
2-
import { convertUser, displayUserColumns } from 'common/supabase/users'
2+
import { convertUser } from 'common/supabase/users'
33
import { createSupabaseDirectClient } from 'shared/supabase/init'
44
import { APIError } from 'common/api/utils'
5-
import { removeNullOrUndefinedProps } from 'common/util/object'
65

76
export const getUser = async (props: { id: string } | { username: string }) => {
87
const pg = createSupabaseDirectClient()

backend/api/src/report.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const report: APIHandler<'report'> = async (body, auth) => {
5252
console.error('Failed to get reported user for report', userError)
5353
return
5454
}
55-
let message: string = `
55+
const message: string = `
5656
🚨 **New Report** 🚨
5757
**Type:** ${contentType}
5858
**Content ID:** ${contentId}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
jest.mock('shared/supabase/init')
2+
jest.mock('shared/helpers/auth')
3+
jest.mock('common/envs/constants')
4+
jest.mock('shared/supabase/users')
5+
jest.mock('shared/analytics')
6+
jest.mock('shared/utils')
7+
8+
import { banUser } from "api/ban-user";
9+
import * as supabaseInit from "shared/supabase/init";
10+
import { throwErrorIfNotMod } from "shared/helpers/auth";
11+
import * as constants from "common/envs/constants";
12+
import * as supabaseUsers from "shared/supabase/users";
13+
import * as sharedAnalytics from "shared/analytics";
14+
import { } from "shared/helpers/auth";
15+
import { APIError, AuthedUser } from "api/helpers/endpoint"
16+
17+
18+
describe('banUser', () => {
19+
const mockPg = {} as any;
20+
21+
beforeEach(() => {
22+
jest.resetAllMocks();
23+
24+
(supabaseInit.createSupabaseDirectClient as jest.Mock)
25+
.mockReturnValue(mockPg);
26+
});
27+
28+
afterEach(() => {
29+
jest.restoreAllMocks();
30+
});
31+
32+
describe('should', () => {
33+
it('ban a user successfully', async () => {
34+
const mockUser = {
35+
userId: '123',
36+
unban: false
37+
};
38+
const mockAuth = {uid: '321'} as AuthedUser;
39+
const mockReq = {} as any;
40+
41+
(constants.isAdminId as jest.Mock).mockReturnValue(false);
42+
43+
await banUser(mockUser, mockAuth, mockReq);
44+
45+
expect(throwErrorIfNotMod).toBeCalledWith(mockAuth.uid);
46+
expect(constants.isAdminId).toBeCalledWith(mockUser.userId);
47+
expect(sharedAnalytics.trackPublicEvent)
48+
.toBeCalledWith(mockAuth.uid, 'ban user', {userId: mockUser.userId});
49+
expect(supabaseUsers.updateUser)
50+
.toBeCalledWith(mockPg, mockUser.userId, {isBannedFromPosting: true});
51+
});
52+
53+
it('unban a user successfully', async () => {
54+
const mockUser = {
55+
userId: '123',
56+
unban: true
57+
};
58+
const mockAuth = {uid: '321'} as AuthedUser;
59+
const mockReq = {} as any;
60+
61+
(constants.isAdminId as jest.Mock).mockReturnValue(false);
62+
63+
await banUser(mockUser, mockAuth, mockReq);
64+
65+
expect(throwErrorIfNotMod).toBeCalledWith(mockAuth.uid);
66+
expect(constants.isAdminId).toBeCalledWith(mockUser.userId);
67+
expect(sharedAnalytics.trackPublicEvent)
68+
.toBeCalledWith(mockAuth.uid, 'ban user', {userId: mockUser.userId});
69+
expect(supabaseUsers.updateUser)
70+
.toBeCalledWith(mockPg, mockUser.userId, {isBannedFromPosting: false});
71+
});
72+
73+
it('throw and error if the ban requester is not a mod or admin', async () => {
74+
const mockUser = {
75+
userId: '123',
76+
unban: false
77+
};
78+
const mockAuth = {uid: '321'} as AuthedUser;
79+
const mockReq = {} as any;
80+
81+
(throwErrorIfNotMod as jest.Mock).mockRejectedValue(
82+
new APIError(
83+
403,
84+
`User ${mockAuth.uid} must be an admin or trusted to perform this action.`
85+
)
86+
);
87+
88+
await expect(banUser(mockUser, mockAuth, mockReq))
89+
.rejects
90+
.toThrowError(`User ${mockAuth.uid} must be an admin or trusted to perform this action.`);
91+
expect(throwErrorIfNotMod).toBeCalledWith(mockAuth.uid);
92+
expect(sharedAnalytics.trackPublicEvent).toBeCalledTimes(0);
93+
expect(supabaseUsers.updateUser).toBeCalledTimes(0);
94+
});
95+
96+
it('throw an error if the ban target is an admin', async () => {
97+
const mockUser = {
98+
userId: '123',
99+
unban: false
100+
};
101+
const mockAuth = {uid: '321'} as AuthedUser;
102+
const mockReq = {} as any;
103+
104+
(constants.isAdminId as jest.Mock).mockReturnValue(true);
105+
106+
await expect(banUser(mockUser, mockAuth, mockReq))
107+
.rejects
108+
.toThrowError('Cannot ban admin');
109+
expect(throwErrorIfNotMod).toBeCalledWith(mockAuth.uid);
110+
expect(constants.isAdminId).toBeCalledWith(mockUser.userId);
111+
expect(sharedAnalytics.trackPublicEvent).toBeCalledTimes(0);
112+
expect(supabaseUsers.updateUser).toBeCalledTimes(0);
113+
});
114+
});
115+
});
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
jest.mock('shared/supabase/init')
2+
jest.mock('shared/supabase/users')
3+
jest.mock('shared/supabase/utils')
4+
5+
import * as blockUserModule from "api/block-user";
6+
import { AuthedUser } from "api/helpers/endpoint";
7+
import * as supabaseInit from "shared/supabase/init";
8+
import * as supabaseUsers from "shared/supabase/users";
9+
import * as supabaseUtils from "shared/supabase/utils";
10+
11+
describe('blockUser', () => {
12+
let mockPg: any;
13+
14+
beforeEach(() => {
15+
jest.resetAllMocks()
16+
mockPg = {
17+
tx: jest.fn(async (cb) => {
18+
const mockTx = {};
19+
await cb(mockTx);
20+
}),
21+
};
22+
23+
(supabaseInit.createSupabaseDirectClient as jest.Mock)
24+
.mockReturnValue(mockPg)
25+
});
26+
27+
afterEach(() => {
28+
jest.restoreAllMocks();
29+
});
30+
31+
describe('should', () => {
32+
it('block the user successfully', async () => {
33+
const mockParams = { id: '123' }
34+
const mockAuth = {uid: '321'} as AuthedUser;
35+
const mockReq = {} as any;
36+
37+
(supabaseUsers.updatePrivateUser as jest.Mock).mockResolvedValue(null);
38+
39+
await blockUserModule.blockUser(mockParams, mockAuth, mockReq)
40+
41+
expect(mockPg.tx).toHaveBeenCalledTimes(1)
42+
43+
expect(supabaseUsers.updatePrivateUser)
44+
.toHaveBeenCalledWith(
45+
expect.any(Object),
46+
mockAuth.uid,
47+
{ blockedByUserIds: supabaseUtils.FieldVal.arrayConcat(mockParams.id)}
48+
);
49+
expect(supabaseUsers.updatePrivateUser)
50+
.toHaveBeenCalledWith(
51+
expect.any(Object),
52+
mockParams.id,
53+
{ blockedByUserIds: supabaseUtils.FieldVal.arrayConcat(mockAuth.uid)}
54+
);
55+
});
56+
57+
it('throw an error if the user tries to block themselves', async () => {
58+
const mockParams = { id: '123' }
59+
const mockAuth = {uid: '123'} as AuthedUser;
60+
const mockReq = {} as any;
61+
62+
expect(blockUserModule.blockUser(mockParams, mockAuth, mockReq))
63+
.rejects
64+
.toThrowError('You cannot block yourself')
65+
66+
expect(mockPg.tx).toHaveBeenCalledTimes(0)
67+
});
68+
});
69+
70+
});
71+
72+
describe('unblockUser', () => {
73+
let mockPg: any;
74+
75+
beforeEach(() => {
76+
jest.resetAllMocks()
77+
mockPg = {
78+
tx: jest.fn(async (cb) => {
79+
const mockTx = {};
80+
await cb(mockTx);
81+
}),
82+
};
83+
84+
(supabaseInit.createSupabaseDirectClient as jest.Mock)
85+
.mockReturnValue(mockPg)
86+
});
87+
88+
afterEach(() => {
89+
jest.restoreAllMocks();
90+
});
91+
92+
describe('should', () => {
93+
it('block the user successfully', async () => {
94+
const mockParams = { id: '123' }
95+
const mockAuth = {uid: '321'} as AuthedUser;
96+
const mockReq = {} as any;
97+
98+
(supabaseUsers.updatePrivateUser as jest.Mock).mockResolvedValue(null);
99+
100+
await blockUserModule.unblockUser(mockParams, mockAuth, mockReq)
101+
102+
expect(mockPg.tx).toHaveBeenCalledTimes(1)
103+
104+
expect(supabaseUsers.updatePrivateUser)
105+
.toHaveBeenCalledWith(
106+
expect.any(Object),
107+
mockAuth.uid,
108+
{ blockedByUserIds: supabaseUtils.FieldVal.arrayConcat(mockParams.id)}
109+
);
110+
expect(supabaseUsers.updatePrivateUser)
111+
.toHaveBeenCalledWith(
112+
expect.any(Object),
113+
mockParams.id,
114+
{ blockedByUserIds: supabaseUtils.FieldVal.arrayConcat(mockAuth.uid)}
115+
);
116+
});
117+
});
118+
119+
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as supabaseInit from "shared/supabase/init";
2+
import {getCompatibleProfiles} from "api/compatible-profiles";
3+
4+
jest.mock('shared/supabase/init')
5+
6+
describe('getCompatibleProfiles', () => {
7+
beforeEach(() => {
8+
jest.resetAllMocks();
9+
const mockPg = {
10+
none: jest.fn().mockResolvedValue(null),
11+
one: jest.fn().mockResolvedValue(null),
12+
oneOrNone: jest.fn().mockResolvedValue(null),
13+
any: jest.fn().mockResolvedValue([]),
14+
map: jest.fn().mockResolvedValue([["abc", {score: 0.69}]]),
15+
} as any;
16+
(supabaseInit.createSupabaseDirectClient as jest.Mock)
17+
.mockReturnValue(mockPg);
18+
});
19+
20+
afterEach(() => {
21+
jest.restoreAllMocks();
22+
});
23+
24+
describe('should', () => {
25+
it('successfully get compatible profiles when supplied with a valid user Id', async () => {
26+
const results = await getCompatibleProfiles("123");
27+
expect(results.status).toEqual('success');
28+
expect(results.profileCompatibilityScores).toEqual({"abc": {score: 0.69}});
29+
});
30+
31+
});
32+
});

0 commit comments

Comments
 (0)