Source code
Revision control
Copy as Markdown
Other Tools
import { GlobalOverrider } from "test/unit/utils";
import { PersonalityProvider } from "lib/PersonalityProvider/PersonalityProvider.sys.mjs";
describe("Personality Provider", () => {
let instance;
let RemoteSettingsStub;
let RemoteSettingsOnStub;
let RemoteSettingsOffStub;
let RemoteSettingsGetStub;
let sandbox;
let globals;
beforeEach(() => {
sandbox = sinon.createSandbox();
globals = new GlobalOverrider();
RemoteSettingsOnStub = sandbox.stub().returns();
RemoteSettingsOffStub = sandbox.stub().returns();
RemoteSettingsGetStub = sandbox.stub().returns([]);
RemoteSettingsStub = () => ({
get: RemoteSettingsGetStub,
on: RemoteSettingsOnStub,
off: RemoteSettingsOffStub,
});
sinon.spy(global, "BasePromiseWorker");
sinon.spy(global.BasePromiseWorker.prototype, "post");
globals.set("RemoteSettings", RemoteSettingsStub);
globals.set("Utils", {
});
instance = new PersonalityProvider();
instance.interestConfig = {
history_item_builder: "history_item_builder",
history_required_fields: ["a", "b", "c"],
interest_finalizer: "interest_finalizer",
item_to_rank_builder: "item_to_rank_builder",
item_ranker: "item_ranker",
interest_combiner: "interest_combiner",
};
});
afterEach(() => {
sinon.restore();
sandbox.restore();
globals.restore();
});
describe("#personalityProviderWorker", () => {
it("should create a new promise worker on first call", async () => {
const { personalityProviderWorker } = instance;
assert.calledOnce(global.BasePromiseWorker);
assert.isDefined(personalityProviderWorker);
});
it("should cache _personalityProviderWorker on first call", async () => {
instance._personalityProviderWorker = null;
const { personalityProviderWorker } = instance;
assert.isDefined(instance._personalityProviderWorker);
assert.isDefined(personalityProviderWorker);
});
it("should use old promise worker on second call", async () => {
let { personalityProviderWorker } = instance;
personalityProviderWorker = instance.personalityProviderWorker;
assert.calledOnce(global.BasePromiseWorker);
assert.isDefined(personalityProviderWorker);
});
});
describe("#setup", () => {
it("should setup two sync attachments", () => {
sinon.spy(instance, "setupSyncAttachment");
instance.setup();
assert.calledTwice(instance.setupSyncAttachment);
});
});
describe("#teardown", () => {
it("should teardown two sync attachments", () => {
sinon.spy(instance, "teardownSyncAttachment");
instance.teardown();
assert.calledTwice(instance.teardownSyncAttachment);
});
it("should terminate worker", () => {
const terminateStub = sandbox.stub().returns();
instance._personalityProviderWorker = {
terminate: terminateStub,
};
instance.teardown();
assert.calledOnce(terminateStub);
});
});
describe("#setupSyncAttachment", () => {
it("should call remote settings on twice for setupSyncAttachment", () => {
assert.calledTwice(RemoteSettingsOnStub);
});
});
describe("#teardownSyncAttachment", () => {
it("should call remote settings off for teardownSyncAttachment", () => {
instance.teardownSyncAttachment();
assert.calledOnce(RemoteSettingsOffStub);
});
});
describe("#onSync", () => {
it("should call worker onSync", () => {
instance.onSync();
assert.calledWith(global.BasePromiseWorker.prototype.post, "onSync");
});
});
describe("#getAttachment", () => {
it("should call worker onSync", () => {
instance.getAttachment();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"getAttachment"
);
});
});
describe("#getRecipe", () => {
it("should call worker getRecipe and remote settings get", async () => {
RemoteSettingsGetStub = sandbox.stub().returns([
{
key: 1,
},
]);
sinon.spy(instance, "getAttachment");
RemoteSettingsStub = () => ({
get: RemoteSettingsGetStub,
on: RemoteSettingsOnStub,
off: RemoteSettingsOffStub,
});
globals.set("RemoteSettings", RemoteSettingsStub);
const result = await instance.getRecipe();
assert.calledOnce(RemoteSettingsGetStub);
assert.calledOnce(instance.getAttachment);
assert.equal(result.recordKey, 1);
});
});
describe("#fetchHistory", () => {
it("should return a history object for fetchHistory", async () => {
const history = await instance.fetchHistory(["requiredColumn"], 1, 1);
assert.equal(
history.sql,
`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`
);
assert.equal(history.options.columns.length, 1);
assert.equal(Object.keys(history.options.params).length, 0);
});
});
describe("#getHistory", () => {
it("should return an empty array", async () => {
instance.interestConfig = {
history_required_fields: [],
};
const result = await instance.getHistory();
assert.equal(result.length, 0);
});
it("should call fetchHistory", async () => {
sinon.spy(instance, "fetchHistory");
await instance.getHistory();
});
});
describe("#setBaseAttachmentsURL", () => {
it("should call worker setBaseAttachmentsURL", async () => {
await instance.setBaseAttachmentsURL();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"setBaseAttachmentsURL"
);
});
});
describe("#setInterestConfig", () => {
it("should call worker setInterestConfig", async () => {
await instance.setInterestConfig();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"setInterestConfig"
);
});
});
describe("#setInterestVector", () => {
it("should call worker setInterestVector", async () => {
await instance.setInterestVector();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"setInterestVector"
);
});
});
describe("#fetchModels", () => {
it("should call worker fetchModels and remote settings get", async () => {
await instance.fetchModels();
assert.calledOnce(RemoteSettingsGetStub);
assert.calledWith(global.BasePromiseWorker.prototype.post, "fetchModels");
});
});
describe("#generateTaggers", () => {
it("should call worker generateTaggers", async () => {
await instance.generateTaggers();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"generateTaggers"
);
});
});
describe("#generateRecipeExecutor", () => {
it("should call worker generateRecipeExecutor", async () => {
await instance.generateRecipeExecutor();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"generateRecipeExecutor"
);
});
});
describe("#createInterestVector", () => {
it("should call worker createInterestVector", async () => {
await instance.createInterestVector();
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"createInterestVector"
);
});
});
describe("#init", () => {
it("should return early if setInterestConfig fails", async () => {
sandbox.stub(instance, "setBaseAttachmentsURL").returns();
sandbox.stub(instance, "setInterestConfig").returns();
instance.interestConfig = null;
const callback = globals.sandbox.stub();
await instance.init(callback);
assert.notCalled(callback);
});
it("should return early if fetchModels fails", async () => {
sandbox.stub(instance, "setBaseAttachmentsURL").returns();
sandbox.stub(instance, "setInterestConfig").returns();
sandbox.stub(instance, "fetchModels").resolves({
ok: false,
});
const callback = globals.sandbox.stub();
await instance.init(callback);
assert.notCalled(callback);
});
it("should return early if createInterestVector fails", async () => {
sandbox.stub(instance, "setBaseAttachmentsURL").returns();
sandbox.stub(instance, "setInterestConfig").returns();
sandbox.stub(instance, "fetchModels").resolves({
ok: true,
});
sandbox.stub(instance, "generateRecipeExecutor").resolves({
ok: true,
});
sandbox.stub(instance, "createInterestVector").resolves({
ok: false,
});
const callback = globals.sandbox.stub();
await instance.init(callback);
assert.notCalled(callback);
});
it("should call callback on successful init", async () => {
sandbox.stub(instance, "setBaseAttachmentsURL").returns();
sandbox.stub(instance, "setInterestConfig").returns();
sandbox.stub(instance, "fetchModels").resolves({
ok: true,
});
sandbox.stub(instance, "generateRecipeExecutor").resolves({
ok: true,
});
sandbox.stub(instance, "createInterestVector").resolves({
ok: true,
});
sandbox.stub(instance, "setInterestVector").resolves();
const callback = globals.sandbox.stub();
await instance.init(callback);
assert.calledOnce(callback);
assert.isTrue(instance.initialized);
});
it("should do generic init stuff when calling init with no cache", async () => {
sandbox.stub(instance, "setBaseAttachmentsURL").returns();
sandbox.stub(instance, "setInterestConfig").returns();
sandbox.stub(instance, "fetchModels").resolves({
ok: true,
});
sandbox.stub(instance, "generateRecipeExecutor").resolves({
ok: true,
});
sandbox.stub(instance, "createInterestVector").resolves({
ok: true,
interestVector: "interestVector",
});
sandbox.stub(instance, "setInterestVector").resolves();
await instance.init();
assert.calledOnce(instance.setBaseAttachmentsURL);
assert.calledOnce(instance.setInterestConfig);
assert.calledOnce(instance.fetchModels);
assert.calledOnce(instance.generateRecipeExecutor);
assert.calledOnce(instance.createInterestVector);
assert.calledOnce(instance.setInterestVector);
});
});
describe("#calculateItemRelevanceScore", () => {
it("should return score for uninitialized provider", async () => {
instance.initialized = false;
assert.equal(
await instance.calculateItemRelevanceScore({ item_score: 2 }),
2
);
});
it("should return score for initialized provider", async () => {
instance.initialized = true;
instance._personalityProviderWorker = {
post: (postName, [item]) => ({
rankingVector: { score: item.item_score },
}),
};
assert.equal(
await instance.calculateItemRelevanceScore({ item_score: 2 }),
2
);
});
it("should post calculateItemRelevanceScore to PersonalityProviderWorker", async () => {
instance.initialized = true;
await instance.calculateItemRelevanceScore({ item_score: 2 });
assert.calledWith(
global.BasePromiseWorker.prototype.post,
"calculateItemRelevanceScore"
);
});
});
describe("#getScores", () => {
it("should return correct data for getScores", () => {
const scores = instance.getScores();
assert.isDefined(scores.interestConfig);
});
});
});