Skip to content

Commit 72ec7c3

Browse files
Merge branch 'main' into ENG-46-enrichment
2 parents 7fb6ad4 + 6124ff8 commit 72ec7c3

27 files changed

+1250
-27
lines changed

.infra/crons.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,8 @@ export const crons: Cron[] = [
130130
name: 'post-analytics-history-day-clickhouse',
131131
schedule: '3-59/5 * * * *',
132132
},
133+
{
134+
name: 'clean-zombie-opportunities',
135+
schedule: '30 6 * * *',
136+
},
133137
];

__tests__/alerts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ describe('mutation clearOpportunityAlert', () => {
408408
it('should clear opportunityId from alerts', async () => {
409409
loggedUser = '1';
410410

411-
saveFixtures(con, OpportunityJob, [
411+
await saveFixtures(con, OpportunityJob, [
412412
{
413413
...opportunitiesFixture[0],
414414
id: '45bef485-ba42-4fd9-8c8c-a2ea4b2d1d62',

__tests__/boot.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ const LOGGED_IN_BODY = {
159159
clickbaitTries: null,
160160
hasLocationSet: false,
161161
location: null,
162+
hideExperience: false,
162163
},
163164
marketingCta: null,
164165
feeds: [],
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { cleanZombieOpportunities as cron } from '../../src/cron/cleanZombieOpportunities';
2+
import { expectSuccessfulCron, saveFixtures } from '../helpers';
3+
import { DataSource } from 'typeorm';
4+
import createOrGetConnection from '../../src/db';
5+
import { crons } from '../../src/cron/index';
6+
import {
7+
opportunitiesFixture,
8+
organizationsFixture,
9+
} from '../fixture/opportunity';
10+
import { randomUUID } from 'node:crypto';
11+
import { OpportunityJob } from '../../src/entity/opportunities/OpportunityJob';
12+
import { subDays } from 'date-fns';
13+
import { Organization } from '../../src/entity/Organization';
14+
import { Opportunity } from '../../src/entity/opportunities/Opportunity';
15+
16+
let con: DataSource;
17+
18+
beforeAll(async () => {
19+
con = await createOrGetConnection();
20+
});
21+
22+
beforeEach(async () => {
23+
jest.clearAllMocks();
24+
25+
await saveFixtures(con, Organization, organizationsFixture);
26+
27+
await saveFixtures(
28+
con,
29+
OpportunityJob,
30+
opportunitiesFixture.map((opportunity) => {
31+
return {
32+
...opportunity,
33+
id: randomUUID(),
34+
};
35+
}),
36+
);
37+
38+
await saveFixtures(
39+
con,
40+
OpportunityJob,
41+
opportunitiesFixture.map((opportunity) => {
42+
return {
43+
...opportunity,
44+
id: randomUUID(),
45+
organizationId: null,
46+
flags: { anonUserId: randomUUID() },
47+
createdAt: subDays(new Date(), 3),
48+
};
49+
}),
50+
);
51+
52+
await saveFixtures(
53+
con,
54+
OpportunityJob,
55+
opportunitiesFixture.map((opportunity) => {
56+
return {
57+
...opportunity,
58+
id: randomUUID(),
59+
organizationId: null,
60+
flags: { anonUserId: randomUUID() },
61+
createdAt: subDays(new Date(), 1),
62+
};
63+
}),
64+
);
65+
});
66+
67+
describe('cleanZombieOpportunities cron', () => {
68+
it('should be registered', () => {
69+
const registeredWorker = crons.find((item) => item.name === cron.name);
70+
71+
expect(registeredWorker).toBeDefined();
72+
});
73+
74+
it('should clean created opportunities not updated for more then 2 days', async () => {
75+
const opportunities = await con.getRepository(Opportunity).find();
76+
77+
expect(opportunities.length).toEqual(15);
78+
79+
const zombieOpportunitiesCount = opportunities.filter((opportunity) => {
80+
return (
81+
!(opportunity as OpportunityJob).organizationId &&
82+
opportunity.flags?.anonUserId &&
83+
opportunity.createdAt < subDays(new Date(), 2)
84+
);
85+
}).length;
86+
87+
expect(zombieOpportunitiesCount).toEqual(5);
88+
89+
await expectSuccessfulCron(cron);
90+
91+
const opportunitiesCount = await con
92+
.getRepository(OpportunityJob)
93+
.createQueryBuilder()
94+
.getCount();
95+
96+
expect(opportunitiesCount).toEqual(10);
97+
});
98+
});

__tests__/helpers.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ import {
4848
ExtractMarkdownResponse,
4949
ParseCVResponse,
5050
ParseError,
51+
ParseOpportunityResponse,
52+
Opportunity,
53+
OpportunityMeta,
54+
OpportunityContent,
55+
EmploymentType,
56+
SeniorityLevel,
57+
Salary,
58+
SalaryPeriod,
59+
OpportunityContent_ContentBlock,
60+
Location,
5161
} from '@dailydotdev/schema';
5262
import { createClient, type ClickHouseClient } from '@clickhouse/client';
5363
import * as clickhouseCommon from '../src/common/clickhouse';
@@ -67,6 +77,7 @@ export class MockContext extends Context {
6777
logger: FastifyLoggerInstance;
6878
contentLanguage: ContentLanguage;
6979
mockRegion: string;
80+
mockTrackingId: string | undefined;
7081

7182
constructor(
7283
con: DataSource,
@@ -76,6 +87,7 @@ export class MockContext extends Context {
7687
isTeamMember = false,
7788
isPlus = false,
7889
region = '',
90+
trackingId: string | undefined = undefined,
7991
) {
8092
super(mock<FastifyRequest>(), con);
8193
this.mockSpan = mock<opentelemetry.Span>();
@@ -88,6 +100,7 @@ export class MockContext extends Context {
88100
this.mockIsPlus = isPlus;
89101
this.logger = mock<FastifyLoggerInstance>();
90102
this.mockRegion = region;
103+
this.mockTrackingId = trackingId;
91104

92105
if (req?.headers['content-language']) {
93106
this.contentLanguage = req.headers['content-language'] as ContentLanguage;
@@ -103,7 +116,7 @@ export class MockContext extends Context {
103116
}
104117

105118
get trackingId(): string | null {
106-
return this.mockUserId;
119+
return this.mockTrackingId || this.mockUserId;
107120
}
108121

109122
get isTeamMember(): boolean {
@@ -465,6 +478,54 @@ export const createMockBrokkrTransport = () =>
465478
]),
466479
});
467480
},
481+
parseOpportunity: () => {
482+
return new ParseOpportunityResponse({
483+
opportunity: new Opportunity({
484+
title: 'Mocked Opportunity Title',
485+
tldr: 'This is a mocked TL;DR of the opportunity.',
486+
keywords: ['mock', 'opportunity', 'test'],
487+
meta: new OpportunityMeta({
488+
employmentType: EmploymentType.FULL_TIME,
489+
seniorityLevel: SeniorityLevel.SENIOR,
490+
roleType: 0.5,
491+
salary: new Salary({
492+
min: BigInt(1000),
493+
max: BigInt(2000),
494+
currency: 'USD',
495+
period: SalaryPeriod.MONTHLY,
496+
}),
497+
}),
498+
location: [
499+
new Location({
500+
country: 'USA',
501+
city: 'San Francisco',
502+
subdivision: 'CA',
503+
type: 1,
504+
}),
505+
],
506+
content: new OpportunityContent({
507+
overview: new OpportunityContent_ContentBlock({
508+
content: 'This is the overview of the mocked opportunity.',
509+
}),
510+
responsibilities: new OpportunityContent_ContentBlock({
511+
content:
512+
'These are the responsibilities of the mocked opportunity.',
513+
}),
514+
requirements: new OpportunityContent_ContentBlock({
515+
content:
516+
'These are the requirements of the mocked opportunity.',
517+
}),
518+
whatYoullDo: new OpportunityContent_ContentBlock({
519+
content: 'This is what you will do in the mocked opportunity.',
520+
}),
521+
interviewProcess: new OpportunityContent_ContentBlock({
522+
content:
523+
'This is the interview process of the mocked opportunity.',
524+
}),
525+
}),
526+
}),
527+
});
528+
},
468529
});
469530
});
470531

0 commit comments

Comments
 (0)