UserDomainAffinityProvider.test.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import {GlobalOverrider} from "test/unit/utils";
  2. import {UserDomainAffinityProvider} from "lib/UserDomainAffinityProvider.jsm";
  3. const TIME_SEGMENTS = [
  4. {"id": "hour", "startTime": 3600, "endTime": 0, "weightPosition": 1},
  5. {"id": "day", "startTime": 86400, "endTime": 3600, "weightPosition": 0.75},
  6. {"id": "week", "startTime": 604800, "endTime": 86400, "weightPosition": 0.5},
  7. {"id": "weekPlus", "startTime": null, "endTime": 604800, "weightPosition": 0.25},
  8. ];
  9. const PARAMETER_SETS = {
  10. "paramSet1": {
  11. "recencyFactor": 0.5,
  12. "frequencyFactor": 0.5,
  13. "combinedDomainFactor": 0.5,
  14. "perfectFrequencyVisits": 10,
  15. "perfectCombinedDomainScore": 2,
  16. "multiDomainBoost": 0.1,
  17. "itemScoreFactor": 0,
  18. },
  19. "paramSet2": {
  20. "recencyFactor": 1,
  21. "frequencyFactor": 0.7,
  22. "combinedDomainFactor": 0.8,
  23. "perfectFrequencyVisits": 10,
  24. "perfectCombinedDomainScore": 2,
  25. "multiDomainBoost": 0.1,
  26. "itemScoreFactor": 0,
  27. },
  28. };
  29. describe("User Domain Affinity Provider", () => {
  30. let instance;
  31. let globals;
  32. beforeEach(() => {
  33. globals = new GlobalOverrider();
  34. const testUrl = "www.somedomain.com";
  35. globals.sandbox.stub(global.Services.io, "newURI").returns({host: testUrl});
  36. globals.sandbox.stub(global.PlacesUtils.history, "executeQuery").returns({root: {childCount: 1, getChild: index => ({uri: testUrl, accessCount: 1})}});
  37. globals.sandbox.stub(global.PlacesUtils.history, "getNewQuery").returns({"TIME_RELATIVE_NOW": 1});
  38. globals.sandbox.stub(global.PlacesUtils.history, "getNewQueryOptions").returns({});
  39. instance = new UserDomainAffinityProvider(TIME_SEGMENTS, PARAMETER_SETS);
  40. });
  41. afterEach(() => {
  42. globals.restore();
  43. });
  44. describe("#init", () => {
  45. function calculateScore(visitCounts, timeSeg, domain, ps) {
  46. const vc = visitCounts[timeSeg][domain];
  47. const score = instance.calculateScore(vc * Number(ps.timeSegmentWeights[timeSeg]), ps.perfectFrequencyVisits, ps.frequencyFactor);
  48. return Math.min(1, score);
  49. }
  50. it("should create a UserDomainAffinityProvider", () => {
  51. assert.instanceOf(instance, UserDomainAffinityProvider);
  52. });
  53. it("should calculate time segment weights for parameter sets", () => {
  54. const expectedParamSets = Object.assign({}, PARAMETER_SETS);
  55. // Verify that parameter set specific recencyFactor was applied
  56. expectedParamSets.paramSet1.timeSegmentWeights = {"hour": 1, "day": 0.75, "week": 0.5, "weekPlus": 0.25};
  57. expectedParamSets.paramSet2.timeSegmentWeights = {"hour": 1, "day": 1, "week": 1, "weekPlus": 1};
  58. assert.deepEqual(expectedParamSets, instance.parameterSets);
  59. });
  60. it("should calculate user domain affinity scores", () => {
  61. const ps1 = instance.parameterSets.paramSet1;
  62. const ps2 = instance.parameterSets.paramSet2;
  63. const visitCounts = {
  64. "hour": {"a.com": 1, "b.com": 2},
  65. "day": {"a.com": 4},
  66. "week": {"c.com": 1},
  67. "weekPlus": {"a.com": 1, "d.com": 3},
  68. };
  69. instance.queryVisits = ts => visitCounts[ts.id];
  70. const expScoreAHourPs1 = calculateScore(visitCounts, "hour", "a.com", ps1);
  71. const expScoreBHourPs1 = calculateScore(visitCounts, "hour", "b.com", ps1);
  72. const expScoreAHourPs2 = calculateScore(visitCounts, "hour", "a.com", ps2);
  73. const expScoreBHourPs2 = calculateScore(visitCounts, "hour", "b.com", ps2);
  74. const expScoreADayPs1 = calculateScore(visitCounts, "day", "a.com", ps1);
  75. const expScoreADayPs2 = calculateScore(visitCounts, "day", "a.com", ps2);
  76. const expScoreCWeekPs1 = calculateScore(visitCounts, "week", "c.com", ps1);
  77. const expScoreCWeekPs2 = calculateScore(visitCounts, "week", "c.com", ps2);
  78. const expScoreAWeekPlusPs1 = calculateScore(visitCounts, "weekPlus", "a.com", ps1);
  79. const expScoreAWeekPlusPs2 = calculateScore(visitCounts, "weekPlus", "a.com", ps2);
  80. const expScoreDWeekPlusPs1 = calculateScore(visitCounts, "weekPlus", "d.com", ps1);
  81. const expScoreDWeekPlusPs2 = calculateScore(visitCounts, "weekPlus", "d.com", ps2);
  82. const expectedScores = {
  83. "a.com": {
  84. "paramSet1": Math.min(1, expScoreAHourPs1 + expScoreADayPs1 + expScoreAWeekPlusPs1),
  85. "paramSet2": Math.min(1, expScoreAHourPs2 + expScoreADayPs2 + expScoreAWeekPlusPs2),
  86. },
  87. "b.com": {"paramSet1": expScoreBHourPs1, "paramSet2": expScoreBHourPs2},
  88. "c.com": {"paramSet1": expScoreCWeekPs1, "paramSet2": expScoreCWeekPs2},
  89. "d.com": {"paramSet1": expScoreDWeekPlusPs1, "paramSet2": expScoreDWeekPlusPs2},
  90. };
  91. const scores = instance.calculateAllUserDomainAffinityScores();
  92. assert.deepEqual(expectedScores, scores);
  93. });
  94. it("should return domain affinities", () => {
  95. const scores = {
  96. "a.com": {
  97. "paramSet1": 1,
  98. "paramSet2": 0.9,
  99. },
  100. };
  101. instance = new UserDomainAffinityProvider(TIME_SEGMENTS, PARAMETER_SETS, 100, "v1", scores);
  102. const expectedAffinities = {
  103. "timeSegments": TIME_SEGMENTS,
  104. "parameterSets": PARAMETER_SETS,
  105. "maxHistoryQueryResults": 100,
  106. "scores": scores,
  107. "version": "v1",
  108. };
  109. assert.deepEqual(instance.getAffinities(), expectedAffinities);
  110. });
  111. });
  112. describe("#score", () => {
  113. it("should calculate item relevance score", () => {
  114. const ps = instance.parameterSets.paramSet2;
  115. const visitCounts = {
  116. "hour": {"a.com": 1, "b.com": 2},
  117. "day": {"a.com": 4},
  118. "week": {"c.com": 1},
  119. "weekPlus": {"a.com": 1, "d.com": 3},
  120. };
  121. instance.queryVisits = ts => visitCounts[ts.id];
  122. instance.scores = instance.calculateAllUserDomainAffinityScores();
  123. const testItem = {
  124. "domain_affinities": {"a.com": 1},
  125. "item_score": 1,
  126. "parameter_set": "paramSet2",
  127. };
  128. const combinedDomainScore = instance.scores["a.com"].paramSet2 * Math.pow(ps.multiDomainBoost + 1, 1);
  129. const expectedItemScore = instance.calculateScore(combinedDomainScore,
  130. ps.perfectCombinedDomainScore,
  131. ps.combinedDomainFactor);
  132. const itemScore = instance.calculateItemRelevanceScore(testItem);
  133. assert.equal(expectedItemScore, itemScore);
  134. });
  135. it("should calculate relevance score equal to item_score if item has no domain affinities", () => {
  136. const testItem = {item_score: 0.985};
  137. const itemScore = instance.calculateItemRelevanceScore(testItem);
  138. assert.equal(testItem.item_score, itemScore);
  139. });
  140. it("should calculate scores with factor", () => {
  141. assert.equal(1, instance.calculateScore(2, 1, 0.5));
  142. assert.equal(0.5, instance.calculateScore(0.5, 1, 0.5));
  143. assert.isBelow(instance.calculateScore(0.5, 1, 0.49), 1);
  144. assert.isBelow(instance.calculateScore(0.5, 1, 0.51), 1);
  145. });
  146. });
  147. });