Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/**
* 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();
("use strict");
const { sinon } = ChromeUtils.importESModule(
);
const { MemoriesManager } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs"
);
const {
CATEGORIES,
INTENTS,
HISTORY: SOURCE_HISTORY,
CONVERSATION: SOURCE_CONVERSATION,
} = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/memories/MemoriesConstants.sys.mjs"
);
const { getFormattedMemoryAttributeList } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/models/memories/Memories.sys.mjs"
);
const { MemoryStore } = ChromeUtils.importESModule(
"moz-src:///browser/components/aiwindow/services/MemoryStore.sys.mjs"
);
/**
* Constants for test memories
*/
const TEST_MESSAGE = "Remember I like coffee.";
const TEST_MEMORIES = [
{
memory_summary: "Loves drinking coffee",
category: "Food & Drink",
intent: "Plan / Organize",
score: 3,
},
{
memory_summary: "Buys dog food online",
category: "Pets & Animals",
intent: "Buy / Acquire",
score: 4,
},
];
/**
* Constants for preference keys and test values
*/
const PREF_API_KEY = "browser.aiwindow.apiKey";
const PREF_ENDPOINT = "browser.aiwindow.endpoint";
const PREF_MODEL = "browser.aiwindow.model";
const API_KEY = "fake-key";
const ENDPOINT = "https://api.fake-endpoint.com/v1";
const MODEL = "fake-model";
/**
* Helper function to delete all memories before and after a test
*/
async function deleteAllMemories() {
const memories = await MemoryStore.getMemories({ includeSoftDeleted: true });
for (const memory of memories) {
await MemoryStore.hardDeleteMemory(memory.id);
}
}
/**
* Helper function to bulk-add memories
*/
async function addMemories() {
await deleteAllMemories();
for (const memory of TEST_MEMORIES) {
await MemoryStore.addMemory(memory);
}
}
add_setup(async function () {
// Setup prefs used across multiple tests
Services.prefs.setStringPref(PREF_API_KEY, API_KEY);
Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT);
Services.prefs.setStringPref(PREF_MODEL, MODEL);
// Clear prefs after testing
registerCleanupFunction(() => {
for (let pref of [PREF_API_KEY, PREF_ENDPOINT, PREF_MODEL]) {
if (Services.prefs.prefHasUserValue(pref)) {
Services.prefs.clearUserPref(pref);
}
}
});
});
/**
* Tests getting aggregated browser history from MemoriesHistorySource
*/
add_task(async function test_getAggregatedBrowserHistory() {
// Setup fake history data
const now = Date.now();
const seeded = [
{
title: "Google Search: firefox history",
visits: [{ date: new Date(now - 5 * 60 * 1000) }],
},
{
title: "Hacker News",
visits: [{ date: new Date(now - 15 * 60 * 1000) }],
},
{
title: "Internet for people, not profit — Mozilla",
visits: [{ date: new Date(now - 25 * 60 * 1000) }],
},
];
await PlacesUtils.history.clear();
await PlacesUtils.history.insertMany(seeded);
// Check that all 3 outputs are arrays
const [domainItems, titleItems, searchItems] =
await MemoriesManager.getAggregatedBrowserHistory();
Assert.ok(Array.isArray(domainItems), "Domain items should be an array");
Assert.ok(Array.isArray(titleItems), "Title items should be an array");
Assert.ok(Array.isArray(searchItems), "Search items should be an array");
// Check the length of each
Assert.equal(domainItems.length, 3, "Should have 3 domain items");
Assert.equal(titleItems.length, 3, "Should have 3 title items");
Assert.equal(searchItems.length, 1, "Should have 1 search item");
// Check the top entry in each aggregate
Assert.deepEqual(
domainItems[0],
["mozilla.org", 100],
"Top domain should be `mozilla.org' with score 100"
);
Assert.deepEqual(
titleItems[0],
["Internet for people, not profit — Mozilla", 100],
"Top title should be 'Internet for people, not profit — Mozilla' with score 100"
);
Assert.equal(
searchItems[0].q[0],
"Google Search: firefox history",
"Top search item query should be 'Google Search: firefox history'"
);
Assert.equal(searchItems[0].r, 1, "Top search item rank should be 1");
});
/**
* Tests retrieving all stored memories
*/
add_task(async function test_getAllMemories() {
await addMemories();
const memories = await MemoriesManager.getAllMemories();
// Check that the right number of memories were retrieved
Assert.equal(
memories.length,
TEST_MEMORIES.length,
"Should retrieve all stored memories."
);
// Check that the memories summaries are correct
const testMemoriesSummaries = TEST_MEMORIES.map(
memory => memory.memory_summary
);
const retrievedMemoriesSummaries = memories.map(
memory => memory.memory_summary
);
retrievedMemoriesSummaries.forEach(memorySummary => {
Assert.ok(
testMemoriesSummaries.includes(memorySummary),
`Memory summary "${memorySummary}" should be in the test memories.`
);
});
await deleteAllMemories();
});
/**
* Tests soft deleting a memory by ID
*/
add_task(async function test_softDeleteMemoryById() {
await addMemories();
// Pull memories that aren't already soft deleted
const memoriesBeforeSoftDelete = await MemoriesManager.getAllMemories();
// Pick a memory off the top to soft delete
const memoryBeforeSoftDelete = memoriesBeforeSoftDelete[0];
// Double check that the memory isn't already soft deleted
Assert.equal(
memoryBeforeSoftDelete.is_deleted,
false,
"Memory should not be soft deleted initially."
);
// Soft delete the memory
const memoryAfterSoftDelete = await MemoriesManager.softDeleteMemoryById(
memoryBeforeSoftDelete.id
);
// Check that the memory is soft deleted
Assert.equal(
memoryAfterSoftDelete.is_deleted,
true,
"Memory should be soft deleted after calling softDeleteMemoryById."
);
// Retrieve all memories again, including soft deleted ones this time to make sure the deletion saved correctly
const memoriesAfterSoftDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
const softDeletedMemories = memoriesAfterSoftDelete.filter(
memory => memory.is_deleted
);
Assert.equal(
softDeletedMemories.length,
1,
"There should be one soft deleted memory."
);
await deleteAllMemories();
});
/**
* Tests attempting to soft delete a memory that doesn't exist by ID
*/
add_task(async function test_softDeleteMemoryById_not_found() {
await addMemories();
// Retrieve all memories, including soft deleted ones
const memoriesBeforeSoftDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
// Check that no memories are soft deleted initially
const softDeletedMemoriesBefore = memoriesBeforeSoftDelete.filter(
memory => memory.is_deleted
);
Assert.equal(
softDeletedMemoriesBefore.length,
0,
"There should be no soft deleted memories initially."
);
// Attempt to soft delete a non-existent memory
const memoryAfterSoftDelete =
await MemoriesManager.softDeleteMemoryById("non-existent-id");
// Check that the result is null (no memories were soft deleted)
Assert.equal(
memoryAfterSoftDelete,
null,
"softDeleteMemoryById should return null for non-existent memory ID."
);
// Retrieve all memories again to confirm no memories were soft deleted
const memoriesAfterSoftDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
const softDeletedMemoriesAfter = memoriesAfterSoftDelete.filter(
memory => memory.is_deleted
);
Assert.equal(
softDeletedMemoriesAfter.length,
0,
"There should be no soft deleted memories after attempting to delete a non-existent memory."
);
await deleteAllMemories();
});
/**
* Tests hard deleting a memory by ID
*/
add_task(async function test_hardDeleteMemoryById() {
await addMemories();
// Retrieve all memories, including soft deleted ones
const memoriesBeforeHardDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
// Pick a memory off the top to test hard deletion
const memoryBeforeHardDelete = memoriesBeforeHardDelete[0];
// Hard delete the memory
const deletionResult = await MemoriesManager.hardDeleteMemoryById(
memoryBeforeHardDelete.id
);
// Check that the deletion was successful
Assert.ok(
deletionResult,
"hardDeleteMemoryById should return true on successful deletion."
);
// Retrieve all memories again to confirm the hard deletion was saved correctly
const memoriesAfterHardDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
Assert.equal(
memoriesAfterHardDelete.length,
memoriesBeforeHardDelete.length - 1,
"There should be one fewer memory after hard deletion."
);
await deleteAllMemories();
});
/**
* Tests attempting to hard delete a memory that doesn't exist by ID
*/
add_task(async function test_hardDeleteMemoryById_not_found() {
await addMemories();
// Retrieve all memories, including soft deleted ones
const memoriesBeforeHardDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
// Hard delete the memory
const deletionResult =
await MemoriesManager.hardDeleteMemoryById("non-existent-id");
// Check that the result is false (no memories were hard deleted)
Assert.ok(
!deletionResult,
"hardDeleteMemoryById should return false for non-existent memory ID."
);
// Retrieve all memories again to make sure no memories were hard deleted
const memoriesAfterHardDelete = await MemoriesManager.getAllMemories({
includeSoftDeleted: true,
});
Assert.equal(
memoriesAfterHardDelete.length,
memoriesBeforeHardDelete.length,
"Memory count before and after failed hard deletion should be the same."
);
await deleteAllMemories();
});
/**
* Tests building the message memory classification prompt
*/
add_task(async function test_buildMessageMemoryClassificationPrompt() {
const prompt =
await MemoriesManager.buildMessageMemoryClassificationPrompt(TEST_MESSAGE);
Assert.ok(
prompt.includes(TEST_MESSAGE),
"Prompt should include the original message."
);
Assert.ok(
prompt.includes(getFormattedMemoryAttributeList(CATEGORIES)),
"Prompt should include formatted categories."
);
Assert.ok(
prompt.includes(getFormattedMemoryAttributeList(INTENTS)),
"Prompt should include formatted intents."
);
});
/**
* Tests classifying a user message into memory categories and intents
*/
add_task(async function test_memoryClassifyMessage_happy_path() {
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: `{
"categories": ["Food & Drink"],
"intents": ["Plan / Organize"]
}`,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const messageClassification =
await MemoriesManager.memoryClassifyMessage(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check classification result was returned correctly
Assert.equal(
typeof messageClassification,
"object",
"Result should be an object."
);
Assert.equal(
Object.keys(messageClassification).length,
2,
"Result should have two keys."
);
Assert.deepEqual(
messageClassification.categories,
["Food & Drink"],
"Categories should match the fake response."
);
Assert.deepEqual(
messageClassification.intents,
["Plan / Organize"],
"Intents should match the fake response."
);
} finally {
sb.restore();
}
});
/**
* Tests failed message classification - LLM returns empty output
*/
add_task(async function test_memoryClassifyMessage_sad_path_empty_output() {
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: ``,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const messageClassification =
await MemoriesManager.memoryClassifyMessage(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check classification result was returned correctly despite empty output
Assert.equal(
typeof messageClassification,
"object",
"Result should be an object."
);
Assert.equal(
Object.keys(messageClassification).length,
2,
"Result should have two keys."
);
Assert.equal(
messageClassification.category,
null,
"Category should be null for empty output."
);
Assert.equal(
messageClassification.intent,
null,
"Intent should be null for empty output."
);
} finally {
sb.restore();
}
});
/**
* Tests failed message classification - LLM returns incorrect schema
*/
add_task(async function test_memoryClassifyMessage_sad_path_bad_schema() {
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: `{
"wrong_key": "some value"
}`,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const messageClassification =
await MemoriesManager.memoryClassifyMessage(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check classification result was returned correctly despite bad schema
Assert.equal(
typeof messageClassification,
"object",
"Result should be an object."
);
Assert.equal(
Object.keys(messageClassification).length,
2,
"Result should have two keys."
);
Assert.equal(
messageClassification.category,
null,
"Category should be null for bad schema output."
);
Assert.equal(
messageClassification.intent,
null,
"Intent should be null for bad schema output."
);
} finally {
sb.restore();
}
});
/**
* Tests retrieving relevant memories for a user message
*/
add_task(async function test_getRelevantMemories_happy_path() {
// Add memories so that we pass the existing memories check in the `getRelevantMemories` method
await addMemories();
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: `{
"categories": ["Food & Drink"],
"intents": ["Plan / Organize"]
}`,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const relevantMemories =
await MemoriesManager.getRelevantMemories(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check that the correct relevant memory was returned
Assert.ok(Array.isArray(relevantMemories), "Result should be an array.");
Assert.equal(
relevantMemories.length,
1,
"Result should contain one relevant memory."
);
Assert.equal(
relevantMemories[0].memory_summary,
"Loves drinking coffee",
"Relevant memory summary should match."
);
// Delete memories after test
await deleteAllMemories();
} finally {
sb.restore();
}
});
/**
* Tests failed memories retrieval - no existing memories stored
*
* We don't mock an engine for this test case because getRelevantMemories should immediately return an empty array
* because there aren't any existing memories -> No need to call the LLM.
*/
add_task(
async function test_getRelevantMemories_sad_path_no_existing_memories() {
const relevantMemories =
await MemoriesManager.getRelevantMemories(TEST_MESSAGE);
// Check that result is an empty array
Assert.ok(Array.isArray(relevantMemories), "Result should be an array.");
Assert.equal(
relevantMemories.length,
0,
"Result should be an empty array when there are no existing memories."
);
}
);
/**
* Tests failed memories retrieval - null classification
*/
add_task(
async function test_getRelevantMemories_sad_path_null_classification() {
// Add memories so that we pass the existing memories check
await addMemories();
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: `{
"categories": [],
"intents": []
}`,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const relevantMemories =
await MemoriesManager.getRelevantMemories(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check that result is an empty array
Assert.ok(Array.isArray(relevantMemories), "Result should be an array.");
Assert.equal(
relevantMemories.length,
0,
"Result should be an empty array when category is null."
);
// Delete memories after test
await deleteAllMemories();
} finally {
sb.restore();
}
}
);
/**
* Tests failed memories retrieval - no memory in message's category
*/
add_task(
async function test_getRelevantMemories_sad_path_no_memories_in_message_category() {
// Add memories so that we pass the existing memories check
await addMemories();
const sb = sinon.createSandbox();
try {
const fakeEngine = {
run() {
return {
finalOutput: `{
"categories": ["Health & Fitness"],
"intents": ["Plan / Organize"]
}`,
};
},
};
const stub = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.returns(fakeEngine);
const relevantMemories =
await MemoriesManager.getRelevantMemories(TEST_MESSAGE);
// Check that the stub was called
Assert.ok(stub.calledOnce, "ensureOpenAIEngine should be called once");
// Check that result is an empty array
Assert.ok(Array.isArray(relevantMemories), "Result should be an array.");
Assert.equal(
relevantMemories.length,
0,
"Result should be an empty array when no memories match the message category."
);
// Delete memories after test
await deleteAllMemories();
} finally {
sb.restore();
}
}
);
/**
* Tests saveMemories correctly persists history memories and updates last_history_memory_ts.
*/
add_task(async function test_saveMemories_history_updates_meta() {
const sb = sinon.createSandbox();
try {
const now = Date.now();
const generatedMemories = [
{
memory_summary: "foo",
category: "A",
intent: "X",
score: 1,
updated_at: now - 1000,
},
{
memory_summary: "bar",
category: "B",
intent: "Y",
score: 2,
updated_at: now + 500,
},
];
const storedMemories = generatedMemories.map((generatedMemory, idx) => ({
id: `id-${idx}`,
...generatedMemory,
}));
const addMemoryStub = sb
.stub(MemoryStore, "addMemory")
.callsFake(async partial => {
// simple mapping: return first / second stored memory based on summary
return storedMemories.find(
s => s.memory_summary === partial.memory_summary
);
});
const updateMetaStub = sb.stub(MemoryStore, "updateMeta").resolves();
const { persistedMemories, newTimestampMs } =
await MemoriesManager.saveMemories(
generatedMemories,
SOURCE_HISTORY,
now
);
Assert.equal(
addMemoryStub.callCount,
generatedMemories.length,
"addMemory should be called once per generated memory"
);
Assert.deepEqual(
persistedMemories.map(i => i.id),
storedMemories.map(i => i.id),
"Persisted memories should match stored memories"
);
Assert.ok(
updateMetaStub.calledOnce,
"updateMeta should be called once for history source"
);
const metaArg = updateMetaStub.firstCall.args[0];
Assert.ok(
"last_history_memory_ts" in metaArg,
"updateMeta should update last_history_memory_ts for history source"
);
Assert.equal(
metaArg.last_history_memory_ts,
storedMemories[1].updated_at,
"last_history_memory_ts should be set to max(updated_at) among persisted memories"
);
Assert.equal(
newTimestampMs,
storedMemories[1].updated_at,
"Returned newTimestampMs should match the updated meta timestamp"
);
} finally {
sb.restore();
}
});
/**
* Tests saveMemories correctly persists conversation memories and updates last_chat_memory_ts.
*/
add_task(async function test_saveMemories_conversation_updates_meta() {
const sb = sinon.createSandbox();
try {
const now = Date.now();
const generatedMemories = [
{
memory_summary: "chat-memory",
category: "Chat",
intent: "Talk",
score: 1,
updated_at: now,
},
];
const storedMemory = { id: "chat-1", ...generatedMemories[0] };
const addMemoryStub = sb
.stub(MemoryStore, "addMemory")
.resolves(storedMemory);
const updateMetaStub = sb.stub(MemoryStore, "updateMeta").resolves();
const { persistedMemories, newTimestampMs } =
await MemoriesManager.saveMemories(
generatedMemories,
SOURCE_CONVERSATION,
now
);
Assert.equal(
addMemoryStub.callCount,
1,
"addMemory should be called once for conversation memory"
);
Assert.equal(
persistedMemories[0].id,
storedMemory.id,
"Persisted memory should match stored memory"
);
Assert.ok(
updateMetaStub.calledOnce,
"updateMeta should be called once for conversation source"
);
const metaArg = updateMetaStub.firstCall.args[0];
Assert.ok(
"last_chat_memory_ts" in metaArg,
"updateMeta should update last_chat_memory_ts for conversation source"
);
Assert.equal(
metaArg.last_chat_memory_ts,
storedMemory.updated_at,
"last_chat_memory_ts should be set to memory.updated_at"
);
Assert.equal(
newTimestampMs,
storedMemory.updated_at,
"Returned newTimestampMs should match the updated meta timestamp"
);
} finally {
sb.restore();
}
});
/**
* Tests that getLastHistoryMemoryTimestamp reads the same value written via MemoryStore.updateMeta.
*/
add_task(async function test_getLastHistoryMemoryTimestamp_reads_meta() {
const ts = Date.now() - 12345;
// Write meta directly
await MemoryStore.updateMeta({
last_history_memory_ts: ts,
});
// Read via MemoriesManager helper
const readTs = await MemoriesManager.getLastHistoryMemoryTimestamp();
Assert.equal(
readTs,
ts,
"getLastHistoryMemoryTimestamp should return last_history_memory_ts from MemoryStore meta"
);
});
/**
* Tests that getLastConversationMemoryTimestamp reads the same value written via MemoryStore.updateMeta.
*/
add_task(async function test_getLastConversationMemoryTimestamp_reads_meta() {
const ts = Date.now() - 54321;
// Write meta directly
await MemoryStore.updateMeta({
last_chat_memory_ts: ts,
});
// Read via MemoriesManager helper
const readTs = await MemoriesManager.getLastConversationMemoryTimestamp();
Assert.equal(
readTs,
ts,
"getLastConversationMemoryTimestamp should return last_chat_memory_ts from MemoryStore meta"
);
});
/**
* Tests that history memory generation updates last_history_memory_ts and not last_conversation_memory_ts.
*/
add_task(
async function test_historyTimestampUpdatedAfterHistoryMemoriesGenerationPass() {
const sb = sinon.createSandbox();
const lastHistoryMemoriesUpdateTs =
await MemoriesManager.getLastHistoryMemoryTimestamp();
const lastConversationMemoriesUpdateTs =
await MemoriesManager.getLastConversationMemoryTimestamp();
try {
const aggregateBrowserHistoryStub = sb
.stub(MemoriesManager, "getAggregatedBrowserHistory")
.resolves([[], [], []]);
const fakeEngine = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.resolves({
run() {
return {
finalOutput: `[
{
"why": "User has recently searched for Firefox history and visited mozilla.org.",
"category": "Internet & Telecom",
"intent": "Research / Learn",
"memory_summary": "Searches for Firefox information",
"score": 7,
"evidence": [
{
"type": "search",
"value": "Google Search: firefox history"
},
{
"type": "domain",
"value": "mozilla.org"
}
]
},
{
"why": "User buys dog food online regularly from multiple sources.",
"category": "Pets & Animals",
"intent": "Buy / Acquire",
"memory_summary": "Purchases dog food online",
"score": -1,
"evidence": [
{
"type": "domain",
"value": "example.com"
}
]
}
]`,
};
},
});
await MemoriesManager.generateMemoriesFromBrowsingHistory();
Assert.ok(
aggregateBrowserHistoryStub.calledOnce,
"getAggregatedBrowserHistory should be called once during memory generation"
);
Assert.ok(
fakeEngine.calledOnce,
"ensureOpenAIEngine should be called once during memory generation"
);
Assert.greater(
await MemoriesManager.getLastHistoryMemoryTimestamp(),
lastHistoryMemoriesUpdateTs,
"Last history memory timestamp should be updated after history generation pass"
);
Assert.equal(
await MemoriesManager.getLastConversationMemoryTimestamp(),
lastConversationMemoriesUpdateTs,
"Last conversation memory timestamp should remain unchanged after history generation pass"
);
} finally {
sb.restore();
}
}
);
/**
* Tests that conversation memory generation updates last_conversation_memory_ts and not last_history_memory_ts.
*/
add_task(
async function test_conversationTimestampUpdatedAfterConversationMemoriesGenerationPass() {
const sb = sinon.createSandbox();
const lastConversationMemoriesUpdateTs =
await MemoriesManager.getLastConversationMemoryTimestamp();
const lastHistoryMemoriesUpdateTs =
await MemoriesManager.getLastHistoryMemoryTimestamp();
try {
const getRecentChatsStub = sb
.stub(MemoriesManager, "_getRecentChats")
.resolves([]);
const fakeEngine = sb
.stub(MemoriesManager, "ensureOpenAIEngine")
.resolves({
run() {
return {
finalOutput: `[
{
"why": "User has recently searched for Firefox history and visited mozilla.org.",
"category": "Internet & Telecom",
"intent": "Research / Learn",
"memory_summary": "Searches for Firefox information",
"score": 7,
"evidence": [
{
"type": "search",
"value": "Google Search: firefox history"
},
{
"type": "domain",
"value": "mozilla.org"
}
]
},
{
"why": "User buys dog food online regularly from multiple sources.",
"category": "Pets & Animals",
"intent": "Buy / Acquire",
"memory_summary": "Purchases dog food online",
"score": -1,
"evidence": [
{
"type": "domain",
"value": "example.com"
}
]
}
]`,
};
},
});
await MemoriesManager.generateMemoriesFromConversationHistory();
Assert.ok(
getRecentChatsStub.calledOnce,
"getRecentChats should be called once during memory generation"
);
Assert.ok(
fakeEngine.calledOnce,
"ensureOpenAIEngine should be called once during memory generation"
);
Assert.greater(
await MemoriesManager.getLastConversationMemoryTimestamp(),
lastConversationMemoriesUpdateTs,
"Last conversation memory timestamp should be updated after conversation generation pass"
);
Assert.equal(
await MemoriesManager.getLastHistoryMemoryTimestamp(),
lastHistoryMemoriesUpdateTs,
"Last history memory timestamp should remain unchanged after conversation generation pass"
);
} finally {
sb.restore();
}
}
);