Skip to content

Commit

Permalink
update test coverage (#445)
Browse files Browse the repository at this point in the history
* add tests for the user service

* fix linting

* test funding mechanism and refactor service

* resolve lint warnings
  • Loading branch information
MartinBenediktBusch authored Jul 12, 2024
1 parent 9ecdeff commit b9a4888
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 29 deletions.
57 changes: 57 additions & 0 deletions src/services/funding-mechanism.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as db from '../db';
import { createDbClient } from '../utils/db/create-db-connection';
import { runMigrations } from '../utils/db/run-migrations';
import { environmentVariables } from '../types';
import { cleanup, seed } from '../utils/db/seed';
import { calculateFunding } from './funding-mechanism';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { Client } from 'pg';

describe('service: funding-mechanism', () => {
let dbPool: NodePgDatabase<typeof db>;
let dbConnection: Client;
let question: db.Question;

beforeAll(async () => {
const envVariables = environmentVariables.parse(process.env);
const initDb = await createDbClient({
database: envVariables.DATABASE_NAME,
host: envVariables.DATABASE_HOST,
password: envVariables.DATABASE_PASSWORD,
user: envVariables.DATABASE_USER,
port: envVariables.DATABASE_PORT,
});

await runMigrations({
database: envVariables.DATABASE_NAME,
host: envVariables.DATABASE_HOST,
password: envVariables.DATABASE_PASSWORD,
user: envVariables.DATABASE_USER,
port: envVariables.DATABASE_PORT,
});

dbPool = initDb.db;
dbConnection = initDb.client;
const { forumQuestions } = await seed(dbPool);
question = forumQuestions[0]!;
});

test('calculateFunding returns and error if the query returns no optionData', async () => {
const response = await calculateFunding(dbPool, '00000000-0000-0000-0000-000000000000');
expect(response.allocatedFunding).toBeNull();
expect(response.remainingFunding).toBeNull();
expect(response.error).toEqual(expect.any(String));
});

test('calculateFunding returns the correct funding amount', async () => {
const response = await calculateFunding(dbPool, question?.id);
expect(response.allocatedFunding).toBeDefined();
expect(response.remainingFunding).toEqual(100000);
expect(response.error).toBeNull();
});

afterAll(async () => {
await cleanup(dbPool);
await dbConnection.end();
});
});
22 changes: 17 additions & 5 deletions src/services/funding-mechanism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import { NodePgDatabase } from 'drizzle-orm/node-postgres';
export async function calculateFunding(
dbPool: NodePgDatabase<typeof db>,
forumQuestionId: string,
): Promise<{ allocated_funding: { [key: string]: number }; remaining_funding: number }> {
): Promise<{
allocatedFunding: { [key: string]: number } | null;
remainingFunding: number | null;
error: string | null;
}> {
const getOptionData = await dbPool
.select({
id: db.options.id,
Expand All @@ -24,15 +28,23 @@ export async function calculateFunding(
.from(db.options)
.where(eq(db.options.questionId, forumQuestionId));

if (!getOptionData) {
throw new Error('Error in query getOptionData');
if (getOptionData.length === 0) {
return {
allocatedFunding: null,
remainingFunding: null,
error: 'Error in query getOptionData',
};
}

const funding = allocateFunding(100000, 10000, getOptionData);

if (!funding) {
throw new Error('Error in allocating funding');
return { allocatedFunding: null, remainingFunding: null, error: 'Error in allocating funding' };
}

return funding;
return {
allocatedFunding: funding.allocated_funding,
remainingFunding: funding.remaining_funding,
error: null,
};
}
184 changes: 162 additions & 22 deletions src/services/users.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,166 @@
import * as db from '../db';
import { createDbClient } from '../utils/db/create-db-connection';
import { runMigrations } from '../utils/db/run-migrations';
import { environmentVariables, insertUserSchema } from '../types';
import { cleanup, seed } from '../utils/db/seed';
import { updateUser, upsertUserData, validateUserData } from './users';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { Client } from 'pg';
import { z } from 'zod';
import { insertUserSchema } from '../types/users';

describe('service: users', function () {
describe('schema: insertUserSchema', function () {
it('should remove empty strings from user data', function () {
const user: z.infer<typeof insertUserSchema> = {
email: '',
username: '',
firstName: '',
lastName: '',
telegram: '',
};

const transformedUser: { [key: string]: string | null | string[] | object } =
insertUserSchema.parse(user);

// loop through all keys and check if they are not empty strings

for (const key of Object.keys(transformedUser)) {
console.log(key);
expect(transformedUser[key]).not.toBe('');
}

describe('service: users', () => {
let dbPool: NodePgDatabase<typeof db>;
let dbConnection: Client;
let userData: {
email: string | null;
username: string | null;
firstName: string | null;
lastName: string | null;
telegram: string | null;
};
let user: db.User;
let secondUser: db.User;
beforeAll(async () => {
const envVariables = environmentVariables.parse(process.env);
const initDb = await createDbClient({
database: envVariables.DATABASE_NAME,
host: envVariables.DATABASE_HOST,
password: envVariables.DATABASE_PASSWORD,
user: envVariables.DATABASE_USER,
port: envVariables.DATABASE_PORT,
});

await runMigrations({
database: envVariables.DATABASE_NAME,
host: envVariables.DATABASE_HOST,
password: envVariables.DATABASE_PASSWORD,
user: envVariables.DATABASE_USER,
port: envVariables.DATABASE_PORT,
});

dbPool = initDb.db;
dbConnection = initDb.client;
// seed
const { users } = await seed(dbPool);
user = users[0]!;
secondUser = users[1]!;
});

test('should remove empty strings from user data', function () {
const user: z.infer<typeof insertUserSchema> = {
email: '',
username: '',
firstName: '',
lastName: '',
telegram: '',
};

const transformedUser: { [key: string]: string | null | string[] | object } =
insertUserSchema.parse(user);

// Loop through all keys and check if they are not empty strings
for (const key of Object.keys(transformedUser)) {
expect(transformedUser[key]).not.toBe('');
}
});

test('validateUserData returns an error if email already exists', async () => {
userData = {
email: secondUser?.email ?? null,
username: user?.username ?? null,
firstName: user?.firstName ?? null,
lastName: user?.lastName ?? null,
telegram: user?.telegram ?? null,
};

const response = await validateUserData(dbPool, user?.id, userData);
expect(response).toBeDefined();
expect(response).toEqual(expect.arrayContaining([expect.any(String)]));
});

test('validateUserData returns an error if username already exists', async () => {
userData = {
email: user?.email ?? null,
username: secondUser?.username ?? null,
firstName: user?.firstName ?? null,
lastName: user?.lastName ?? null,
telegram: user?.telegram ?? null,
};

const response = await validateUserData(dbPool, user?.id, userData);
expect(response).toBeDefined();
expect(response).toEqual(expect.arrayContaining([expect.any(String)]));
});

test('validateUserData returns null if validation is successful', async () => {
userData = {
email: user?.email ?? null,
username: user?.username ?? null,
firstName: 'Some Name' ?? null,
lastName: 'Some Other Name' ?? null,
telegram: user?.telegram ?? null,
};

const response = await validateUserData(dbPool, user?.id, userData);
expect(response).toBeNull();
});

test('upsertUserData returns updated user data if insertion is successful', async () => {
userData = {
email: user?.email ?? null,
username: user?.username ?? null,
firstName: 'Some Name' ?? null,
lastName: 'Some Other Name' ?? null,
telegram: user?.telegram ?? null,
};

const response = await upsertUserData(dbPool, user?.id, userData);
expect(response).toBeDefined();
expect(Array.isArray(response)).toBe(true);
const updatedUser = response![0];
expect(updatedUser!.firstName).toBe('Some Name');
expect(updatedUser!.lastName).toBe('Some Other Name');
});

test('updateUser returns the respective error if validation fails', async () => {
userData = {
email: user?.email ?? null,
username: secondUser?.username ?? null,
firstName: user?.firstName ?? null,
lastName: user?.lastName ?? null,
telegram: user?.telegram ?? null,
};
const mockData = {
userId: user?.id,
userData: userData,
};

const response = await updateUser(dbPool, mockData);
expect(response.errors).toBeDefined();
expect(response.errors![0]).toEqual(expect.any(String));
});

test('updateUser returns user data if validation and insertion succeeds', async () => {
userData = {
email: user?.email ?? null,
username: user?.username ?? null,
firstName: 'Some Name' ?? null,
lastName: 'Some Other Name' ?? null,
telegram: user?.telegram ?? null,
};
const mockData = {
userId: user?.id,
userData: userData,
};

const response = await updateUser(dbPool, mockData);
expect(response.data).toBeDefined();
expect(response.data![0]!.firstName).toBe('Some Name');
expect(response.data![0]!.lastName).toBe('Some Other Name');
});

afterAll(async () => {
await cleanup(dbPool);
await dbConnection.end();
});
});
4 changes: 2 additions & 2 deletions src/services/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { NodePgDatabase } from 'drizzle-orm/node-postgres';
* @param {UserData} userData - The user data to check.
* @returns {Promise<Array<string> | null>} - An array of errors if user data conflicts, otherwise null.
*/
async function validateUserData(
export async function validateUserData(
dbPool: NodePgDatabase<typeof db>,
userId: string,
userData: UserData,
Expand Down Expand Up @@ -51,7 +51,7 @@ async function validateUserData(
* @param {string} userId - The ID of the user to update.
* @param {UserData} userData - The updated user data.
*/
async function upsertUserData(
export async function upsertUserData(
dbPool: NodePgDatabase<typeof db>,
userId: string,
userData: UserData,
Expand Down

0 comments on commit b9a4888

Please sign in to comment.