Skip to content

Commit

Permalink
Merge branch 'staging' into feature_new_qf_filters_table
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlosQ96 authored Oct 3, 2023
2 parents 3620351 + 86fea20 commit 4ca466b
Show file tree
Hide file tree
Showing 15 changed files with 761 additions and 37 deletions.
3 changes: 3 additions & 0 deletions config/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,6 @@ POWER_BALANCE_AGGREGATOR_ADAPTER=powerBalanceAggregator
NUMBER_OF_BALANCE_AGGREGATOR_BATCH=20

QF_ROUND_ESTIMATED_MATCHING_CACHE_DURATION=60000

# OPTIONAL - default: Every 10 minutes
PROJECT_CAMPAIGNS_CACHE_DURATION=600000
2 changes: 2 additions & 0 deletions config/test.env
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,5 @@ NUMBER_OF_BALANCE_AGGREGATOR_BATCH=7

# ! millisecond cache, if we increase cache in test ENV we might get some errors in tests
QF_ROUND_ESTIMATED_MATCHING_CACHE_DURATION=1
# ! millisecond cache, if we increase cache in test ENV we might get some errors in tests
PROJECT_CAMPAIGNS_CACHE_DURATION=1
17 changes: 17 additions & 0 deletions src/entities/campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,26 @@ export enum CampaignFilterField {
}

export enum CampaignType {
// https://github.com/Giveth/impact-graph/blob/staging/docs/campaignsInstruction.md

// In these type of projects we pick some projects to show them in campaign,
// for instance for Turkey earthquake we pick some projects.
// so we just need to add slug of those projects in Related Projects Slugs and in
// what order we add them they will be shown in frontend
ManuallySelected = 'ManuallySelected',

// Sometimes in a campaign we just want to show projects in an specified order,
// for instance we can create a campaign like ** Check projects that received most likes** so for
// this campaign you set SortField as campaign type and then you can use one of below sorting fields
SortField = 'SortField',

// Sometimes we need to filter some projects in a campaign,
// for instance Let's verified projects that accept funds on Gnosis chain,
// for this we can Add verified and acceptFundOnGnosis filters
FilterFields = 'FilterFields',

// Some campaigns don't include any project in them and they are just some banner
// like Feeling $nice? campaign in below image
WithoutProjects = 'WithoutProjects',
}

Expand Down
193 changes: 193 additions & 0 deletions src/repositories/donationRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
findDonationsByTransactionId,
findStableCoinDonationsWithoutPrice,
getPendingDonationsIds,
isVerifiedDonationExistsInQfRound,
sumDonationValueUsd,
sumDonationValueUsdForQfRound,
} from './donationRepository';
Expand Down Expand Up @@ -64,6 +65,10 @@ describe(
describe('countUniqueDonors() test cases', countUniqueDonorsTestCases);
describe('sumDonationValueUsd() test cases', sumDonationValueUsdTestCases);
describe('estimatedMatching() test cases', estimatedMatchingTestCases);
describe(
'isVerifiedDonationExistsInQfRound() test cases',
isVerifiedDonationExistsInQfRoundTestCases,
);

function fillQfRoundDonationsUserScoresTestCases() {
let qfRound: QfRound;
Expand Down Expand Up @@ -1262,3 +1267,191 @@ function sumDonationValueUsdTestCases() {
assert.equal(donationSum, valueUsd1 + valueUsd2 + valueUsd3);
});
}

function isVerifiedDonationExistsInQfRoundTestCases() {
it('should return true when there is a verified donation', async () => {
const project = await saveProjectDirectlyToDb({
...createProjectData(),
title: String(new Date().getTime()),
slug: String(new Date().getTime()),
});
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
allocatedFund: 100,
minimumPassportScore: 12,
beginDate: new Date(),
endDate: moment().add(10, 'days').toDate(),
}).save();
project.qfRounds = [qfRound];
await project.save();
const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress());

await saveDonationDirectlyToDb(
{
...createDonationData(),
status: 'verified',
qfRoundId: qfRound.id,
},
donor.id,
project.id,
);
const doesExist = await isVerifiedDonationExistsInQfRound({
projectId: project.id,
qfRoundId: qfRound.id,
userId: donor.id,
});
assert.isTrue(doesExist);

