158 lines
4.8 KiB
JavaScript
158 lines
4.8 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
do_get_profile(); // ChatStore touches PathUtils.profileDir at module load
|
|
|
|
const { TelemetryScheduler } = ChromeUtils.importESModule(
|
|
"moz-src:///browser/components/aiwindow/models/TelemetryManager.sys.mjs"
|
|
);
|
|
const { TelemetryEngine } = ChromeUtils.importESModule(
|
|
"moz-src:///browser/components/aiwindow/models/TelemetryUtils.sys.mjs"
|
|
);
|
|
const { ChatStore } = ChromeUtils.importESModule(
|
|
"moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs"
|
|
);
|
|
const { TestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/TestUtils.sys.mjs"
|
|
);
|
|
const { sinon } = ChromeUtils.importESModule(
|
|
"resource://testing-common/Sinon.sys.mjs"
|
|
);
|
|
const { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
|
|
// Mirrors the (non-exported) constant in TelemetryManager.sys.mjs.
|
|
const LAST_RUN_PREF = "browser.smartwindow.lastLLMTelemetryRunTime";
|
|
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref(LAST_RUN_PREF);
|
|
});
|
|
|
|
add_task(async function test_scheduler_skips_run_when_last_run_is_recent() {
|
|
// Non-zero pref disables the first-run immediate execution path: the
|
|
// scheduler only starts the interval and should not fetch conversations.
|
|
Services.prefs.setIntPref(LAST_RUN_PREF, Math.floor(Date.now() / 1000));
|
|
const sb = sinon.createSandbox();
|
|
const fetchStub = sb
|
|
.stub(ChatStore, "getConversationsForTelemetry")
|
|
.resolves([]);
|
|
|
|
const scheduler = TelemetryScheduler.maybeInit();
|
|
try {
|
|
// Give the async #init a chance to run.
|
|
await new Promise(r => Services.tm.dispatchToMainThread(r));
|
|
Assert.ok(
|
|
fetchStub.notCalled,
|
|
"should not fetch conversations when pref indicates a recent run"
|
|
);
|
|
} finally {
|
|
scheduler.destroy();
|
|
sb.restore();
|
|
}
|
|
});
|
|
|
|
add_task(async function test_scheduler_first_run_processes_conversation() {
|
|
Services.fog.testResetFOG();
|
|
Services.prefs.setIntPref(LAST_RUN_PREF, 0); // forces first-run path
|
|
const sb = sinon.createSandbox();
|
|
const conv = { id: "c1", currentTurnIndex: () => 7 };
|
|
|
|
sb.stub(ChatStore, "getConversationsForTelemetry").resolves([
|
|
{
|
|
convId: "c1",
|
|
modelId: "model",
|
|
telemetryJobs: { wasSuccessful: 5 },
|
|
telemetryProbs: { wasSuccessful: 0.5 },
|
|
uniformSamplingProbability: 0.1,
|
|
},
|
|
]);
|
|
sb.stub(ChatStore, "findConversationById").resolves(conv);
|
|
const markSpy = sb.stub(ChatStore, "markLLMTelemetryProcessed").resolves();
|
|
const runStub = sb
|
|
.stub(TelemetryEngine.prototype, "runTelemetryByName")
|
|
.resolves([
|
|
{
|
|
telemetry_name: "wasSuccessful",
|
|
result: { wasSuccessful: "yes" },
|
|
telemetry_version: 1,
|
|
},
|
|
]);
|
|
|
|
const scheduler = TelemetryScheduler.maybeInit();
|
|
try {
|
|
await TestUtils.waitForCondition(
|
|
() => markSpy.called,
|
|
"scheduler marked the conversation as processed"
|
|
);
|
|
|
|
Assert.deepEqual(
|
|
runStub.firstCall.args[0],
|
|
["wasSuccessful"],
|
|
"runTelemetryByName receives the telemetry-job names from the record"
|
|
);
|
|
Assert.equal(
|
|
markSpy.firstCall.args[0],
|
|
"c1",
|
|
"markLLMTelemetryProcessed receives the conversation id"
|
|
);
|
|
Assert.equal(
|
|
markSpy.firstCall.args[2],
|
|
7,
|
|
"markLLMTelemetryProcessed receives the current turn index"
|
|
);
|
|
} finally {
|
|
scheduler.destroy();
|
|
sb.restore();
|
|
}
|
|
});
|
|
|
|
add_task(async function test_destroy_stops_midloop_processing() {
|
|
Services.fog.testResetFOG();
|
|
Services.prefs.setIntPref(LAST_RUN_PREF, 0);
|
|
const sb = sinon.createSandbox();
|
|
const convIds = ["c1", "c2", "c3", "c4", "c5"];
|
|
|
|
sb.stub(ChatStore, "getConversationsForTelemetry").resolves(
|
|
convIds.map(id => ({
|
|
convId: id,
|
|
modelId: "m",
|
|
telemetryJobs: {},
|
|
telemetryProbs: {},
|
|
}))
|
|
);
|
|
let scheduler;
|
|
const findStub = sb
|
|
.stub(ChatStore, "findConversationById")
|
|
.callsFake(async id => {
|
|
if (id === "c2") {
|
|
scheduler.destroy();
|
|
}
|
|
return { id, currentTurnIndex: () => 0 };
|
|
});
|
|
sb.stub(ChatStore, "markLLMTelemetryProcessed").resolves();
|
|
sb.stub(TelemetryEngine.prototype, "runTelemetryByName").resolves([]);
|
|
|
|
scheduler = TelemetryScheduler.maybeInit();
|
|
try {
|
|
await TestUtils.waitForCondition(
|
|
() => findStub.callCount >= 2,
|
|
"loop advanced past the destroy point"
|
|
);
|
|
// Wait briefly to confirm no further iterations run.
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
await new Promise(r => setTimeout(r, 100));
|
|
Assert.lessOrEqual(
|
|
findStub.callCount,
|
|
2,
|
|
"destroy() prevents iterations beyond c2 (no c3-c5 lookups)"
|
|
);
|
|
} finally {
|
|
try {
|
|
scheduler.destroy();
|
|
} catch (e) {}
|
|
sb.restore();
|
|
}
|
|
});
|