Files
optima/tests/unit/computeProductsCacheTTL.test.ts
T

189 lines
6.0 KiB
TypeScript

import { describe, test, expect } from "bun:test";
import {
computeProductsCacheTTL,
PRODUCTS_TTL_HOT,
PRODUCTS_TTL_LAZY,
WON_LOST_STATUS_IDS,
} from "../../src/modules/algorithms/computeProductsCacheTTL";
const NOW = new Date("2026-03-02T12:00:00Z");
const DAY_MS = 24 * 60 * 60 * 1000;
describe("computeProductsCacheTTL", () => {
// -- Constants ----------------------------------------------------------
test("PRODUCTS_TTL_HOT is 45 seconds", () => {
expect(PRODUCTS_TTL_HOT).toBe(45_000);
});
test("PRODUCTS_TTL_LAZY is 20 minutes", () => {
expect(PRODUCTS_TTL_LAZY).toBe(1_200_000);
});
// -- Won/Lost status set ------------------------------------------------
test("WON_LOST_STATUS_IDS contains Won canonical ID (29) and Pending Won (49)", () => {
expect(WON_LOST_STATUS_IDS.has(29)).toBe(true);
expect(WON_LOST_STATUS_IDS.has(49)).toBe(true);
});
test("WON_LOST_STATUS_IDS contains Lost canonical ID (53) and Pending Lost (50)", () => {
expect(WON_LOST_STATUS_IDS.has(53)).toBe(true);
expect(WON_LOST_STATUS_IDS.has(50)).toBe(true);
});
test("WON_LOST_STATUS_IDS does not contain Active (58) or New (24)", () => {
expect(WON_LOST_STATUS_IDS.has(58)).toBe(false);
expect(WON_LOST_STATUS_IDS.has(24)).toBe(false);
});
// -- Rule 1: Won/Lost/Pending Won/Lost → null --------------------------
test("returns null for Won status (CW ID 29)", () => {
const result = computeProductsCacheTTL({
statusCwId: 29,
closedFlag: true,
closedDate: new Date(NOW.getTime() - 2 * DAY_MS),
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1 * DAY_MS),
now: NOW,
});
expect(result).toBeNull();
});
test("returns null for Pending Won status (CW ID 49)", () => {
const result = computeProductsCacheTTL({
statusCwId: 49,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1 * DAY_MS),
now: NOW,
});
expect(result).toBeNull();
});
test("returns null for Lost status (CW ID 53)", () => {
const result = computeProductsCacheTTL({
statusCwId: 53,
closedFlag: true,
closedDate: new Date(NOW.getTime() - 5 * DAY_MS),
expectedCloseDate: null,
lastUpdated: null,
now: NOW,
});
expect(result).toBeNull();
});
test("returns null for Pending Lost status (CW ID 50)", () => {
const result = computeProductsCacheTTL({
statusCwId: 50,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1 * DAY_MS),
now: NOW,
});
expect(result).toBeNull();
});
// -- Rule 2: Opp not cacheable → null ----------------------------------
test("returns null when opp is closed > 30 days (main cache null)", () => {
const result = computeProductsCacheTTL({
statusCwId: 58, // Active — but closed flag overrides
closedFlag: true,
closedDate: new Date(NOW.getTime() - 60 * DAY_MS),
expectedCloseDate: null,
lastUpdated: null,
now: NOW,
});
expect(result).toBeNull();
});
// -- Rule 3: Updated within 3 days → 15s -------------------------------
test("returns PRODUCTS_TTL_HOT when lastUpdated is within 3 days", () => {
const result = computeProductsCacheTTL({
statusCwId: 58,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1 * DAY_MS),
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_HOT);
});
test("returns PRODUCTS_TTL_HOT when lastUpdated is exactly 3 days ago", () => {
const result = computeProductsCacheTTL({
statusCwId: 58,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 3 * DAY_MS),
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_HOT);
});
// -- Rule 4: Everything else → 30 min ----------------------------------
test("returns PRODUCTS_TTL_LAZY when lastUpdated is > 3 days ago", () => {
const result = computeProductsCacheTTL({
statusCwId: 58,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 10 * DAY_MS),
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_LAZY);
});
test("returns PRODUCTS_TTL_LAZY when no lastUpdated is set", () => {
const result = computeProductsCacheTTL({
statusCwId: 58,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: null,
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_LAZY);
});
test("returns PRODUCTS_TTL_LAZY for recently-closed (within 30 days) non-won/lost", () => {
// Edge case: closedFlag true, but status is not Won/Lost (unusual but possible)
const result = computeProductsCacheTTL({
statusCwId: 56, // Internal Review (not won/lost)
closedFlag: true,
closedDate: new Date(NOW.getTime() - 10 * DAY_MS),
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 20 * DAY_MS),
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_LAZY);
});
// -- Rule priority: Won/Lost takes priority over recent activity --------
test("Won status takes priority even with very recent lastUpdated", () => {
const result = computeProductsCacheTTL({
statusCwId: 29,
closedFlag: true,
closedDate: new Date(NOW.getTime() - 1 * DAY_MS),
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1000), // 1 second ago
now: NOW,
});
expect(result).toBeNull();
});
// -- Null statusCwId (should not skip rule 1) ---------------------------
test("null statusCwId falls through to other rules", () => {
const result = computeProductsCacheTTL({
statusCwId: null,
closedFlag: false,
closedDate: null,
expectedCloseDate: null,
lastUpdated: new Date(NOW.getTime() - 1 * DAY_MS),
now: NOW,
});
expect(result).toBe(PRODUCTS_TTL_HOT);
});
});