qfRound.isActive = false;
await qfRound.save();
});
it('should return false when there is a non-verified donation', async () => {
const project = await saveProjectDirectlyToDb({
...createProjectData(),
title: String(new Date().getTime()),
slug: String(new Date().getTime()),
});
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
allocatedFund: 100,
minimumPassportScore: 12,
beginDate: new Date(),
endDate: moment().add(10, 'days').toDate(),
}).save();
project.qfRounds = [qfRound];
await project.save();
const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress());

await saveDonationDirectlyToDb(
{
...createDonationData(),
status: 'pending',
qfRoundId: qfRound.id,
},
donor.id,
project.id,
);
const doesExist = await isVerifiedDonationExistsInQfRound({
projectId: project.id,
qfRoundId: qfRound.id,
userId: donor.id,
});
assert.isFalse(doesExist);

qfRound.isActive = false;
await qfRound.save();
});
it('should return false when sending invalid userId', async () => {
const project = await saveProjectDirectlyToDb({
...createProjectData(),
title: String(new Date().getTime()),
slug: String(new Date().getTime()),
});
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
allocatedFund: 100,
minimumPassportScore: 12,
beginDate: new Date(),
endDate: moment().add(10, 'days').toDate(),
}).save();
project.qfRounds = [qfRound];
await project.save();
const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress());

await saveDonationDirectlyToDb(
{
...createDonationData(),
status: 'verified',
qfRoundId: qfRound.id,
},
donor.id,
project.id,
);
const doesExist = await isVerifiedDonationExistsInQfRound({
projectId: project.id,
qfRoundId: qfRound.id,
userId: 999999,
});
assert.isFalse(doesExist);

qfRound.isActive = false;
await qfRound.save();
});
it('should return false when sending invalid projectId', async () => {
const project = await saveProjectDirectlyToDb({
...createProjectData(),
title: String(new Date().getTime()),
slug: String(new Date().getTime()),
});
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
allocatedFund: 100,
minimumPassportScore: 12,
beginDate: new Date(),
endDate: moment().add(10, 'days').toDate(),
}).save();
project.qfRounds = [qfRound];
await project.save();
const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress());

await saveDonationDirectlyToDb(
{
...createDonationData(),
status: 'verified',
qfRoundId: qfRound.id,
},
donor.id,
project.id,
);
const doesExist = await isVerifiedDonationExistsInQfRound({
projectId: 9999900,
qfRoundId: qfRound.id,
userId: donor.id,
});
assert.isFalse(doesExist);

qfRound.isActive = false;
await qfRound.save();
});
it('should return false when sending invalid qfRoundId', async () => {
const project = await saveProjectDirectlyToDb({
...createProjectData(),
title: String(new Date().getTime()),
slug: String(new Date().getTime()),
});
const qfRound = await QfRound.create({
isActive: true,
name: new Date().toString(),
allocatedFund: 100,
minimumPassportScore: 12,
beginDate: new Date(),
endDate: moment().add(10, 'days').toDate(),
}).save();
project.qfRounds = [qfRound];
await project.save();
const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress());

await saveDonationDirectlyToDb(
{
...createDonationData(),
status: 'verified',
qfRoundId: qfRound.id,
},
donor.id,
project.id,
);
const doesExist = await isVerifiedDonationExistsInQfRound({
projectId: project.id,
qfRoundId: 9999999,
userId: donor.id,
});
assert.isFalse(doesExist);

qfRound.isActive = false;
await qfRound.save();
});
}
31 changes: 31 additions & 0 deletions src/repositories/donationRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import moment from 'moment';
import { ProjectEstimatedMatchingView } from '../entities/ProjectEstimatedMatchingView';
import { AppDataSource } from '../orm';
import { getProjectDonationsSqrtRootSum } from './qfRoundRepository';
import { logger } from '../utils/logger';

