Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: browser/components/aiwindow/models/tests/xpcshell/xpcshell.toml
/* 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();
const {
getRecentChats,
computeFreshnessScore,
_setBlockListManagerForTesting,
} = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs"
);
const { ChatStore, ChatMessage, MESSAGE_ROLE } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs"
);
const { sinon } = ChromeUtils.importESModule(
);
const MS_PER_DAY = 1000 * 60 * 60 * 24;
function normalizeToMs(value) {
if (value instanceof Date) {
return value.getTime();
}
if (typeof value === "number") {
return value;
}
// assume string (e.g. ISO date)
return Date.parse(value);
}
let sandbox;
add_setup(function () {
sandbox = sinon.createSandbox();
registerCleanupFunction(() => {
sandbox.restore();
});
});
// past date check
add_task(function test_computeFreshnessScore_past_date_check() {
const createdDate = new Date(Date.now() - 10 * MS_PER_DAY);
const score = computeFreshnessScore(createdDate, 7);
Assert.less(score, 0.5, "Freshness score should be < 0.5");
});
// future date check
add_task(function test_computeFreshnessScore_future_date_check() {
const createdDate = new Date(Date.now() + 1 * MS_PER_DAY);
const score = computeFreshnessScore(createdDate, 7);
Assert.equal(score, 1, "Freshness score should be 1");
});
// current date check
add_task(function test_computeFreshnessScore_current_date_check() {
const createdDate = new Date();
const score = computeFreshnessScore(createdDate, 7);
// allow tiny floating point / timing jitter
Assert.greaterOrEqual(score, 0.9999, "Freshness score should be ≈ 1");
Assert.lessOrEqual(score, 1, "Freshness score must be <= 1");
});
// approx halflife check
add_task(function test_computeFreshnessScore_halflife_approx_check() {
const createdDate = new Date(Date.now() - 7 * MS_PER_DAY);
const score = computeFreshnessScore(createdDate, 7);
// making sure that score in between 0.49 & 0.51 (closer to halflife)
Assert.less(score, 0.51, "Freshness score should be < 0.51");
Assert.greater(score, 0.49, "Freshness score should be > 0.49");
});
// older vs recent score check
add_task(function test_computeFreshnessScore_older_vs_recent_check() {
const olderDate = new Date(Date.now() - 30 * MS_PER_DAY);
const recentDate = new Date(Date.now() - 1 * MS_PER_DAY);
const olderScore = computeFreshnessScore(olderDate, 7);
const recentScore = computeFreshnessScore(recentDate, 7);
Assert.less(olderScore, recentScore, "Older score should be < recent score");
});
add_task(async function test_getRecentChats_basic_mapping_and_limit() {
const fixedNow = 1_700_000_000_000;
const clock = sandbox.useFakeTimers({ now: fixedNow });
const messages = [
new ChatMessage({
createdDate: fixedNow - 1_000,
ordinal: 1,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "msg1" },
pageUrl: "https://example.com/1",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 10_000,
ordinal: 2,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "msg2" },
pageUrl: "https://example.com/2",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 100_000,
ordinal: 3,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "msg3" },
pageUrl: "https://example.com/3",
turnIndex: 0,
}),
];
messages.forEach(msg => {
Assert.ok(
"createdDate" in msg,
"Test stub message should have createdDate (camelCase)"
);
Assert.ok(
msg.content &&
typeof msg.content === "object" &&
!Array.isArray(msg.content),
"msg.content should be an object, not an array"
);
Assert.ok("body" in msg.content, "msg.content should have a body field");
});
const maxResults = 3;
const halfLifeDays = 7;
const startTime = fixedNow - 1_000_000;
// Stub the method
const stub = sandbox
.stub(ChatStore, "findMessagesByDate")
.callsFake(async (startTimeArg, endTimeArg, roleArg, limitArg) => {
Assert.equal(
roleArg,
MESSAGE_ROLE.USER,
"Role passed to findMessagesByDate should be USER"
);
const startMs = normalizeToMs(startTimeArg);
const endMs = normalizeToMs(endTimeArg);
Assert.greaterOrEqual(endMs, startMs, "endTime should be >= startTime");
Assert.equal(limitArg, maxResults, "limit should match maxResults");
return messages;
});
try {
const result = await getRecentChats(startTime, maxResults, halfLifeDays);
// Assert stub was actually called
Assert.equal(stub.callCount, 1, "findMessagesByDate should be called once");
const [startTimeArg, , roleArg] = stub.firstCall.args;
Assert.equal(roleArg, MESSAGE_ROLE.USER, "Role should be USER");
const startMs = normalizeToMs(startTimeArg);
Assert.equal(
startMs,
fixedNow - 1_000_000,
"startTime should be fixedNow - 1_000_000 in ms"
);
Assert.equal(result.length, maxResults, "Should respect maxResults");
const first = result[0];
const second = result[1];
Assert.equal(first.content, "msg1");
Assert.equal(second.content, "msg2");
Assert.ok("freshness_score" in first);
Assert.greater(
first.freshness_score,
second.freshness_score,
"More recent message should have higher freshness_score"
);
} finally {
stub.restore();
clock.restore?.();
}
});
add_task(async function test_getRecentChats_filters_blocked_messages() {
const fixedNow = 1_700_000_000_000;
const clock = sandbox.useFakeTimers({ now: fixedNow });
// stub to lock any message containing the token "kmeOCKME" as a whole word.
_setBlockListManagerForTesting({
matchAtWordBoundary: ({ text }) => /\bblockme\b/.test(text),
});
const messages = [
new ChatMessage({
createdDate: fixedNow - 1_000,
ordinal: 1,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "hello blockme world" }, // should be filtered
pageUrl: "https://example.com/blocked",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 2_000,
ordinal: 2,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "hello normal world" }, // should remain
pageUrl: "https://example.com/ok",
turnIndex: 0,
}),
];
const maxResults = 50;
const halfLifeDays = 7;
const startTime = fixedNow - 1_000_000;
const stub = sandbox.stub(ChatStore, "findMessagesByDate").resolves(messages);
try {
const result = await getRecentChats(startTime, maxResults, halfLifeDays);
Assert.equal(stub.callCount, 1, "findMessagesByDate should be called once");
Assert.equal(result.length, 1, "Should filter out blocked message(s)");
Assert.equal(
result[0].content,
"hello normal world",
"Should keep unblocked message"
);
Assert.equal(
result[0].pageUrl,
"Should preserve pageUrl for unblocked message"
);
Assert.strictEqual(
typeof result[0].freshness_score,
"number",
"Should include freshness_score"
);
// Restore default behavior for any later tests
_setBlockListManagerForTesting({
matchAtWordBoundary: () => false,
});
} finally {
stub.restore();
clock.restore?.();
}
});
add_task(async function test_getRecentChats_filters_sensitive_info() {
const fixedNow = 1_700_000_000_000;
const clock = sandbox.useFakeTimers({ now: fixedNow });
_setBlockListManagerForTesting({
matchAtWordBoundary: () => false,
});
const messages = [
new ChatMessage({
createdDate: fixedNow - 1_000,
ordinal: 1,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Contact me at user@example.com" },
pageUrl: "https://example.com/contact",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 2_000,
ordinal: 2,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "My phone is 555-123-4567" },
pageUrl: "https://example.com/phone",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 3_000,
ordinal: 3,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Normal message without sensitive info" },
pageUrl: "https://example.com/normal",
turnIndex: 0,
}),
];
const maxResults = 50;
const halfLifeDays = 7;
const startTime = fixedNow - 1_000_000;
const stub = sandbox.stub(ChatStore, "findMessagesByDate").resolves(messages);
try {
const result = await getRecentChats(startTime, maxResults, halfLifeDays);
Assert.equal(stub.callCount, 1, "findMessagesByDate should be called once");
Assert.equal(
result.length,
1,
"Should filter out all messages with sensitive info"
);
Assert.equal(
result[0].content,
"Normal message without sensitive info",
"Should keep only the message without sensitive info"
);
Assert.equal(
result[0].pageUrl,
"Should preserve pageUrl for clean message"
);
const contentTexts = result.map(r => r.content);
Assert.ok(
!contentTexts.some(c => c.includes("user@example.com")),
"Should filter message with email in content"
);
Assert.ok(
!contentTexts.some(c => c.includes("555-123-4567")),
"Should filter message with phone number in content"
);
} finally {
stub.restore();
clock.restore?.();
}
});
add_task(async function test_getRecentChats_filters_sensitive_keywords() {
const fixedNow = 1_700_000_000_000;
const clock = sandbox.useFakeTimers({ now: fixedNow });
_setBlockListManagerForTesting({
matchAtWordBoundary: () => false,
});
const messages = [
new ChatMessage({
createdDate: fixedNow - 1_000,
ordinal: 1,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Looking for cancer treatment options" },
pageUrl: "https://example.com/medical",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 2_000,
ordinal: 2,
role: MESSAGE_ROLE.USER,
content: {
type: "text",
body: "How to improve my credit score quickly",
},
pageUrl: "https://example.com/finance",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 3_000,
ordinal: 3,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Finding a divorce attorney nearby" },
pageUrl: "https://example.com/legal",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 4_000,
ordinal: 4,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Best restaurants in San Francisco" },
pageUrl: "https://example.com/food",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 5_000,
ordinal: 5,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Democrat vs Republican tax policies" },
pageUrl: "https://example.com/politics",
turnIndex: 0,
}),
new ChatMessage({
createdDate: fixedNow - 6_000,
ordinal: 6,
role: MESSAGE_ROLE.USER,
content: { type: "text", body: "Pregnancy symptoms and early signs" },
pageUrl: "https://example.com/health",
turnIndex: 0,
}),
];
const maxResults = 50;
const halfLifeDays = 7;
const startTime = fixedNow - 1_000_000;
const stub = sandbox.stub(ChatStore, "findMessagesByDate").resolves(messages);
try {
const result = await getRecentChats(startTime, maxResults, halfLifeDays);
Assert.equal(stub.callCount, 1, "findMessagesByDate should be called once");
Assert.equal(
result.length,
1,
"Should filter out all messages with sensitive keywords"
);
Assert.equal(
result[0].content,
"Best restaurants in San Francisco",
"Should keep only the message without sensitive keywords"
);
const contentTexts = result.map(r => r.content);
Assert.ok(
!contentTexts.some(c => c.toLowerCase().includes("cancer")),
"Should filter message with medical keyword (cancer)"
);
Assert.ok(
!contentTexts.some(c => c.toLowerCase().includes("credit score")),
"Should filter message with finance keyword (credit score)"
);
Assert.ok(
!contentTexts.some(c => c.toLowerCase().includes("divorce")),
"Should filter message with legal keyword (divorce)"
);
Assert.ok(
!contentTexts.some(c => c.toLowerCase().includes("democrat")),
"Should filter message with political keyword (democrat)"
);
Assert.ok(
!contentTexts.some(c => c.toLowerCase().includes("pregnancy")),
"Should filter message with medical keyword (pregnancy)"
);
} finally {
stub.restore();
clock.restore?.();
}
});