tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_Memories.js (41273B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 do_get_profile();
      6 
      7 const { ChatStore, ChatMessage, MESSAGE_ROLE } = ChromeUtils.importESModule(
      8  "moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs"
      9 );
     10 const {
     11  getRecentHistory,
     12  generateProfileInputs,
     13  aggregateSessions,
     14  topkAggregates,
     15 } = ChromeUtils.importESModule(
     16  "moz-src:///browser/components/aiwindow/models/memories/MemoriesHistorySource.sys.mjs"
     17 );
     18 const { getRecentChats } = ChromeUtils.importESModule(
     19  "moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs"
     20 );
     21 const { DEFAULT_ENGINE_ID, MODEL_FEATURES, openAIEngine, SERVICE_TYPES } =
     22  ChromeUtils.importESModule(
     23    "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"
     24  );
     25 const { sinon } = ChromeUtils.importESModule(
     26  "resource://testing-common/Sinon.sys.mjs"
     27 );
     28 
     29 const { CATEGORIES, INTENTS } = ChromeUtils.importESModule(
     30  "moz-src:///browser/components/aiwindow/models/memories/MemoriesConstants.sys.mjs"
     31 );
     32 
     33 const {
     34  formatListForPrompt,
     35  getFormattedMemoryAttributeList,
     36  renderRecentHistoryForPrompt,
     37  renderRecentConversationForPrompt,
     38  mapFilteredMemoriesToInitialList,
     39  buildInitialMemoriesGenerationPrompt,
     40  buildMemoriesDeduplicationPrompt,
     41  buildMemoriesSensitivityFilterPrompt,
     42  generateInitialMemoriesList,
     43  deduplicateMemories,
     44  filterSensitiveMemories,
     45 } = ChromeUtils.importESModule(
     46  "moz-src:///browser/components/aiwindow/models/memories/Memories.sys.mjs"
     47 );
     48 
     49 /**
     50 * Constants for preference keys and test values
     51 */
     52 const PREF_API_KEY = "browser.aiwindow.apiKey";
     53 const PREF_ENDPOINT = "browser.aiwindow.endpoint";
     54 const PREF_MODEL = "browser.aiwindow.model";
     55 
     56 const API_KEY = "fake-key";
     57 const ENDPOINT = "https://api.fake-endpoint.com/v1";
     58 const MODEL = "fake-model";
     59 
     60 const EXISTING_MEMORIES = [
     61  "Loves outdoor activities",
     62  "Enjoys cooking recipes",
     63  "Like sci-fi media",
     64 ];
     65 const NEW_MEMORIES = [
     66  "Loves hiking and camping",
     67  "Reads science fiction novels",
     68  "Likes both dogs and cats",
     69  "Likes risky stock bets",
     70 ];
     71 
     72 add_setup(async function () {
     73  // Setup prefs used across multiple tests
     74  Services.prefs.setStringPref(PREF_API_KEY, API_KEY);
     75  Services.prefs.setStringPref(PREF_ENDPOINT, ENDPOINT);
     76  Services.prefs.setStringPref(PREF_MODEL, MODEL);
     77 
     78  // Clear prefs after testing
     79  registerCleanupFunction(() => {
     80    for (let pref of [PREF_API_KEY, PREF_ENDPOINT, PREF_MODEL]) {
     81      if (Services.prefs.prefHasUserValue(pref)) {
     82        Services.prefs.clearUserPref(pref);
     83      }
     84    }
     85  });
     86 });
     87 
     88 /**
     89 * Builds fake browsing history data for testing
     90 */
     91 async function buildFakeBrowserHistory() {
     92  const now = Date.now();
     93 
     94  const seeded = [
     95    {
     96      url: "https://www.google.com/search?q=firefox+history",
     97      title: "Google Search: firefox history",
     98      visits: [{ date: new Date(now - 5 * 60 * 1000) }],
     99    },
    100    {
    101      url: "https://news.ycombinator.com/",
    102      title: "Hacker News",
    103      visits: [{ date: new Date(now - 15 * 60 * 1000) }],
    104    },
    105    {
    106      url: "https://mozilla.org/en-US/",
    107      title: "Internet for people, not profit — Mozilla",
    108      visits: [{ date: new Date(now - 25 * 60 * 1000) }],
    109    },
    110  ];
    111  await PlacesUtils.history.clear();
    112  await PlacesUtils.history.insertMany(seeded);
    113 }
    114 
    115 /**
    116 * Shortcut for full browser history aggregation pipeline
    117 */
    118 async function getBrowserHistoryAggregates() {
    119  const profileRecords = await getRecentHistory();
    120  const profilePreparedInputs = await generateProfileInputs(profileRecords);
    121  const [domainAgg, titleAgg, searchAgg] = aggregateSessions(
    122    profilePreparedInputs
    123  );
    124 
    125  return await topkAggregates(domainAgg, titleAgg, searchAgg);
    126 }
    127 
    128 /**
    129 * Builds fake chat history data for testing
    130 */
    131 async function buildFakeChatHistory() {
    132  const fixedNow = 1_700_000_000_000;
    133 
    134  return [
    135    new ChatMessage({
    136      createdDate: fixedNow - 1_000,
    137      ordinal: 1,
    138      role: MESSAGE_ROLE.USER,
    139      content: { type: "text", body: "I like dogs." },
    140      pageUrl: "https://example.com/1",
    141      turnIndex: 0,
    142    }),
    143    new ChatMessage({
    144      createdDate: fixedNow - 10_000,
    145      ordinal: 2,
    146      role: MESSAGE_ROLE.USER,
    147      content: { type: "text", body: "I also like cats." },
    148      pageUrl: "https://example.com/2",
    149      turnIndex: 0,
    150    }),
    151    new ChatMessage({
    152      createdDate: fixedNow - 100_000,
    153      ordinal: 3,
    154      role: MESSAGE_ROLE.USER,
    155      content: {
    156        type: "text",
    157        body: "Tell me a joke about my favorite animals.",
    158      },
    159      pageUrl: "https://example.com/3",
    160      turnIndex: 0,
    161    }),
    162  ];
    163 }
    164 
    165 /**
    166 * Tests building the prompt for initial memories generation
    167 */
    168 add_task(async function test_buildInitialMemoriesGenerationPrompt() {
    169  // Check that history is rendered correctly into CSV tables
    170  await buildFakeBrowserHistory();
    171  const [domainItems, titleItems, searchItems] =
    172    await getBrowserHistoryAggregates();
    173  const renderedBrowserHistory = await renderRecentHistoryForPrompt(
    174    domainItems,
    175    titleItems,
    176    searchItems
    177  );
    178  Assert.equal(
    179    renderedBrowserHistory,
    180    `# Domains
    181 Domain,Importance Score
    182 www.google.com,100
    183 news.ycombinator.com,100
    184 mozilla.org,100
    185 
    186 # Titles
    187 Title,Importance Score
    188 Google Search: firefox history,100
    189 Hacker News,100
    190 Internet for people, not profit — Mozilla,100
    191 
    192 # Searches
    193 Search,Importance Score
    194 Google Search: firefox history,1`.trim()
    195  );
    196 
    197  // Check that the full prompt is built correctly with injected categories, intents, and browsing history
    198  const sources = { history: [domainItems, titleItems, searchItems] };
    199  const initialMemoriesPrompt =
    200    await buildInitialMemoriesGenerationPrompt(sources);
    201  Assert.ok(
    202    initialMemoriesPrompt.includes(
    203      "You are an expert at extracting memories from user browser data."
    204    ),
    205    "Initial memories generation prompt should pull from the correct base"
    206  );
    207  Assert.ok(
    208    initialMemoriesPrompt.includes(getFormattedMemoryAttributeList(CATEGORIES)),
    209    "Prompt should include formatted categories list"
    210  );
    211  Assert.ok(
    212    initialMemoriesPrompt.includes(getFormattedMemoryAttributeList(INTENTS)),
    213    "Prompt should include formatted intents list"
    214  );
    215  Assert.ok(
    216    initialMemoriesPrompt.includes(renderedBrowserHistory),
    217    "Prompt should include rendered browsing history"
    218  );
    219 });
    220 
    221 /**
    222 * Tests rendering history as CSV when only search data is present
    223 */
    224 add_task(async function test_buildRecentHistoryCSV_only_search() {
    225  const now = Date.now();
    226  const seeded = [
    227    {
    228      url: "https://www.google.com/search?q=firefox+history",
    229      title: "Google Search: firefox history",
    230      visits: [{ date: new Date(now - 5 * 60 * 1000) }],
    231    },
    232  ];
    233  await PlacesUtils.history.clear();
    234  await PlacesUtils.history.insertMany(seeded);
    235 
    236  const [domainItems, titleItems, searchItems] =
    237    await getBrowserHistoryAggregates();
    238  const renderedBrowserHistory = await renderRecentHistoryForPrompt(
    239    domainItems,
    240    titleItems,
    241    searchItems
    242  );
    243  Assert.equal(
    244    renderedBrowserHistory,
    245    `# Domains
    246 Domain,Importance Score
    247 www.google.com,100
    248 
    249 # Titles
    250 Title,Importance Score
    251 Google Search: firefox history,100
    252 
    253 # Searches
    254 Search,Importance Score
    255 Google Search: firefox history,1`.trim()
    256  );
    257 });
    258 
    259 /**
    260 * Tests rendering history as CSV when only history data is present
    261 */
    262 add_task(async function test_buildRecentHistoryCSV_only_browsing_history() {
    263  const now = Date.now();
    264  const seeded = [
    265    {
    266      url: "https://news.ycombinator.com/",
    267      title: "Hacker News",
    268      visits: [{ date: new Date(now - 15 * 60 * 1000) }],
    269    },
    270    {
    271      url: "https://mozilla.org/en-US/",
    272      title: "Internet for people, not profit — Mozilla",
    273      visits: [{ date: new Date(now - 25 * 60 * 1000) }],
    274    },
    275  ];
    276  await PlacesUtils.history.clear();
    277  await PlacesUtils.history.insertMany(seeded);
    278 
    279  const [domainItems, titleItems, searchItems] =
    280    await getBrowserHistoryAggregates();
    281  const renderedBrowserHistory = await renderRecentHistoryForPrompt(
    282    domainItems,
    283    titleItems,
    284    searchItems
    285  );
    286  Assert.equal(
    287    renderedBrowserHistory,
    288    `# Domains
    289 Domain,Importance Score
    290 news.ycombinator.com,100
    291 mozilla.org,100
    292 
    293 # Titles
    294 Title,Importance Score
    295 Hacker News,100
    296 Internet for people, not profit — Mozilla,100`.trim()
    297  );
    298 });
    299 
    300 /**
    301 * Tests building the prompt for initial memories generation with only chat data
    302 */
    303 add_task(async function test_buildInitialMemoriesGenerationPrompt_only_chat() {
    304  const messages = await buildFakeChatHistory();
    305  const sb = sinon.createSandbox();
    306  const maxResults = 3;
    307  const halfLifeDays = 7;
    308  const startTime = 1_700_000_000_000 - 1_000_000;
    309 
    310  try {
    311    // Stub the method
    312    const stub = sb
    313      .stub(ChatStore.prototype, "findMessagesByDate")
    314      .callsFake(async () => {
    315        return messages;
    316      });
    317 
    318    const recentMessages = await getRecentChats(
    319      startTime,
    320      maxResults,
    321      halfLifeDays
    322    );
    323 
    324    // Assert stub was actually called
    325    Assert.equal(stub.callCount, 1, "findMessagesByDate should be called once");
    326 
    327    // Double check we get only the 3 expected messages back
    328    Assert.equal(recentMessages.length, 3, "Should return 3 chat messages");
    329 
    330    // Render the messages into CSV format and check correctness
    331    const renderedConversationHistory =
    332      await renderRecentConversationForPrompt(recentMessages);
    333    Assert.equal(
    334      renderedConversationHistory,
    335      `# Chat History
    336 Message
    337 I like dogs.
    338 I also like cats.
    339 Tell me a joke about my favorite animals.`.trim(),
    340      "Rendered conversation history should match expected CSV format"
    341    );
    342 
    343    // Build the actual prompt and check its contents
    344    const sources = { conversation: recentMessages };
    345    const initialMemoriesPrompt =
    346      await buildInitialMemoriesGenerationPrompt(sources);
    347    Assert.ok(
    348      initialMemoriesPrompt.includes(
    349        "You are an expert at extracting memories from user browser data."
    350      ),
    351      "Initial memories generation prompt should pull from the correct base"
    352    );
    353    Assert.ok(
    354      initialMemoriesPrompt.includes(renderedConversationHistory),
    355      "Prompt should include rendered conversation history"
    356    );
    357  } finally {
    358    sb.restore();
    359  }
    360 });
    361 
    362 /**
    363 * Tests building the prompt for memories deduplication
    364 */
    365 add_task(async function test_buildMemoriesDeduplicationPrompt() {
    366  const memoriesDeduplicationPrompt = await buildMemoriesDeduplicationPrompt(
    367    EXISTING_MEMORIES,
    368    NEW_MEMORIES
    369  );
    370  Assert.ok(
    371    memoriesDeduplicationPrompt.includes(
    372      "You are an expert at identifying duplicate statements."
    373    ),
    374    "Memories deduplication prompt should pull from the correct base"
    375  );
    376  Assert.ok(
    377    memoriesDeduplicationPrompt.includes(
    378      formatListForPrompt(EXISTING_MEMORIES)
    379    ),
    380    "Deduplication prompt should include existing memories list"
    381  );
    382  Assert.ok(
    383    memoriesDeduplicationPrompt.includes(formatListForPrompt(NEW_MEMORIES)),
    384    "Deduplication prompt should include new memories list"
    385  );
    386 });
    387 
    388 /**
    389 * Tests building the prompt for memories sensitivity filtering
    390 */
    391 add_task(async function test_buildMemoriesSensitivityFilterPrompt() {
    392  /** Memories sensitivity filter prompt */
    393  const memoriesSensitivityFilterPrompt =
    394    await buildMemoriesSensitivityFilterPrompt(NEW_MEMORIES);
    395  Assert.ok(
    396    memoriesSensitivityFilterPrompt.includes(
    397      "You are an expert at identifying sensitive statements and content."
    398    ),
    399    "Memories sensitivity filter prompt should pull from the correct base"
    400  );
    401  Assert.ok(
    402    memoriesSensitivityFilterPrompt.includes(formatListForPrompt(NEW_MEMORIES)),
    403    "Sensitivity filter prompt should include memories list"
    404  );
    405 });
    406 
    407 /**
    408 * Tests successful initial memories generation
    409 */
    410 add_task(async function test_generateInitialMemoriesList_happy_path() {
    411  const sb = sinon.createSandbox();
    412  try {
    413    /**
    414     * The fake engine returns canned LLM response.
    415     * The main `generateInitialMemoriesList` function should modify this heavily, cutting it back to only the required fields.
    416     */
    417    const fakeEngine = {
    418      run() {
    419        return {
    420          finalOutput: `[
    421  {
    422    "why": "User has recently searched for Firefox history and visited mozilla.org.",
    423    "category": "Internet & Telecom",
    424    "intent": "Research / Learn",
    425    "memory_summary": "Searches for Firefox information",
    426    "score": 7,
    427    "evidence": [
    428      {
    429        "type": "search",
    430        "value": "Google Search: firefox history"
    431      },
    432      {
    433        "type": "domain",
    434        "value": "mozilla.org"
    435      }
    436    ]
    437  },
    438  {
    439    "why": "User buys dog food online regularly from multiple sources.",
    440    "category": "Pets & Animals",
    441    "intent": "Buy / Acquire",
    442    "memory_summary": "Purchases dog food online",
    443    "score": -1,
    444    "evidence": [
    445      {
    446        "type": "domain",
    447        "value": "example.com"
    448      }
    449    ]
    450  }
    451 ]`,
    452        };
    453      },
    454    };
    455 
    456    // Check that the stub was called
    457    const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine);
    458    const engine = await openAIEngine.build(
    459      MODEL_FEATURES.MEMORIES,
    460      DEFAULT_ENGINE_ID,
    461      SERVICE_TYPES.MEMORIES
    462    );
    463    Assert.ok(stub.calledOnce, "_createEngine should be called once");
    464 
    465    const [domainItems, titleItems, searchItems] =
    466      await getBrowserHistoryAggregates();
    467    const sources = { history: [domainItems, titleItems, searchItems] };
    468    const memoriesList = await generateInitialMemoriesList(engine, sources);
    469 
    470    // Check top level structure
    471    Assert.ok(
    472      Array.isArray(memoriesList),
    473      "Should return an array of memories"
    474    );
    475    Assert.equal(memoriesList.length, 2, "Array should contain 2 memories");
    476 
    477    // Check first memory structure and content
    478    const firstMemory = memoriesList[0];
    479    Assert.equal(
    480      typeof firstMemory,
    481      "object",
    482      "First memory should be an object/map"
    483    );
    484    Assert.equal(
    485      Object.keys(firstMemory).length,
    486      4,
    487      "First memory should have 4 keys"
    488    );
    489    Assert.equal(
    490      firstMemory.category,
    491      "Internet & Telecom",
    492      "First memory should have expected category (Internet & Telecom)"
    493    );
    494    Assert.equal(
    495      firstMemory.intent,
    496      "Research / Learn",
    497      "First memory should have expected intent (Research / Learn)"
    498    );
    499    Assert.equal(
    500      firstMemory.memory_summary,
    501      "Searches for Firefox information",
    502      "First memory should have expected summary"
    503    );
    504    Assert.equal(
    505      firstMemory.score,
    506      5,
    507      "First memory should have expected score, clamping 7 to 5"
    508    );
    509 
    510    // Check that the second memory's score was clamped to the minimum
    511    const secondMemory = memoriesList[1];
    512    Assert.equal(
    513      secondMemory.score,
    514      1,
    515      "Second memory should have expected score, clamping -1 to 1"
    516    );
    517  } finally {
    518    sb.restore();
    519  }
    520 });
    521 
    522 /**
    523 * Tests failed initial memories generation - Empty output
    524 */
    525 add_task(
    526  async function test_generateInitialMemoriesList_sad_path_empty_output() {
    527    const sb = sinon.createSandbox();
    528    try {
    529      // LLM returns an empty memories list
    530      const fakeEngine = {
    531        run() {
    532          return {
    533            finalOutput: `[]`,
    534          };
    535        },
    536      };
    537 
    538      // Check that the stub was called
    539      const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine);
    540      const engine = await openAIEngine.build(
    541        MODEL_FEATURES.MEMORIES,
    542        DEFAULT_ENGINE_ID,
    543        SERVICE_TYPES.MEMORIES
    544      );
    545      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    546 
    547      const [domainItems, titleItems, searchItems] =
    548        await getBrowserHistoryAggregates();
    549      const sources = { history: [domainItems, titleItems, searchItems] };
    550      const memoriesList = await generateInitialMemoriesList(engine, sources);
    551 
    552      Assert.equal(Array.isArray(memoriesList), true, "Should return an array");
    553      Assert.equal(memoriesList.length, 0, "Array should contain 0 memories");
    554    } finally {
    555      sb.restore();
    556    }
    557  }
    558 );
    559 
    560 /**
    561 * Tests failed initial memories generation - Output not array
    562 */
    563 add_task(
    564  async function test_generateInitialMemoriesList_sad_path_output_not_array() {
    565    const sb = sinon.createSandbox();
    566    try {
    567      // LLM doesn't return an array
    568      const fakeEngine = {
    569        run() {
    570          return {
    571            finalOutput: `testing`,
    572          };
    573        },
    574      };
    575 
    576      // Check that the stub was called
    577      const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine);
    578      const engine = await openAIEngine.build(
    579        MODEL_FEATURES.MEMORIES,
    580        DEFAULT_ENGINE_ID,
    581        SERVICE_TYPES.MEMORIES
    582      );
    583      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    584 
    585      const [domainItems, titleItems, searchItems] =
    586        await getBrowserHistoryAggregates();
    587      const sources = { history: [domainItems, titleItems, searchItems] };
    588      const memoriesList = await generateInitialMemoriesList(engine, sources);
    589 
    590      Assert.equal(Array.isArray(memoriesList), true, "Should return an array");
    591      Assert.equal(memoriesList.length, 0, "Array should contain 0 memories");
    592    } finally {
    593      sb.restore();
    594    }
    595  }
    596 );
    597 
    598 /**
    599 * Tests failed initial memories generation - Output not array of maps
    600 */
    601 add_task(
    602  async function test_generateInitialMemoriesList_sad_path_output_not_array_of_maps() {
    603    const sb = sinon.createSandbox();
    604    try {
    605      // LLM doesn't return an array of maps
    606      const fakeEngine = {
    607        run() {
    608          return {
    609            finalOutput: `["testing1", "testing2", ["testing3"]]`,
    610          };
    611        },
    612      };
    613 
    614      // Check that the stub was called
    615      const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine);
    616      const engine = await openAIEngine.build(
    617        MODEL_FEATURES.MEMORIES,
    618        DEFAULT_ENGINE_ID,
    619        SERVICE_TYPES.MEMORIES
    620      );
    621      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    622 
    623      const [domainItems, titleItems, searchItems] =
    624        await getBrowserHistoryAggregates();
    625      const sources = { history: [domainItems, titleItems, searchItems] };
    626      const memoriesList = await generateInitialMemoriesList(engine, sources);
    627 
    628      Assert.equal(Array.isArray(memoriesList), true, "Should return an array");
    629      Assert.equal(memoriesList.length, 0, "Array should contain 0 memories");
    630    } finally {
    631      sb.restore();
    632    }
    633  }
    634 );
    635 
    636 /**
    637 * Tests failed initial memories generation - Some correct memories
    638 */
    639 add_task(
    640  async function test_generateInitialMemoriesList_sad_path_some_correct_memories() {
    641    const sb = sinon.createSandbox();
    642    try {
    643      // LLM returns an memories list where 1 is fully correct and 1 is missing required keys (category in this case)
    644      const fakeEngine = {
    645        run() {
    646          return {
    647            finalOutput: `[
    648  {
    649    "why": "User has recently searched for Firefox history and visited mozilla.org.",
    650    "intent": "Research / Learn",
    651    "memory_summary": "Searches for Firefox information",
    652    "score": 7,
    653    "evidence": [
    654      {
    655        "type": "search",
    656        "value": "Google Search: firefox history"
    657      },
    658      {
    659        "type": "domain",
    660        "value": "mozilla.org"
    661      }
    662    ]
    663  },
    664  {
    665    "why": "User buys dog food online regularly from multiple sources.",
    666    "category": "Pets & Animals",
    667    "intent": "Buy / Acquire",
    668    "memory_summary": "Purchases dog food online",
    669    "score": -1,
    670    "evidence": [
    671      {
    672        "type": "domain",
    673        "value": "example.com"
    674      }
    675    ]
    676  }
    677 ]`,
    678          };
    679        },
    680      };
    681 
    682      // Check that the stub was called
    683      const stub = sb.stub(openAIEngine, "_createEngine").returns(fakeEngine);
    684      const engine = await openAIEngine.build(
    685        MODEL_FEATURES.MEMORIES,
    686        DEFAULT_ENGINE_ID,
    687        SERVICE_TYPES.MEMORIES
    688      );
    689      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    690 
    691      const [domainItems, titleItems, searchItems] =
    692        await getBrowserHistoryAggregates();
    693      const sources = { history: [domainItems, titleItems, searchItems] };
    694      const memoriesList = await generateInitialMemoriesList(engine, sources);
    695 
    696      Assert.equal(
    697        Array.isArray(memoriesList),
    698        true,
    699        "Should return an array of memories"
    700      );
    701      Assert.equal(memoriesList.length, 1, "Array should contain 1 memory");
    702      Assert.equal(
    703        memoriesList[0].memory_summary,
    704        "Purchases dog food online",
    705        "Memory summary should match the valid memory"
    706      );
    707    } finally {
    708      sb.restore();
    709    }
    710  }
    711 );
    712 
    713 /**
    714 * Tests successful memories deduplication
    715 */
    716 add_task(async function test_deduplicateMemoriesList_happy_path() {
    717  const sb = sinon.createSandbox();
    718  try {
    719    /**
    720     * The fake engine that returns a canned LLM response for deduplication.
    721     * The `deduplicateMemories` function should return an array containing only the `main_memory` values.
    722     */
    723    const fakeEngine = {
    724      run() {
    725        return {
    726          finalOutput: `{
    727            "unique_memories": [
    728              {
    729                "main_memory": "Loves outdoor activities",
    730                "duplicates": ["Loves hiking and camping"]
    731              },
    732              {
    733                "main_memory": "Enjoys cooking recipes",
    734                "duplicates": []
    735              },
    736              {
    737                "main_memory": "Like sci-fi media",
    738                "duplicates": ["Reads science fiction novels"]
    739              },
    740              {
    741                "main_memory": "Likes both dogs and cats",
    742                "duplicates": []
    743              },
    744              {
    745                "main_memory": "Likes risky stock bets",
    746                "duplicates": []
    747              }
    748            ]
    749          }`,
    750        };
    751      },
    752    };
    753 
    754    // Check that the stub was called
    755    const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
    756    const engine = await openAIEngine.build(
    757      MODEL_FEATURES.MEMORIES,
    758      DEFAULT_ENGINE_ID,
    759      SERVICE_TYPES.MEMORIES
    760    );
    761    Assert.ok(stub.calledOnce, "_createEngine should be called once");
    762 
    763    const dedupedMemoriesList = await deduplicateMemories(
    764      engine,
    765      EXISTING_MEMORIES,
    766      NEW_MEMORIES
    767    );
    768 
    769    // Check that the deduplicated list contains only unique memories (`main_memory` values)
    770    Assert.equal(
    771      dedupedMemoriesList.length,
    772      5,
    773      "Deduplicated memories list should contain 5 unique memories"
    774    );
    775    Assert.ok(
    776      dedupedMemoriesList.includes("Loves outdoor activities"),
    777      "Deduplicated memories should include 'Loves outdoor activities'"
    778    );
    779    Assert.ok(
    780      dedupedMemoriesList.includes("Enjoys cooking recipes"),
    781      "Deduplicated memories should include 'Enjoys cooking recipes'"
    782    );
    783    Assert.ok(
    784      dedupedMemoriesList.includes("Like sci-fi media"),
    785      "Deduplicated memories should include 'Like sci-fi media'"
    786    );
    787    Assert.ok(
    788      dedupedMemoriesList.includes("Likes both dogs and cats"),
    789      "Deduplicated memories should include 'Likes both dogs and cats'"
    790    );
    791    Assert.ok(
    792      dedupedMemoriesList.includes("Likes risky stock bets"),
    793      "Deduplicated memories should include 'Likes risky stock bets'"
    794    );
    795  } finally {
    796    sb.restore();
    797  }
    798 });
    799 
    800 /**
    801 * Tests failed memories deduplication - Empty output
    802 */
    803 add_task(async function test_deduplicateMemoriesList_sad_path_empty_output() {
    804  const sb = sinon.createSandbox();
    805  try {
    806    // LLM returns the correct schema but with an empty unique_memories array
    807    const fakeEngine = {
    808      run() {
    809        return {
    810          finalOutput: `{
    811            "unique_memories": []
    812          }`,
    813        };
    814      },
    815    };
    816 
    817    // Check that the stub was called
    818    const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
    819    const engine = await openAIEngine.build(
    820      MODEL_FEATURES.MEMORIES,
    821      DEFAULT_ENGINE_ID,
    822      SERVICE_TYPES.MEMORIES
    823    );
    824    Assert.ok(stub.calledOnce, "_createEngine should be called once");
    825 
    826    const dedupedMemoriesList = await deduplicateMemories(
    827      engine,
    828      EXISTING_MEMORIES,
    829      NEW_MEMORIES
    830    );
    831 
    832    Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
    833    Assert.equal(dedupedMemoriesList.length, 0, "Should return an empty array");
    834  } finally {
    835    sb.restore();
    836  }
    837 });
    838 
    839 /**
    840 * Tests failed memories deduplication - Wrong top-level data type
    841 */
    842 add_task(
    843  async function test_deduplicateMemoriesList_sad_path_wrong_top_level_data_type() {
    844    const sb = sinon.createSandbox();
    845    try {
    846      // LLM returns an incorrect data type
    847      const fakeEngine = {
    848        run() {
    849          return {
    850            finalOutput: `testing`,
    851          };
    852        },
    853      };
    854 
    855      // Check that the stub was called
    856      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
    857      const engine = await openAIEngine.build(
    858        MODEL_FEATURES.MEMORIES,
    859        DEFAULT_ENGINE_ID,
    860        SERVICE_TYPES.MEMORIES
    861      );
    862      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    863 
    864      const dedupedMemoriesList = await deduplicateMemories(
    865        engine,
    866        EXISTING_MEMORIES,
    867        NEW_MEMORIES
    868      );
    869 
    870      Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
    871      Assert.equal(
    872        dedupedMemoriesList.length,
    873        0,
    874        "Should return an empty array"
    875      );
    876    } finally {
    877      sb.restore();
    878    }
    879  }
    880 );
    881 
    882 /**
    883 * Tests failed memories deduplication - Wrong inner data type
    884 */
    885 add_task(
    886  async function test_deduplicateMemoriesList_sad_path_wrong_inner_data_type() {
    887    const sb = sinon.createSandbox();
    888    try {
    889      // LLM returns a map with the right top-level key, but the inner structure is wrong
    890      const fakeEngine = {
    891        run() {
    892          return {
    893            finalOutput: `{
    894            "unique_memories": "testing"
    895          }`,
    896          };
    897        },
    898      };
    899 
    900      // Check that the stub was called
    901      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
    902      const engine = await openAIEngine.build(
    903        MODEL_FEATURES.MEMORIES,
    904        DEFAULT_ENGINE_ID,
    905        SERVICE_TYPES.MEMORIES
    906      );
    907      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    908 
    909      const dedupedMemoriesList = await deduplicateMemories(
    910        engine,
    911        EXISTING_MEMORIES,
    912        NEW_MEMORIES
    913      );
    914 
    915      Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
    916      Assert.equal(
    917        dedupedMemoriesList.length,
    918        0,
    919        "Should return an empty array"
    920      );
    921    } finally {
    922      sb.restore();
    923    }
    924  }
    925 );
    926 
    927 /**
    928 * Tests failed memories deduplication - Wrong inner array structure
    929 */
    930 add_task(
    931  async function test_deduplicateMemoriesList_sad_path_wrong_inner_array_structure() {
    932    const sb = sinon.createSandbox();
    933    try {
    934      // LLM returns a map of nested arrays, but the array structure is wrong
    935      const fakeEngine = {
    936        run() {
    937          return {
    938            finalOutput: `{
    939            "unique_memories": ["testing1", "testing2"]
    940          }`,
    941          };
    942        },
    943      };
    944 
    945      // Check that the stub was called
    946      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
    947      const engine = await openAIEngine.build(
    948        MODEL_FEATURES.MEMORIES,
    949        DEFAULT_ENGINE_ID,
    950        SERVICE_TYPES.MEMORIES
    951      );
    952      Assert.ok(stub.calledOnce, "_createEngine should be called once");
    953 
    954      const dedupedMemoriesList = await deduplicateMemories(
    955        engine,
    956        EXISTING_MEMORIES,
    957        NEW_MEMORIES
    958      );
    959 
    960      Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
    961      Assert.equal(
    962        dedupedMemoriesList.length,
    963        0,
    964        "Should return an empty array"
    965      );
    966    } finally {
    967      sb.restore();
    968    }
    969  }
    970 );
    971 
    972 /**
    973 * Tests failed memories deduplication - Incorrect top-level schema key
    974 */
    975 add_task(
    976  async function test_deduplicateMemoriesList_sad_path_bad_top_level_key() {
    977    const sb = sinon.createSandbox();
    978    try {
    979      // LLm returns correct output except that the top-level key is wrong
    980      const fakeEngine = {
    981        run() {
    982          return {
    983            finalOutput: `{
    984            "correct_memories": [
    985              {
    986                "main_memory": "Loves outdoor activities",
    987                "duplicates": ["Loves hiking and camping"]
    988              },
    989              {
    990                "main_memory": "Enjoys cooking recipes",
    991                "duplicates": []
    992              },
    993              {
    994                "main_memory": "Like sci-fi media",
    995                "duplicates": ["Reads science fiction novels"]
    996              },
    997              {
    998                "main_memory": "Likes both dogs and cats",
    999                "duplicates": []
   1000              },
   1001              {
   1002                "main_memory": "Likes risky stock bets",
   1003                "duplicates": []
   1004              }
   1005            ]
   1006          }`,
   1007          };
   1008        },
   1009      };
   1010 
   1011      // Check that the stub was called
   1012      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1013      const engine = await openAIEngine.build(
   1014        MODEL_FEATURES.MEMORIES,
   1015        DEFAULT_ENGINE_ID,
   1016        SERVICE_TYPES.MEMORIES
   1017      );
   1018      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1019 
   1020      const dedupedMemoriesList = await deduplicateMemories(
   1021        engine,
   1022        EXISTING_MEMORIES,
   1023        NEW_MEMORIES
   1024      );
   1025 
   1026      Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
   1027      Assert.equal(
   1028        dedupedMemoriesList.length,
   1029        0,
   1030        "Should return an empty array"
   1031      );
   1032    } finally {
   1033      sb.restore();
   1034    }
   1035  }
   1036 );
   1037 
   1038 /**
   1039 * Tests failed memories deduplication - Some correct inner schema
   1040 */
   1041 add_task(
   1042  async function test_deduplicateMemoriesList_sad_path_bad_some_correct_inner_schema() {
   1043    const sb = sinon.createSandbox();
   1044    try {
   1045      // LLm returns correct output except that 1 of the inner maps is wrong and 1 main_memory is the wrong data type
   1046      const fakeEngine = {
   1047        run() {
   1048          return {
   1049            finalOutput: `{
   1050            "unique_memories": [
   1051              {
   1052                "primary_memory": "Loves outdoor activities",
   1053                "duplicates": ["Loves hiking and camping"]
   1054              },
   1055              {
   1056                "main_memory": "Enjoys cooking recipes",
   1057                "duplicates": []
   1058              },
   1059              {
   1060                "main_memory": 12345,
   1061                "duplicates": []
   1062              }
   1063            ]
   1064          }`,
   1065          };
   1066        },
   1067      };
   1068 
   1069      // Check that the stub was called
   1070      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1071      const engine = await openAIEngine.build(
   1072        MODEL_FEATURES.MEMORIES,
   1073        DEFAULT_ENGINE_ID,
   1074        SERVICE_TYPES.MEMORIES
   1075      );
   1076      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1077 
   1078      const dedupedMemoriesList = await deduplicateMemories(
   1079        engine,
   1080        EXISTING_MEMORIES,
   1081        NEW_MEMORIES
   1082      );
   1083 
   1084      Assert.ok(Array.isArray(dedupedMemoriesList), "Should return an array");
   1085      Assert.equal(
   1086        dedupedMemoriesList.length,
   1087        1,
   1088        "Should return an array with one valid memory"
   1089      );
   1090      Assert.equal(
   1091        dedupedMemoriesList[0],
   1092        "Enjoys cooking recipes",
   1093        "Should return the single valid memory"
   1094      );
   1095    } finally {
   1096      sb.restore();
   1097    }
   1098  }
   1099 );
   1100 
   1101 /**
   1102 * Tests successful memories sensitivity filtering
   1103 */
   1104 add_task(async function test_filterSensitiveMemories_happy_path() {
   1105  const sb = sinon.createSandbox();
   1106  try {
   1107    /**
   1108     * The fake engine that returns a canned LLM response for deduplication.
   1109     * The `filterSensitiveMemories` function should return the inner array from `non_sensitive_memories`.
   1110     */
   1111    const fakeEngine = {
   1112      run() {
   1113        return {
   1114          finalOutput: `{
   1115  "non_sensitive_memories": [
   1116    "Loves hiking and camping",
   1117    "Reads science fiction novels",
   1118    "Likes both dogs and cats"
   1119  ]
   1120 }`,
   1121        };
   1122      },
   1123    };
   1124 
   1125    // Check that the stub was called
   1126    const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1127    const engine = await openAIEngine.build(
   1128      MODEL_FEATURES.MEMORIES,
   1129      DEFAULT_ENGINE_ID,
   1130      SERVICE_TYPES.MEMORIES
   1131    );
   1132    Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1133 
   1134    const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1135      engine,
   1136      NEW_MEMORIES
   1137    );
   1138 
   1139    // Check that the non-sensitive memories list contains only non-sensitive memories
   1140    Assert.equal(
   1141      nonSensitiveMemoriesList.length,
   1142      3,
   1143      "Non-sensitive memories list should contain 3 memories"
   1144    );
   1145    Assert.ok(
   1146      nonSensitiveMemoriesList.includes("Loves hiking and camping"),
   1147      "Non-sensitive memories should include 'Loves hiking and camping'"
   1148    );
   1149    Assert.ok(
   1150      nonSensitiveMemoriesList.includes("Reads science fiction novels"),
   1151      "Non-sensitive memories should include 'Reads science fiction novels'"
   1152    );
   1153    Assert.ok(
   1154      nonSensitiveMemoriesList.includes("Likes both dogs and cats"),
   1155      "Non-sensitive memories should include 'Likes both dogs and cats'"
   1156    );
   1157  } finally {
   1158    sb.restore();
   1159  }
   1160 });
   1161 
   1162 /**
   1163 * Tests failed memories sensitivity filtering - Empty output
   1164 */
   1165 add_task(async function test_filterSensitiveMemories_sad_path_empty_output() {
   1166  const sb = sinon.createSandbox();
   1167  try {
   1168    // LLM returns an empty non_sensitive_memories array
   1169    const fakeEngine = {
   1170      run() {
   1171        return {
   1172          finalOutput: `{
   1173  "non_sensitive_memories": []
   1174 }`,
   1175        };
   1176      },
   1177    };
   1178 
   1179    // Check that the stub was called
   1180    const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1181    const engine = await openAIEngine.build(
   1182      MODEL_FEATURES.MEMORIES,
   1183      DEFAULT_ENGINE_ID,
   1184      SERVICE_TYPES.MEMORIES
   1185    );
   1186    Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1187 
   1188    const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1189      engine,
   1190      NEW_MEMORIES
   1191    );
   1192 
   1193    Assert.ok(
   1194      Array.isArray(nonSensitiveMemoriesList),
   1195      "Should return an array"
   1196    );
   1197    Assert.equal(
   1198      nonSensitiveMemoriesList.length,
   1199      0,
   1200      "Should return an empty array"
   1201    );
   1202  } finally {
   1203    sb.restore();
   1204  }
   1205 });
   1206 
   1207 /**
   1208 * Tests failed memories sensitivity filtering - Wrong data type
   1209 */
   1210 add_task(
   1211  async function test_filterSensitiveMemories_sad_path_wrong_data_type() {
   1212    const sb = sinon.createSandbox();
   1213    try {
   1214      // LLM returns the wrong outer data type
   1215      const fakeEngine = {
   1216        run() {
   1217          return {
   1218            finalOutput: `testing`,
   1219          };
   1220        },
   1221      };
   1222 
   1223      // Check that the stub was called
   1224      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1225      const engine = await openAIEngine.build(
   1226        MODEL_FEATURES.MEMORIES,
   1227        DEFAULT_ENGINE_ID,
   1228        SERVICE_TYPES.MEMORIES
   1229      );
   1230      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1231 
   1232      const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1233        engine,
   1234        NEW_MEMORIES
   1235      );
   1236 
   1237      Assert.ok(
   1238        Array.isArray(nonSensitiveMemoriesList),
   1239        "Should return an array"
   1240      );
   1241      Assert.equal(
   1242        nonSensitiveMemoriesList.length,
   1243        0,
   1244        "Should return an empty array"
   1245      );
   1246    } finally {
   1247      sb.restore();
   1248    }
   1249  }
   1250 );
   1251 
   1252 /**
   1253 * Tests failed memories sensitivity filtering - Wrong inner data type
   1254 */
   1255 add_task(
   1256  async function test_filterSensitiveMemories_sad_path_wrong_inner_data_type() {
   1257    const sb = sinon.createSandbox();
   1258    try {
   1259      // LLM returns a map with the non_sensitive_memories key, but its value's data type is wrong
   1260      const fakeEngine = {
   1261        run() {
   1262          return {
   1263            finalOutput: `{
   1264  "non_sensitive_memories": "testing"
   1265 }`,
   1266          };
   1267        },
   1268      };
   1269 
   1270      // Check that the stub was called
   1271      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1272      const engine = await openAIEngine.build(
   1273        MODEL_FEATURES.MEMORIES,
   1274        DEFAULT_ENGINE_ID,
   1275        SERVICE_TYPES.MEMORIES
   1276      );
   1277      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1278 
   1279      const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1280        engine,
   1281        NEW_MEMORIES
   1282      );
   1283 
   1284      Assert.ok(
   1285        Array.isArray(nonSensitiveMemoriesList),
   1286        "Should return an array"
   1287      );
   1288      Assert.equal(
   1289        nonSensitiveMemoriesList.length,
   1290        0,
   1291        "Should return an empty array"
   1292      );
   1293    } finally {
   1294      sb.restore();
   1295    }
   1296  }
   1297 );
   1298 
   1299 /**
   1300 * Tests failed memories sensitivity filtering - Wrong outer schema
   1301 */
   1302 add_task(
   1303  async function test_filterSensitiveMemories_sad_path_wrong_outer_schema() {
   1304    const sb = sinon.createSandbox();
   1305    try {
   1306      // LLM returns a map but with the wrong top-level key
   1307      const fakeEngine = {
   1308        run() {
   1309          return {
   1310            finalOutput: `{
   1311  "these_are_non_sensitive_memories": [
   1312    "testing1", "testing2", "testing3"
   1313  ]
   1314 }`,
   1315          };
   1316        },
   1317      };
   1318 
   1319      // Check that the stub was called
   1320      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1321      const engine = await openAIEngine.build(
   1322        MODEL_FEATURES.MEMORIES,
   1323        DEFAULT_ENGINE_ID,
   1324        SERVICE_TYPES.MEMORIES
   1325      );
   1326      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1327 
   1328      const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1329        engine,
   1330        NEW_MEMORIES
   1331      );
   1332 
   1333      Assert.ok(
   1334        Array.isArray(nonSensitiveMemoriesList),
   1335        "Should return an array"
   1336      );
   1337      Assert.equal(
   1338        nonSensitiveMemoriesList.length,
   1339        0,
   1340        "Should return an empty array"
   1341      );
   1342    } finally {
   1343      sb.restore();
   1344    }
   1345  }
   1346 );
   1347 
   1348 /**
   1349 * Tests failed memories sensitivity filtering - Some correct inner schema
   1350 */
   1351 add_task(
   1352  async function test_filterSensitiveMemories_sad_path_some_correct_inner_schema() {
   1353    const sb = sinon.createSandbox();
   1354    try {
   1355      // LLM returns a map with the non_sensitive_memories key, but the inner schema has a mix of correct and incorrect data types
   1356      const fakeEngine = {
   1357        run() {
   1358          return {
   1359            finalOutput: `{
   1360  "non_sensitive_memories": [
   1361    "correct",
   1362    12345,
   1363    {"bad": "schema"}
   1364  ]
   1365 }`,
   1366          };
   1367        },
   1368      };
   1369 
   1370      // Check that the stub was called
   1371      const stub = sb.stub(openAIEngine, "_createEngine").resolves(fakeEngine);
   1372      const engine = await openAIEngine.build(
   1373        MODEL_FEATURES.MEMORIES,
   1374        DEFAULT_ENGINE_ID,
   1375        SERVICE_TYPES.MEMORIES
   1376      );
   1377      Assert.ok(stub.calledOnce, "_createEngine should be called once");
   1378 
   1379      const nonSensitiveMemoriesList = await filterSensitiveMemories(
   1380        engine,
   1381        NEW_MEMORIES
   1382      );
   1383 
   1384      Assert.ok(
   1385        Array.isArray(nonSensitiveMemoriesList),
   1386        "Should return an array"
   1387      );
   1388      Assert.equal(
   1389        nonSensitiveMemoriesList.length,
   1390        1,
   1391        "Should return an array with one valid memory"
   1392      );
   1393      Assert.equal(
   1394        nonSensitiveMemoriesList[0],
   1395        "correct",
   1396        "Should return the single valid memory"
   1397      );
   1398    } finally {
   1399      sb.restore();
   1400    }
   1401  }
   1402 );
   1403 
   1404 /**
   1405 * Tests mapping filtered memories back to full memory objects
   1406 */
   1407 add_task(async function test_mapFilteredMemoriesToInitialList() {
   1408  // Raw mock full memories object list
   1409  const initialMemoriesList = [
   1410    // Imagined duplicate - should have been filtered out
   1411    {
   1412      category: "Pets & Animals",
   1413      intent: "Buy / Acquire",
   1414      memory_summary: "Buys dog food online",
   1415      score: 4,
   1416    },
   1417    // Sensitive content (stocks) - should have been filtered out
   1418    {
   1419      category: "News",
   1420      intent: "Research / Learn",
   1421      memory_summary: "Likes to invest in risky stocks",
   1422      score: 5,
   1423    },
   1424    {
   1425      category: "Games",
   1426      intent: "Entertain / Relax",
   1427      memory_summary: "Enjoys strategy games",
   1428      score: 3,
   1429    },
   1430  ];
   1431 
   1432  // Mock list of good memories to keep
   1433  const filteredMemoriesList = ["Enjoys strategy games"];
   1434 
   1435  const finalMemoriesList = await mapFilteredMemoriesToInitialList(
   1436    initialMemoriesList,
   1437    filteredMemoriesList
   1438  );
   1439 
   1440  // Check that only the non-duplicate, non-sensitive memory remains
   1441  Assert.equal(
   1442    finalMemoriesList.length,
   1443    1,
   1444    "Final memories should contain 1 memory"
   1445  );
   1446  Assert.equal(
   1447    finalMemoriesList[0].category,
   1448    "Games",
   1449    "Final memory should have the correct category"
   1450  );
   1451  Assert.equal(
   1452    finalMemoriesList[0].intent,
   1453    "Entertain / Relax",
   1454    "Final memory should have the correct intent"
   1455  );
   1456  Assert.equal(
   1457    finalMemoriesList[0].memory_summary,
   1458    "Enjoys strategy games",
   1459    "Final memory should match the filtered memory"
   1460  );
   1461  Assert.equal(
   1462    finalMemoriesList[0].score,
   1463    3,
   1464    "Final memory should have the correct score"
   1465  );
   1466 });