PersonalityProvider.test.js (11750B)
1 import { GlobalOverrider } from "test/unit/utils"; 2 import { PersonalityProvider } from "lib/PersonalityProvider/PersonalityProvider.sys.mjs"; 3 4 describe("Personality Provider", () => { 5 let instance; 6 let RemoteSettingsStub; 7 let RemoteSettingsOnStub; 8 let RemoteSettingsOffStub; 9 let RemoteSettingsGetStub; 10 let sandbox; 11 let globals; 12 13 beforeEach(() => { 14 sandbox = sinon.createSandbox(); 15 globals = new GlobalOverrider(); 16 17 RemoteSettingsOnStub = sandbox.stub().returns(); 18 RemoteSettingsOffStub = sandbox.stub().returns(); 19 RemoteSettingsGetStub = sandbox.stub().returns([]); 20 21 RemoteSettingsStub = () => ({ 22 get: RemoteSettingsGetStub, 23 on: RemoteSettingsOnStub, 24 off: RemoteSettingsOffStub, 25 }); 26 27 sinon.spy(global, "BasePromiseWorker"); 28 sinon.spy(global.BasePromiseWorker.prototype, "post"); 29 30 globals.set("RemoteSettings", RemoteSettingsStub); 31 globals.set("Utils", { 32 baseAttachmentsURL: () => "https://baseattachmentsurl", 33 }); 34 35 instance = new PersonalityProvider(); 36 instance.interestConfig = { 37 history_item_builder: "history_item_builder", 38 history_required_fields: ["a", "b", "c"], 39 interest_finalizer: "interest_finalizer", 40 item_to_rank_builder: "item_to_rank_builder", 41 item_ranker: "item_ranker", 42 interest_combiner: "interest_combiner", 43 }; 44 }); 45 afterEach(() => { 46 sinon.restore(); 47 sandbox.restore(); 48 globals.restore(); 49 }); 50 describe("#personalityProviderWorker", () => { 51 it("should create a new promise worker on first call", async () => { 52 const { personalityProviderWorker } = instance; 53 assert.calledOnce(global.BasePromiseWorker); 54 assert.isDefined(personalityProviderWorker); 55 }); 56 it("should cache _personalityProviderWorker on first call", async () => { 57 instance._personalityProviderWorker = null; 58 const { personalityProviderWorker } = instance; 59 assert.isDefined(instance._personalityProviderWorker); 60 assert.isDefined(personalityProviderWorker); 61 }); 62 it("should use old promise worker on second call", async () => { 63 let { personalityProviderWorker } = instance; 64 personalityProviderWorker = instance.personalityProviderWorker; 65 assert.calledOnce(global.BasePromiseWorker); 66 assert.isDefined(personalityProviderWorker); 67 }); 68 }); 69 describe("#setup", () => { 70 it("should setup two sync attachments", () => { 71 sinon.spy(instance, "setupSyncAttachment"); 72 instance.setup(); 73 assert.calledTwice(instance.setupSyncAttachment); 74 }); 75 }); 76 describe("#teardown", () => { 77 it("should teardown two sync attachments", () => { 78 sinon.spy(instance, "teardownSyncAttachment"); 79 instance.teardown(); 80 assert.calledTwice(instance.teardownSyncAttachment); 81 }); 82 it("should terminate worker", () => { 83 const terminateStub = sandbox.stub().returns(); 84 instance._personalityProviderWorker = { 85 terminate: terminateStub, 86 }; 87 instance.teardown(); 88 assert.calledOnce(terminateStub); 89 }); 90 }); 91 describe("#setupSyncAttachment", () => { 92 it("should call remote settings on twice for setupSyncAttachment", () => { 93 assert.calledTwice(RemoteSettingsOnStub); 94 }); 95 }); 96 describe("#teardownSyncAttachment", () => { 97 it("should call remote settings off for teardownSyncAttachment", () => { 98 instance.teardownSyncAttachment(); 99 assert.calledOnce(RemoteSettingsOffStub); 100 }); 101 }); 102 describe("#onSync", () => { 103 it("should call worker onSync", () => { 104 instance.onSync(); 105 assert.calledWith(global.BasePromiseWorker.prototype.post, "onSync"); 106 }); 107 }); 108 describe("#getAttachment", () => { 109 it("should call worker onSync", () => { 110 instance.getAttachment(); 111 assert.calledWith( 112 global.BasePromiseWorker.prototype.post, 113 "getAttachment" 114 ); 115 }); 116 }); 117 describe("#getRecipe", () => { 118 it("should call worker getRecipe and remote settings get", async () => { 119 RemoteSettingsGetStub = sandbox.stub().returns([ 120 { 121 key: 1, 122 }, 123 ]); 124 sinon.spy(instance, "getAttachment"); 125 RemoteSettingsStub = () => ({ 126 get: RemoteSettingsGetStub, 127 on: RemoteSettingsOnStub, 128 off: RemoteSettingsOffStub, 129 }); 130 globals.set("RemoteSettings", RemoteSettingsStub); 131 132 const result = await instance.getRecipe(); 133 assert.calledOnce(RemoteSettingsGetStub); 134 assert.calledOnce(instance.getAttachment); 135 assert.equal(result.recordKey, 1); 136 }); 137 }); 138 describe("#fetchHistory", () => { 139 it("should return a history object for fetchHistory", async () => { 140 const history = await instance.fetchHistory(["requiredColumn"], 1, 1); 141 assert.equal( 142 history.sql, 143 `SELECT url, title, visit_count, frecency, last_visit_date, description\n FROM moz_places\n WHERE last_visit_date >= 1000000\n AND last_visit_date < 1000000 AND IFNULL(requiredColumn, '') <> '' LIMIT 30000` 144 ); 145 assert.equal(history.options.columns.length, 1); 146 assert.equal(Object.keys(history.options.params).length, 0); 147 }); 148 }); 149 describe("#getHistory", () => { 150 it("should return an empty array", async () => { 151 instance.interestConfig = { 152 history_required_fields: [], 153 }; 154 const result = await instance.getHistory(); 155 assert.equal(result.length, 0); 156 }); 157 it("should call fetchHistory", async () => { 158 sinon.spy(instance, "fetchHistory"); 159 await instance.getHistory(); 160 }); 161 }); 162 describe("#setBaseAttachmentsURL", () => { 163 it("should call worker setBaseAttachmentsURL", async () => { 164 await instance.setBaseAttachmentsURL(); 165 assert.calledWith( 166 global.BasePromiseWorker.prototype.post, 167 "setBaseAttachmentsURL" 168 ); 169 }); 170 }); 171 describe("#setInterestConfig", () => { 172 it("should call worker setInterestConfig", async () => { 173 await instance.setInterestConfig(); 174 assert.calledWith( 175 global.BasePromiseWorker.prototype.post, 176 "setInterestConfig" 177 ); 178 }); 179 }); 180 describe("#setInterestVector", () => { 181 it("should call worker setInterestVector", async () => { 182 await instance.setInterestVector(); 183 assert.calledWith( 184 global.BasePromiseWorker.prototype.post, 185 "setInterestVector" 186 ); 187 }); 188 }); 189 describe("#fetchModels", () => { 190 it("should call worker fetchModels and remote settings get", async () => { 191 await instance.fetchModels(); 192 assert.calledOnce(RemoteSettingsGetStub); 193 assert.calledWith(global.BasePromiseWorker.prototype.post, "fetchModels"); 194 }); 195 }); 196 describe("#generateTaggers", () => { 197 it("should call worker generateTaggers", async () => { 198 await instance.generateTaggers(); 199 assert.calledWith( 200 global.BasePromiseWorker.prototype.post, 201 "generateTaggers" 202 ); 203 }); 204 }); 205 describe("#generateRecipeExecutor", () => { 206 it("should call worker generateRecipeExecutor", async () => { 207 await instance.generateRecipeExecutor(); 208 assert.calledWith( 209 global.BasePromiseWorker.prototype.post, 210 "generateRecipeExecutor" 211 ); 212 }); 213 }); 214 describe("#createInterestVector", () => { 215 it("should call worker createInterestVector", async () => { 216 await instance.createInterestVector(); 217 assert.calledWith( 218 global.BasePromiseWorker.prototype.post, 219 "createInterestVector" 220 ); 221 }); 222 }); 223 describe("#init", () => { 224 it("should return early if setInterestConfig fails", async () => { 225 sandbox.stub(instance, "setBaseAttachmentsURL").returns(); 226 sandbox.stub(instance, "setInterestConfig").returns(); 227 instance.interestConfig = null; 228 const callback = globals.sandbox.stub(); 229 await instance.init(callback); 230 assert.notCalled(callback); 231 }); 232 it("should return early if fetchModels fails", async () => { 233 sandbox.stub(instance, "setBaseAttachmentsURL").returns(); 234 sandbox.stub(instance, "setInterestConfig").returns(); 235 sandbox.stub(instance, "fetchModels").resolves({ 236 ok: false, 237 }); 238 const callback = globals.sandbox.stub(); 239 await instance.init(callback); 240 assert.notCalled(callback); 241 }); 242 it("should return early if createInterestVector fails", async () => { 243 sandbox.stub(instance, "setBaseAttachmentsURL").returns(); 244 sandbox.stub(instance, "setInterestConfig").returns(); 245 sandbox.stub(instance, "fetchModels").resolves({ 246 ok: true, 247 }); 248 sandbox.stub(instance, "generateRecipeExecutor").resolves({ 249 ok: true, 250 }); 251 sandbox.stub(instance, "createInterestVector").resolves({ 252 ok: false, 253 }); 254 const callback = globals.sandbox.stub(); 255 await instance.init(callback); 256 assert.notCalled(callback); 257 }); 258 it("should call callback on successful init", async () => { 259 sandbox.stub(instance, "setBaseAttachmentsURL").returns(); 260 sandbox.stub(instance, "setInterestConfig").returns(); 261 sandbox.stub(instance, "fetchModels").resolves({ 262 ok: true, 263 }); 264 sandbox.stub(instance, "generateRecipeExecutor").resolves({ 265 ok: true, 266 }); 267 sandbox.stub(instance, "createInterestVector").resolves({ 268 ok: true, 269 }); 270 sandbox.stub(instance, "setInterestVector").resolves(); 271 const callback = globals.sandbox.stub(); 272 await instance.init(callback); 273 assert.calledOnce(callback); 274 assert.isTrue(instance.initialized); 275 }); 276 it("should do generic init stuff when calling init with no cache", async () => { 277 sandbox.stub(instance, "setBaseAttachmentsURL").returns(); 278 sandbox.stub(instance, "setInterestConfig").returns(); 279 sandbox.stub(instance, "fetchModels").resolves({ 280 ok: true, 281 }); 282 sandbox.stub(instance, "generateRecipeExecutor").resolves({ 283 ok: true, 284 }); 285 sandbox.stub(instance, "createInterestVector").resolves({ 286 ok: true, 287 interestVector: "interestVector", 288 }); 289 sandbox.stub(instance, "setInterestVector").resolves(); 290 await instance.init(); 291 assert.calledOnce(instance.setBaseAttachmentsURL); 292 assert.calledOnce(instance.setInterestConfig); 293 assert.calledOnce(instance.fetchModels); 294 assert.calledOnce(instance.generateRecipeExecutor); 295 assert.calledOnce(instance.createInterestVector); 296 assert.calledOnce(instance.setInterestVector); 297 }); 298 }); 299 describe("#calculateItemRelevanceScore", () => { 300 it("should return score for uninitialized provider", async () => { 301 instance.initialized = false; 302 assert.equal( 303 await instance.calculateItemRelevanceScore({ item_score: 2 }), 304 2 305 ); 306 }); 307 it("should return score for initialized provider", async () => { 308 instance.initialized = true; 309 310 instance._personalityProviderWorker = { 311 post: (postName, [item]) => ({ 312 rankingVector: { score: item.item_score }, 313 }), 314 }; 315 316 assert.equal( 317 await instance.calculateItemRelevanceScore({ item_score: 2 }), 318 2 319 ); 320 }); 321 it("should post calculateItemRelevanceScore to PersonalityProviderWorker", async () => { 322 instance.initialized = true; 323 await instance.calculateItemRelevanceScore({ item_score: 2 }); 324 assert.calledWith( 325 global.BasePromiseWorker.prototype.post, 326 "calculateItemRelevanceScore" 327 ); 328 }); 329 }); 330 describe("#getScores", () => { 331 it("should return correct data for getScores", () => { 332 const scores = instance.getScores(); 333 assert.isDefined(scores.interestConfig); 334 }); 335 }); 336 });