Partially implemented the Manage Time functionality

This commit is contained in:
2026-10-16 19:50:39 -03:00
parent 2ae057cc90
commit d392e5ddd7
28 changed files with 2528 additions and 64 deletions

View File

@@ -0,0 +1,71 @@
const assert = require("node:assert/strict");
const fs = require("node:fs");
const vm = require("node:vm");
const { test } = require("node:test");
const { loadBackground } = require("./support/characterization.cjs");
function setup() {
let now = 100000, hours = 24, user = "a@example.invalid";
const factory = new vm.Script(fs.readFileSync("src/features/preferences/request-cache.js", "utf8") + "\ncreateFormRequestCache;").runInNewContext({ Date: { now: () => now } });
const cache = factory({ getHours: () => hours, getUserId: () => user });
return { cache, advance: value => { now += value; }, hours: value => { hours = value; }, user: value => { user = value; } };
}
const message = (name, fields = {}) => ({ type: `opportunitiesExtension.${name}`, ...fields });
test("all four form query families reuse successes with parameter-specific keys and live duration", async () => {
const h = setup(); let calls = 0;
const fetch = async request => { calls++; assert.equal(request.cacheMaxAgeMs, 24 * 3600000); return { ok: true, items: [{ name: "Original" }], debugRequests: [{ name: "network" }] }; };
for (const name of ["requestWeekDetails", "requestActiveServiceRequests", "requestServiceRequestExpand", "requestLookupTaskType", "requestActionTeamMembers", "requestActionFindLookup", "requestSrLookupPrimaryPillar", "requestSrLookupLanguage", "requestSrLookupTechPlatform", "requestSrLookupOpportunityBusiness", "requestLookupTypePlatform", "requestInternalSrServices"]) {
const msg = message(name, { id: 1 });
const result = await h.cache.send(msg, fetch); result.items[0].name = "Edited";
const cached = await h.cache.send(msg, fetch);
assert.equal(cached.fromCache, true); assert.equal(cached.items[0].name, "Original"); assert.equal(cached.debugRequests.length, 0);
await h.cache.send(message(name, { id: 2 }), fetch);
}
assert.equal(calls, 24);
h.advance(2 * 3600000); h.hours(1);
await h.cache.send(message("requestWeekDetails", { id: 1 }), async request => { calls++; assert.equal(request.cacheMaxAgeMs, 3600000); return { ok: true }; });
assert.equal(calls, 25);
});
test("failed results are never cached and identity changes discard previous responses", async () => {
const h = setup(); let calls = 0;
const send = async () => { calls++; return { ok: calls > 1 }; };
const msg = message("requestLookupTaskType");
await h.cache.send(msg, send); await h.cache.send(msg, send); await h.cache.send(msg, send); assert.equal(calls, 2);
h.user("b@example.invalid"); await h.cache.send(msg, send); assert.equal(calls, 3);
h.user(""); await h.cache.send(msg, send); await h.cache.send(msg, send); assert.equal(calls, 5);
});
test("writes and token requests always execute; successful saves and Refresh all invalidate queries", async () => {
for (const name of ["requestMgmtAction", "requestPostServiceRequest", "requestPostInternalServiceRequest", "invalidateProductsCache"]) {
const h = setup(); let queries = 0, writes = 0;
const query = message("requestActionFindLookup");
const queryTransport = async () => { queries++; return { ok: true }; };
await h.cache.send(query, queryTransport);
for (let i = 0; i < 2; i++) await h.cache.send(message(name, { phase: "confirm" }), async () => { writes++; return { ok: true }; });
await h.cache.send(query, queryTransport);
assert.equal(writes, 2); assert.equal(queries, 2);
}
const h = setup(); let calls = 0;
for (let i = 0; i < 2; i++) await h.cache.send(message("requestSrTokenCatalog"), async () => { calls++; return { ok: true }; });
assert.equal(calls, 2);
});
test("concurrent queries share work but old responses cannot repopulate an invalidated cache", async () => {
const h = setup(); let resolve, calls = 0;
const msg = message("requestLookupTypePlatform");
const transport = () => { calls++; return new Promise(done => { resolve = done; }); };
const first = h.cache.send(msg, transport), second = h.cache.send(msg, transport);
assert.equal(calls, 1); h.cache.invalidate(); resolve({ ok: true, items: [] }); await Promise.all([first, second]);
await h.cache.send(msg, async () => { calls++; return { ok: true }; }); assert.equal(calls, 2);
});
test("resource user respects duration and Refresh all invalidation", async () => {
let calls = 0;
const app = loadBackground({ fetch: async () => { calls++; return new Response(JSON.stringify({ resourcePartyId: 123 })); } });
const msg = message("requestResourceUser", { emailAddress: "a@example.invalid", cacheMaxAgeMs: 3600000 });
await app.dispatch(msg); await app.dispatch(msg); assert.equal(calls, 1);
app.advance(3600000); await app.dispatch(msg); assert.equal(calls, 2);
await app.dispatch(message("invalidateProductsCache")); await app.dispatch(msg); assert.equal(calls, 3);
});
test("Preferences displays generic cache duration while preserving the saved preference key", () => {
const source = fs.readFileSync("src/features/preferences/view.js", "utf8");
assert.match(source, /cacheLabel.textContent = "Cache duration"/);
assert.doesNotMatch(source, /Product cache duration/);
assert.match(source, /cacheSelect.name = "opptyProductsCacheHours"/);
});