Skip to content

Commit 4ddf703

Browse files
authored
feat: add work email verification for user experiences and related tests (#2968)
1 parent 2447ca3 commit 4ddf703

File tree

3 files changed

+307
-33
lines changed

3 files changed

+307
-33
lines changed

__tests__/userExperience.ts

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import createOrGetConnection from '../src/db';
1111
import { DataSource } from 'typeorm';
1212
import {
13+
completeVerificationForExperienceByUserCompany,
1314
DEFAULT_AUTOCOMPLETE_LIMIT,
1415
ExperienceAutocompleteType,
1516
getEmptyExperienceTypesMap,
@@ -21,8 +22,10 @@ import {
2122
ExperienceStatus,
2223
ProjectLinkType,
2324
UserExperienceType,
25+
WorkEmploymentType,
26+
WorkVerificationStatus,
2427
} from '../src/entity/user/experiences/types';
25-
import { User } from '../src/entity';
28+
import { User, UserCompany } from '../src/entity';
2629
import { usersFixture } from './fixture';
2730
import { UserAwardExperience } from '../src/entity/user/experiences/UserAwardExperience';
2831
import { UserEducationExperience } from '../src/entity/user/experiences/UserEducationExperience';
@@ -1380,3 +1383,224 @@ describe('user experience', () => {
13801383
});
13811384
});
13821385
});
1386+
1387+
describe('work experience verification flow', () => {
1388+
const companyId = 'company1';
1389+
const userId = '1';
1390+
let workExperienceId: string;
1391+
1392+
beforeEach(async () => {
1393+
await saveFixtures(con, User, usersFixture);
1394+
1395+
// Create test company
1396+
await saveFixtures(con, Company, [
1397+
{
1398+
id: companyId,
1399+
name: 'Test Company',
1400+
type: CompanyType.Business,
1401+
image: 'https://example.com/company.png',
1402+
domains: ['testcompany.com'],
1403+
},
1404+
]);
1405+
1406+
// Create a work experience that needs verification
1407+
const workExperience = await con.getRepository(UserWorkExperience).save({
1408+
userId,
1409+
title: 'Software Engineer',
1410+
status: ExperienceStatus.Published,
1411+
description: 'Working on awesome projects',
1412+
startDate: new Date('2023-01-01'),
1413+
companyId,
1414+
employmentType: WorkEmploymentType.FullTime,
1415+
verificationStatus: WorkVerificationStatus.Pending,
1416+
});
1417+
1418+
workExperienceId = workExperience.id;
1419+
});
1420+
1421+
it('should verify work experience when UserCompany is verified', async () => {
1422+
// Create a verified UserCompany record
1423+
const userCompany = await con.getRepository(UserCompany).save({
1424+
userId,
1425+
companyId,
1426+
1427+
code: '123456',
1428+
verified: true,
1429+
});
1430+
1431+
// Call the verification function
1432+
const result = await completeVerificationForExperienceByUserCompany(
1433+
con,
1434+
userCompany,
1435+
);
1436+
1437+
// Verify the result is true (verification was successful)
1438+
expect(result).toBe(true);
1439+
1440+
// Check that the work experience was updated
1441+
const updatedExperience = await con
1442+
.getRepository(UserWorkExperience)
1443+
.findOneBy({ id: workExperienceId });
1444+
1445+
expect(updatedExperience).toBeDefined();
1446+
expect(updatedExperience?.verificationStatus).toBe(
1447+
WorkVerificationStatus.Verified,
1448+
);
1449+
expect(updatedExperience?.verificationEmail).toBe('[email protected]');
1450+
});
1451+
1452+
it('should not verify work experience when UserCompany has no companyId', async () => {
1453+
// Create a UserCompany record without companyId
1454+
const userCompany = await con.getRepository(UserCompany).save({
1455+
userId,
1456+
companyId: null,
1457+
1458+
code: '123456',
1459+
verified: true,
1460+
});
1461+
1462+
// Call the verification function
1463+
const result = await completeVerificationForExperienceByUserCompany(
1464+
con,
1465+
userCompany,
1466+
);
1467+
1468+
// Verify the result is false (verification was not successful)
1469+
expect(result).toBe(false);
1470+
1471+
// Check that the work experience was not updated
1472+
const updatedExperience = await con
1473+
.getRepository(UserWorkExperience)
1474+
.findOneBy({ id: workExperienceId });
1475+
1476+
expect(updatedExperience).toBeDefined();
1477+
expect(updatedExperience?.verificationStatus).toBe(
1478+
WorkVerificationStatus.Pending,
1479+
);
1480+
});
1481+
1482+
it('should not verify work experience when no matching experience exists', async () => {
1483+
// Create a UserCompany record with a different companyId
1484+
await con.getRepository(Company).save({
1485+
id: 'different-company-id',
1486+
name: 'Test Company 2',
1487+
type: CompanyType.Business,
1488+
image: 'https://example.com/company2.png',
1489+
domains: ['testcompany2.com'],
1490+
});
1491+
const userCompany = await con.getRepository(UserCompany).save({
1492+
userId,
1493+
companyId: 'different-company-id',
1494+
1495+
code: '123456',
1496+
verified: true,
1497+
});
1498+
1499+
// Call the verification function
1500+
const result = await completeVerificationForExperienceByUserCompany(
1501+
con,
1502+
userCompany,
1503+
);
1504+
1505+
// Verify the result is false (verification was not successful)
1506+
expect(result).toBe(false);
1507+
1508+
// Check that the work experience was not updated
1509+
const updatedExperience = await con
1510+
.getRepository(UserWorkExperience)
1511+
.findOneBy({ id: workExperienceId });
1512+
1513+
expect(updatedExperience).toBeDefined();
1514+
expect(updatedExperience?.verificationStatus).toBe(
1515+
WorkVerificationStatus.Pending,
1516+
);
1517+
});
1518+
1519+
it('should not update already verified work experience', async () => {
1520+
// First, update the work experience to be already verified
1521+
await con.getRepository(UserWorkExperience).update(
1522+
{ id: workExperienceId },
1523+
{
1524+
verificationStatus: WorkVerificationStatus.Verified,
1525+
verificationEmail: '[email protected]',
1526+
},
1527+
);
1528+
1529+
// Create a UserCompany record
1530+
const userCompany = await con.getRepository(UserCompany).save({
1531+
userId,
1532+
companyId,
1533+
1534+
code: '123456',
1535+
verified: true,
1536+
});
1537+
1538+
// Call the verification function
1539+
const result = await completeVerificationForExperienceByUserCompany(
1540+
con,
1541+
userCompany,
1542+
);
1543+
1544+
// Verify the result is false (no update was needed)
1545+
expect(result).toBe(false);
1546+
1547+
// Check that the work experience was not updated
1548+
const updatedExperience = await con
1549+
.getRepository(UserWorkExperience)
1550+
.findOneBy({ id: workExperienceId });
1551+
1552+
expect(updatedExperience).toBeDefined();
1553+
expect(updatedExperience?.verificationStatus).toBe(
1554+
WorkVerificationStatus.Verified,
1555+
);
1556+
expect(updatedExperience?.verificationEmail).toBe('[email protected]');
1557+
});
1558+
1559+
it('should verify work experience when verifyUserCompanyCode is called', async () => {
1560+
loggedUser = '1';
1561+
1562+
// Create an unverified UserCompany record
1563+
await con.getRepository(UserCompany).save({
1564+
userId,
1565+
companyId,
1566+
1567+
code: '123456',
1568+
verified: false,
1569+
});
1570+
1571+
const MUTATION = `mutation VerifyUserCompanyCode($email: String!, $code: String!) {
1572+
verifyUserCompanyCode(email: $email, code: $code) {
1573+
email
1574+
}
1575+
}`;
1576+
1577+
// Call the verifyUserCompanyCode mutation (which internally calls completeVerificationForExperienceByUserCompany)
1578+
const res = await client.mutate(MUTATION, {
1579+
variables: { email: '[email protected]', code: '123456' },
1580+
});
1581+
expect(res.errors).toBeFalsy();
1582+
expect(res.data.verifyUserCompanyCode.email).toEqual(
1583+
1584+
);
1585+
1586+
const row = await con.getRepository(UserCompany).findOneBy({
1587+
1588+
});
1589+
expect(row?.verified).toBeTruthy();
1590+
1591+
// Check that the work experience was updated
1592+
const updatedExperience = await con
1593+
.getRepository(UserWorkExperience)
1594+
.findOneBy({ id: workExperienceId });
1595+
1596+
expect(updatedExperience).toBeDefined();
1597+
expect(updatedExperience).toEqual(
1598+
expect.objectContaining({
1599+
companyId,
1600+
userId,
1601+
verificationStatus: WorkVerificationStatus.Verified,
1602+
verificationEmail: '[email protected]',
1603+
}),
1604+
);
1605+
});
1606+
});