export const fillQfRoundDonationsUserScores = async (): Promise<void> => {
await Donation.query(`
Expand Down Expand Up @@ -379,3 +380,33 @@ export async function sumDonationValueUsd(projectId: number): Promise<number> {

return result[0]?.sumVerifiedDonations || 0;
}

export async function isVerifiedDonationExistsInQfRound(params: {
qfRoundId: number;
projectId: number;
userId: number;
}): Promise<boolean> {
try {
const result = await Donation.query(
`
SELECT EXISTS (
SELECT 1
FROM donation
WHERE
status = 'verified' AND
"qfRoundId" = $1 AND
"projectId" = $2 AND
"userId" = $3
) AS exists;
`,
[params.qfRoundId, params.projectId, params.userId],
);
return result?.[0]?.exists || false;
} catch (err) {
logger.error(
'Error executing the query in isVerifiedDonationExists() function',
err,
);
return false;
}
}
47 changes: 36 additions & 11 deletions src/repositories/projectRepository.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
findProjectById,
findProjectBySlug,
findProjectBySlugWithoutAnyJoin,
findProjectByWalletAddress,
findProjectsByIdArray,
findProjectsBySlugArray,
Expand Down Expand Up @@ -56,6 +57,20 @@ describe(
updateDescriptionSummaryTestCases,
);

describe('verifyProject test cases', verifyProjectTestCases);
describe('verifyMultipleProjects test cases', verifyMultipleProjectsTestCases);
describe('findProjectById test cases', findProjectByIdTestCases);
describe('findProjectsByIdArray test cases', findProjectsByIdArrayTestCases);
describe('findProjectBySlug test cases', findProjectBySlugTestCases);
describe(
'findProjectBySlugWithoutAnyJoin test cases',
findProjectBySlugWithoutAnyJoinTestCases,
);
describe(
'findProjectsBySlugArray test cases',
findProjectsBySlugArrayTestCases,
);

function projectsWithoutUpdateAfterTimeFrameTestCases() {
it('should return projects created a long time ago', async () => {
const superExpiredProject = await saveProjectDirectlyToDb({
Expand Down Expand Up @@ -96,22 +111,13 @@ function projectsWithoutUpdateAfterTimeFrameTestCases() {
});
}

describe('verifyProject test cases', verifyProjectTestCases);
describe('verifyMultipleProjects test cases', verifyMultipleProjectsTestCases);
describe('findProjectById test cases', findProjectByIdTestCases);
describe('findProjectsByIdArray test cases', findProjectsByIdArrayTestCases);
describe('findProjectBySlug test cases', findProjectBySlugTestCases);
describe(
'findProjectsBySlugArray test cases',
findProjectsBySlugArrayTestCases,
);

function findProjectBySlugTestCases() {
it('Should find project by id', async () => {
it('Should find project by slug', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const foundProject = await findProjectBySlug(project.slug as string);
assert.isOk(foundProject);
assert.equal(foundProject?.id, project.id);
assert.isOk(foundProject?.adminUser);
});

it('should not find project when project doesnt exists', async () => {
Expand All @@ -120,6 +126,25 @@ function findProjectBySlugTestCases() {
});
}

function findProjectBySlugWithoutAnyJoinTestCases() {
it('Should find project by slug', async () => {
const project = await saveProjectDirectlyToDb(createProjectData());
const foundProject = await findProjectBySlugWithoutAnyJoin(
project.slug as string,
);
assert.isOk(foundProject);
assert.equal(foundProject?.id, project.id);
assert.isNotOk(foundProject?.adminUser);
});

it('should not find project when project doesnt exists', async () => {
const foundProject = await findProjectBySlugWithoutAnyJoin(
new Date().toString(),
);
assert.isNull(foundProject);
});
}

function findProjectsBySlugArrayTestCases() {
it('Should find project multi projects by slug', async () => {
const project1 = await saveProjectDirectlyToDb(createProjectData());
Expand Down
11 changes: 11 additions & 0 deletions src/repositories/projectRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,17 @@ export const findProjectBySlug = (slug: string): Promise<Project | null> => {
);
};

export const findProjectBySlugWithoutAnyJoin = (
slug: string,
): Promise<Project | null> => {
// check current slug and previous slugs
return Project.createQueryBuilder('project')
.where(`:slug = ANY(project."slugHistory") or project.slug = :slug`, {
slug,
})
.getOne();
};

export const verifyMultipleProjects = async (params: {
verified: boolean;
projectsIds: string[] | number[];
Expand Down
1 change: 1 addition & 0 deletions src/repositories/qfRoundRepository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { QfRound } from '../entities/qfRound';
import { AppDataSource } from '../orm';
import { logger } from '../utils/logger';

const qfRoundEstimatedMatchingParamsCacheDuration = Number(
process.env.QF_ROUND_ESTIMATED_MATCHING_CACHE_DURATION || 60000,
Expand Down
Loading

0 comments on commit 4ca466b

Please sign in to comment.