mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-07-17 18:47:20 +02:00
Fix 9 bugs and add 103 tests for core anonymization, config, and routing (#669)
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
const { expect } = require("chai");
|
||||
|
||||
/**
|
||||
* Tests for Conference.toJSON() price calculation logic.
|
||||
*
|
||||
* Replicates the pricing algorithm from src/core/Conference.ts to test
|
||||
* the math in isolation without needing MongoDB.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Replicated price calculation from Conference.toJSON()
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function calculatePrice(plan, repositories, endDate) {
|
||||
const pricePerHourPerRepo = plan.pricePerRepository / 30;
|
||||
let price = 0;
|
||||
const today = new Date() > endDate ? endDate : new Date();
|
||||
|
||||
repositories.forEach((r) => {
|
||||
const removeDate =
|
||||
r.removeDate && r.removeDate < today ? r.removeDate : today;
|
||||
price +=
|
||||
(Math.max(removeDate.getTime() - r.addDate.getTime(), 0) /
|
||||
1000 /
|
||||
60 /
|
||||
60 /
|
||||
24) *
|
||||
pricePerHourPerRepo;
|
||||
});
|
||||
return price;
|
||||
}
|
||||
|
||||
function countActiveRepos(repositories) {
|
||||
return repositories.filter((r) => !r.removeDate).length;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("Conference price calculation", function () {
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
describe("basic pricing", function () {
|
||||
it("returns 0 for no repositories", function () {
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[],
|
||||
new Date(Date.now() + 30 * DAY_MS)
|
||||
);
|
||||
expect(price).to.equal(0);
|
||||
});
|
||||
|
||||
it("returns 0 for free plan", function () {
|
||||
const addDate = new Date(Date.now() - 10 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 0 },
|
||||
[{ addDate }],
|
||||
new Date(Date.now() + 30 * DAY_MS)
|
||||
);
|
||||
expect(price).to.equal(0);
|
||||
});
|
||||
|
||||
it("calculates price for one repo over 10 days", function () {
|
||||
const now = new Date();
|
||||
const addDate = new Date(now.getTime() - 10 * DAY_MS);
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate }],
|
||||
endDate
|
||||
);
|
||||
// pricePerHourPerRepo = 3/30 = 0.1 per day
|
||||
// duration ≈ 10 days
|
||||
// price ≈ 10 * 0.1 = 1.0
|
||||
expect(price).to.be.closeTo(1.0, 0.01);
|
||||
});
|
||||
|
||||
it("calculates price for multiple repos", function () {
|
||||
const now = new Date();
|
||||
const addDate1 = new Date(now.getTime() - 10 * DAY_MS);
|
||||
const addDate2 = new Date(now.getTime() - 5 * DAY_MS);
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate: addDate1 }, { addDate: addDate2 }],
|
||||
endDate
|
||||
);
|
||||
// repo1: 10 days * 0.1 = 1.0
|
||||
// repo2: 5 days * 0.1 = 0.5
|
||||
// total ≈ 1.5
|
||||
expect(price).to.be.closeTo(1.5, 0.01);
|
||||
});
|
||||
});
|
||||
|
||||
describe("removed repositories", function () {
|
||||
it("uses removeDate as end for removed repos", function () {
|
||||
const now = new Date();
|
||||
const addDate = new Date(now.getTime() - 10 * DAY_MS);
|
||||
const removeDate = new Date(now.getTime() - 5 * DAY_MS);
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate, removeDate }],
|
||||
endDate
|
||||
);
|
||||
// Only charged for 5 days (add to remove), not 10
|
||||
// 5 * 0.1 = 0.5
|
||||
expect(price).to.be.closeTo(0.5, 0.01);
|
||||
});
|
||||
|
||||
it("uses today if removeDate is in the future", function () {
|
||||
const now = new Date();
|
||||
const addDate = new Date(now.getTime() - 10 * DAY_MS);
|
||||
const removeDate = new Date(now.getTime() + 5 * DAY_MS); // future
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate, removeDate }],
|
||||
endDate
|
||||
);
|
||||
// removeDate is in the future, so today is used instead
|
||||
// ≈ 10 days * 0.1 = 1.0
|
||||
expect(price).to.be.closeTo(1.0, 0.01);
|
||||
});
|
||||
});
|
||||
|
||||
describe("expired conference", function () {
|
||||
it("caps at endDate when conference is expired", function () {
|
||||
const endDate = new Date(Date.now() - 5 * DAY_MS); // 5 days ago
|
||||
const addDate = new Date(endDate.getTime() - 10 * DAY_MS); // 15 days ago
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate }],
|
||||
endDate
|
||||
);
|
||||
// Conference ended 5 days ago, repo was added 10 days before that
|
||||
// Only charged for 10 days (add to end)
|
||||
// 10 * 0.1 = 1.0
|
||||
expect(price).to.be.closeTo(1.0, 0.01);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", function () {
|
||||
it("handles zero-duration repository (add and remove same time)", function () {
|
||||
const now = new Date();
|
||||
const addDate = new Date(now.getTime() - 5 * DAY_MS);
|
||||
const removeDate = addDate; // same time
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate, removeDate }],
|
||||
endDate
|
||||
);
|
||||
expect(price).to.equal(0);
|
||||
});
|
||||
|
||||
it("handles removeDate before addDate via Math.max", function () {
|
||||
const now = new Date();
|
||||
const addDate = new Date(now.getTime() - 5 * DAY_MS);
|
||||
const removeDate = new Date(now.getTime() - 10 * DAY_MS); // before addDate
|
||||
const endDate = new Date(now.getTime() + 20 * DAY_MS);
|
||||
const price = calculatePrice(
|
||||
{ pricePerRepository: 3 },
|
||||
[{ addDate, removeDate }],
|
||||
endDate
|
||||
);
|
||||
// Math.max ensures negative duration becomes 0
|
||||
expect(price).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Conference active repository count", function () {
|
||||
it("counts repos without removeDate", function () {
|
||||
const repos = [
|
||||
{ addDate: new Date() },
|
||||
{ addDate: new Date(), removeDate: new Date() },
|
||||
{ addDate: new Date() },
|
||||
];
|
||||
expect(countActiveRepos(repos)).to.equal(2);
|
||||
});
|
||||
|
||||
it("returns 0 when all repos are removed", function () {
|
||||
const repos = [
|
||||
{ addDate: new Date(), removeDate: new Date() },
|
||||
{ addDate: new Date(), removeDate: new Date() },
|
||||
];
|
||||
expect(countActiveRepos(repos)).to.equal(0);
|
||||
});
|
||||
|
||||
it("returns 0 for empty list", function () {
|
||||
expect(countActiveRepos([])).to.equal(0);
|
||||
});
|
||||
|
||||
it("counts all repos when none are removed", function () {
|
||||
const repos = [
|
||||
{ addDate: new Date() },
|
||||
{ addDate: new Date() },
|
||||
{ addDate: new Date() },
|
||||
];
|
||||
expect(countActiveRepos(repos)).to.equal(3);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user