src/common/userExperience.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CompanyType } from '../entity/Company';
44
import {
55
ExperienceStatus,
66
UserExperienceType,
7+
WorkVerificationStatus,
78
} from '../entity/user/experiences/types';
89
import type { UserWorkExperience } from '../entity/user/experiences/UserWorkExperience';
910
import type { UserProjectExperience } from '../entity/user/experiences/UserProjectExperience';
@@ -21,6 +22,8 @@ import {
2122
userPublicationExperienceSchema,
2223
userWorkExperienceSchema,
2324
} from './schema/userExperience';
25+
import { DataSource, Not } from 'typeorm';
26+
import { UserCompany } from '../entity';
2427

2528
// Autocomplete
2629
export enum ExperienceAutocompleteType {
@@ -187,3 +190,27 @@ export const experienceTypeToRepositoryMap: Record<UserExperienceType, string> =
187190
[UserExperienceType.Publication]: 'UserPublicationExperience',
188191
[UserExperienceType.Course]: 'UserCourseExperience',
189192
};
193+
194+
// Work Email Verification
195+
export const completeVerificationForExperienceByUserCompany = async (
196+
con: DataSource,
197+
{ companyId, userId, email: verificationEmail }: UserCompany,
198+
): Promise<boolean> => {
199+
if (!companyId) return false;
200+
201+
const update = await con
202+
.getRepository<UserWorkExperience>('UserWorkExperience')
203+
.update(
204+
{
205+
userId,
206+
companyId,
207+
verificationStatus: Not(WorkVerificationStatus.Verified),
208+
},
209+
{
210+
verificationEmail,
211+
verificationStatus: WorkVerificationStatus.Verified,
212+
},
213+
);
214+
215+
return !!update?.affected;
216+
};

0 commit comments

Comments
 (0)