diff --git a/src/helpers/agentNameDictionary.js b/src/helpers/agentNameDictionary.js index c66197779..1f8f5051b 100644 --- a/src/helpers/agentNameDictionary.js +++ b/src/helpers/agentNameDictionary.js @@ -1,4 +1,5 @@ -function findStoredWord(words, word) { +export function findStoredWord(words, word) { + if (typeof word !== "string") return undefined; const needle = word.toLowerCase(); return words.find((w) => typeof w === "string" && w.trim().toLowerCase() === needle); } @@ -17,7 +18,12 @@ function findStoredWord(words, word) { * @returns {{ add: string[], remove: string[] }} */ export function agentNameDictionaryChanges(dictionary, newName, oldName) { - const words = Array.isArray(dictionary) ? dictionary : []; + const words = + dictionary != null && + typeof dictionary !== "string" && + typeof dictionary[Symbol.iterator] === "function" + ? [...dictionary] + : []; const trimmedNew = typeof newName === "string" ? newName.trim() : ""; const trimmedOld = typeof oldName === "string" ? oldName.trim() : ""; const storedNew = trimmedNew ? findStoredWord(words, trimmedNew) : undefined; diff --git a/test/helpers/agentNameDictionary.test.js b/test/helpers/agentNameDictionary.test.js index b8a78ae6c..7d9e79280 100644 --- a/test/helpers/agentNameDictionary.test.js +++ b/test/helpers/agentNameDictionary.test.js @@ -113,3 +113,62 @@ test("removes previous agent name when oldName contains surrounding whitespace", } ); }); + +test("accepts a Set dictionary and asks for no changes when the name is present", async () => { + const { agentNameDictionaryChanges } = await load(); + assert.deepEqual(agentNameDictionaryChanges(new Set(["OpenWhispr", "Alice"]), "OpenWhispr"), { + add: [], + remove: [], + }); +}); + +test("swaps the previous agent name when the dictionary is a Set", async () => { + const { agentNameDictionaryChanges } = await load(); + assert.deepEqual( + agentNameDictionaryChanges(new Set(["OpenWhispr", "Alice"]), "Jarvis", "OpenWhispr"), + { + add: ["Jarvis"], + remove: ["OpenWhispr"], + } + ); +}); + +test("accepts any iterable dictionary, not just arrays", async () => { + const { agentNameDictionaryChanges } = await load(); + const dictionary = new Map([ + ["OpenWhispr", 1], + ["Alice", 2], + ]).keys(); + assert.deepEqual(agentNameDictionaryChanges(dictionary, "OpenWhispr", "Alice"), { + add: [], + remove: ["Alice"], + }); +}); + +test("degrades a non-iterable dictionary to an empty one without throwing", async () => { + const { agentNameDictionaryChanges } = await load(); + assert.deepEqual(agentNameDictionaryChanges({ 0: "OpenWhispr" }, "Jarvis"), { + add: ["Jarvis"], + remove: [], + }); +}); + +test("does not throw when the dictionary contains non-string elements", async () => { + const { agentNameDictionaryChanges } = await load(); + assert.deepEqual(agentNameDictionaryChanges(new Set(["OpenWhispr", 42, null]), "OpenWhispr"), { + add: [], + remove: [], + }); +}); + +test("findStoredWord returns undefined for a non-string word", async () => { + const { findStoredWord } = await load(); + assert.equal(findStoredWord(["OpenWhispr"], 42), undefined); + assert.equal(findStoredWord(["OpenWhispr"], null), undefined); + assert.equal(findStoredWord(["OpenWhispr"], undefined), undefined); +}); + +test("findStoredWord matches a stored string word", async () => { + const { findStoredWord } = await load(); + assert.equal(findStoredWord(["OpenWhispr", "Alice"], "openwhispr"), "OpenWhispr"); +});