Skip to content

Commit 219d5a1

Browse files
committed
unit test and improve comments for new versioned costing for licenses
1 parent a09c1e7 commit 219d5a1

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { compute_cost } from "./compute-cost";
2+
import { round2up } from "@cocalc/util/misc";
3+
4+
describe("use the compute-cost function with v1 pricing, no version, and a test version to compute the price of a license", () => {
5+
// This is a monthly business subscription for 3 projects with 1 cpu, 2 GB ram and 3 GB disk,
6+
// using v1 pricing. On the website right now it says this should cost:
7+
// "Cost: USD $27.15 monthly USD $9.05 per project"
8+
const monthly1 = 27.15;
9+
const info1 = {
10+
version: "1",
11+
end: new Date("2024-01-06T22:00:02.582Z"),
12+
type: "quota",
13+
user: "business",
14+
boost: false,
15+
start: new Date("2023-12-05T17:15:55.781Z"),
16+
upgrade: "custom",
17+
quantity: 3,
18+
account_id: "6aae57c6-08f1-4bb5-848b-3ceb53e61ede",
19+
custom_cpu: 1,
20+
custom_ram: 2,
21+
custom_disk: 3,
22+
subscription: "monthly",
23+
custom_member: true,
24+
custom_uptime: "short",
25+
custom_dedicated_cpu: 0,
26+
custom_dedicated_ram: 0,
27+
} as const;
28+
29+
it("computes the cost", () => {
30+
const cost1 = compute_cost(info1);
31+
expect(round2up(cost1.cost_sub_month * cost1.quantity)).toBe(monthly1);
32+
});
33+
34+
it("computes correct cost when version not given (since version 1 is the default)", () => {
35+
const info = { ...info1 };
36+
// @ts-ignore
37+
delete info["version"];
38+
const cost = compute_cost(info);
39+
expect(round2up(cost.cost_sub_month * cost.quantity)).toBe(monthly1);
40+
});
41+
42+
it("computes correct cost with a different version of pricing params", () => {
43+
const info = { ...info1 };
44+
// @ts-ignore
45+
info.version = "test_1";
46+
const cost = compute_cost(info);
47+
expect(round2up(cost.cost_sub_month * cost.quantity)).toBe(54.29);
48+
});
49+
});
50+
51+
describe("a couple more consistency checks with prod", () => {
52+
// each price below comes from just configuring this on prod
53+
54+
it("computes the cost of a yearly academic license sub", () => {
55+
const yearly = 306.98; // from prod store
56+
const info = {
57+
version: "1",
58+
end: new Date("2024-01-06T22:00:02.582Z"),
59+
type: "quota",
60+
user: "academic",
61+
boost: false,
62+
start: new Date("2023-12-05T17:15:55.781Z"),
63+
upgrade: "custom",
64+
quantity: 3,
65+
account_id: "6aae57c6-08f1-4bb5-848b-3ceb53e61ede",
66+
custom_cpu: 2,
67+
custom_ram: 2,
68+
custom_disk: 3,
69+
subscription: "yearly",
70+
custom_member: true,
71+
custom_uptime: "short",
72+
custom_dedicated_cpu: 0,
73+
custom_dedicated_ram: 0,
74+
} as const;
75+
const cost = compute_cost(info);
76+
expect(round2up(cost.cost_sub_year * cost.quantity)).toBe(yearly);
77+
});
78+
79+
it("computes the cost of a specific period academic license", () => {
80+
const amount = 29.61; // from prod store
81+
const info = {
82+
version: "1",
83+
start: new Date("2024-08-01T00:00:00.000Z"),
84+
type: "quota",
85+
user: "academic",
86+
boost: false,
87+
end: new Date("2024-08-31T00:00:00.000Z"),
88+
upgrade: "custom",
89+
quantity: 3,
90+
account_id: "6aae57c6-08f1-4bb5-848b-3ceb53e61ede",
91+
custom_cpu: 2,
92+
custom_ram: 2,
93+
custom_disk: 3,
94+
subscription: "no",
95+
custom_member: true,
96+
custom_uptime: "short",
97+
custom_dedicated_cpu: 0,
98+
custom_dedicated_ram: 0,
99+
} as const;
100+
const cost = compute_cost(info);
101+
expect(round2up(cost.cost)).toBe(amount);
102+
});
103+
});

src/packages/util/licenses/purchase/compute-cost.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,23 @@ import {
1010
} from "@cocalc/util/consts/site-license";
1111
import { BASIC, getCosts, MAX, STANDARD } from "./consts";
1212
import { dedicatedPrice } from "./dedicated-price";
13-
import { Cost, PurchaseInfo } from "./types";
14-
13+
import type { Cost, PurchaseInfo } from "./types";
14+
15+
// NOTE: the PurchaseInfo object optionally has a "version" field in it.
16+
// If the version is not specified, then it defaults to "1", which is the version
17+
// when we started versioning prices. If it is something else, then different
18+
// cost parameters may be used in the algorithm below -- that's what's currently
19+
// implemented. However... maybe we want a new cost function entirely? That's
20+
// possible too:
21+
// - just call a new function for your new version below (that's the easy part), and
22+
// - there is frontend and other UI code that depends on the structure exported
23+
// by contst.ts, and anything that uses that MUST be updated accordingly. E.g.,
24+
// there are tables with example costs for various scenarios, stuff about academic
25+
// discounts, etc., and a completely different cost function would need to explain
26+
// all that differently to users.
27+
// OBVIOUSLY: NEVER EVER CHANGE the code or parameters that compute the value of
28+
// a specific version of a license! If you make any change, then you must assign a
29+
// new version number and also keep the old version around.
1530
export function compute_cost(info: PurchaseInfo): Cost {
1631
if (info.type === "disk" || info.type === "vm") {
1732
return compute_cost_dedicated(info);

src/packages/util/licenses/purchase/cost-versions.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
// the value of any existing licenses. For pay-as-you-go, on the other hand, the charges
44
// are always short live and ephemeral, and the parameters for them are in the database.
55

6+
// OBVIOUSLY: NEVER EVER CHANGE the existing parameters that define the value of
7+
// a specific released version of a license! If you make any change, then you must assign a
8+
// new version number and also keep the old version around!!!!
9+
610
const COST = {
711
1: {
812
// Subscription discount
@@ -45,6 +49,24 @@ const COST = {
4549
// easily be used about half of the time during a week in a large class.
4650
ALWAYS_RUNNING_FACTOR: 2,
4751
},
52+
53+
// this version is PURELY for testing purposes
54+
test_1: {
55+
SUB_DISCOUNT: { no: 1, monthly: 0.9, yearly: 0.85 },
56+
GCE_COSTS: {
57+
ram: 0.67,
58+
cpu: 5,
59+
disk: 0.04,
60+
non_pre_factor: 3.5,
61+
},
62+
COST_MULTIPLIER: 1.6, // double version 1
63+
NONMEMBER_DENSITY: 2,
64+
ACADEMIC_DISCOUNT: 0.6,
65+
DISK_FACTOR: 10,
66+
RAM_OVERCOMMIT: 5,
67+
CPU_OVERCOMMIT: 10,
68+
ALWAYS_RUNNING_FACTOR: 2,
69+
},
4870
} as const;
4971

5072
export default COST;

0 commit comments

Comments
 (0)