UNPKG

12.7 kBtext/coffeescriptView Raw
1ger_tests = (ESM) ->
2 ns = global.default_namespace
3
4 describe 'recommending for a person', ->
5
6 it 'should recommend similar things', ->
7 init_ger(ESM)
8 .then (ger) ->
9 bb.all([
10 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
11 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
12 ger.event(ns, 'p2','view','b', expires_at: tomorrow),
13 ])
14 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {view: 1}, filter_previous_actions: ['view']))
15 .then((recs) ->
16 recs = recs.recommendations
17 recs.length.should.equal 1
18 recs[0].thing.should.equal 'b'
19 )
20
21
22 it 'should not return a weight of NaN if person similarity is 0', ->
23 init_ger(ESM)
24 .then (ger) ->
25 bb.all([
26 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
27 ger.event(ns, 'p2','view','x', created_at: today, expires_at: tomorrow),
28 ger.event(ns, 'p2','view','a', created_at: yesterday, expires_at: tomorrow),
29 ])
30 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {view: 1}, filter_previous_actions: ['view'], neighbourhood_search_size: 1))
31 .then((recs) ->
32 for r in recs.recommendations
33 throw "BAD WEIGHT #{r.weight}" if not _.isFinite(r.weight)
34
35 throw "BAD Confidence #{recommendations_object.confidence}" if not _.isFinite(recs.confidence)
36 )
37
38 describe 'time_until_expiry', ->
39 it 'should not return recommendations that will expire within time_until_expiry seconds', ->
40 one_hour = 60*60
41 one_day = 24*one_hour
42 a1day = moment().add(1, 'days').format()
43 a2days = moment().add(2, 'days').format()
44 a3days = moment().add(3, 'days').format()
45
46 init_ger(ESM)
47 .then (ger) ->
48 bb.all([
49 ger.event(ns, 'p1','view','a'),
50
51 ger.event(ns, 'p2','view','a'),
52 ger.event(ns, 'p2','buy','x', expires_at: a1day),
53 ger.event(ns, 'p2','buy','y', expires_at: a2days),
54 ger.event(ns, 'p2','buy','z', expires_at: a3days)
55 ])
56 .then(-> ger.recommendations_for_person(ns, 'p1', time_until_expiry: (one_day + one_hour), actions: {view: 1, buy: 1}))
57 .then((recs) ->
58 recs = recs.recommendations
59 recs.length.should.equal 2
60 sorted_recs = [recs[0].thing, recs[1].thing].sort()
61 sorted_recs[0].should.equal 'y'
62 sorted_recs[1].should.equal 'z'
63 )
64
65 describe "minimum_history_required", ->
66 it "should not generate recommendations for events ", ->
67 init_ger(ESM)
68 .then (ger) ->
69 bb.all([
70 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
71 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
72 ger.event(ns, 'p2','view','b', expires_at: tomorrow),
73 ])
74 .then(-> ger.recommendations_for_person(ns, 'p1', minimum_history_required: 2, actions: {view: 1}))
75 .then((recs) ->
76 recs.recommendations.length.should.equal 0
77 ger.recommendations_for_person(ns, 'p2', minimum_history_required: 2, actions: {view: 1})
78 ).then((recs) ->
79 recs.recommendations.length.should.equal 2
80 )
81
82
83 describe "joining multiple gers", ->
84 it "similar recommendations should return same confidence", ->
85 ns1 = 'ger_1'
86 ns2 = 'ger_2'
87 bb.all([
88 init_ger(ESM, ns1),
89 init_ger(ESM, ns2)
90 ])
91 .spread (ger1, ger2) ->
92 bb.all([
93
94 ger1.event(ns1, 'p1','view','a', expires_at: tomorrow),
95 ger1.event(ns1, 'p2','view','a', expires_at: tomorrow),
96 ger1.event(ns1, 'p2','buy','b', expires_at: tomorrow),
97
98 ger2.event(ns2, 'p1','view','a', expires_at: tomorrow),
99 ger2.event(ns2, 'p2','view','a', expires_at: tomorrow),
100 ger2.event(ns2, 'p2','buy','b', expires_at: tomorrow),
101 ])
102 .then( -> bb.all([
103 ger1.recommendations_for_person(ns1, 'p1', {neighbourhood_size: 2, neighbourhood_search_size: 4, actions: {view: 1}}),
104 ger2.recommendations_for_person(ns2, 'p1', {neighbourhood_size: 4, neighbourhood_search_size: 8, actions: {view: 1}})
105 ])
106 )
107 .spread((recs1, recs2) ->
108 recs1.confidence.should.equal recs2.confidence
109 )
110
111
112 describe "confidence", ->
113
114 it 'should return a confidence ', ->
115 init_ger(ESM)
116 .then (ger) ->
117 bb.all([
118 ger.event(ns, 'p1','action1','a', expires_at: tomorrow),
119 ger.event(ns, 'p2','action1','a', expires_at: tomorrow),
120 ])
121 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {action1: 1}))
122 .then((similar_people) ->
123 similar_people.confidence.should.exist
124 )
125
126 it 'should return a confidence of 0 not NaN', ->
127 init_ger(ESM)
128 .then (ger) ->
129 bb.all([
130 ger.event(ns, 'p1','action1','a', expires_at: tomorrow)
131 ])
132 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {action1: 1}))
133 .then((similar_people) ->
134 similar_people.confidence.should.equal 0
135 )
136
137 it "higher weighted recommendations should return greater confidence", ->
138 init_ger(ESM)
139 .then (ger) ->
140 bb.all([
141 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
142 ger.event(ns, 'p1','view','b', expires_at: tomorrow),
143 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
144 ger.event(ns, 'p2','view','b', expires_at: tomorrow),
145 ger.event(ns, 'p2','view','c', expires_at: tomorrow),
146
147 ger.event(ns, 'p3','view','x', expires_at: tomorrow),
148 ger.event(ns, 'p3','view','y', expires_at: tomorrow),
149 ger.event(ns, 'p4','view','x', expires_at: tomorrow),
150 ger.event(ns, 'p4','view','z', expires_at: tomorrow),
151 ])
152 .then(->
153 bb.all([
154 ger.recommendations_for_person(ns, 'p1', actions: {view: 1})
155 ger.recommendations_for_person(ns, 'p3', actions: {view: 1})
156 ])
157 )
158 .spread((recs1, recs2) ->
159 recs1.confidence.should.greaterThan recs2.confidence
160 )
161
162 it "more similar people should return greater confidence", ->
163 init_ger(ESM)
164 .then (ger) ->
165 bb.all([
166 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
167 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
168
169 ger.event(ns, 'p3','view','b', expires_at: tomorrow),
170 ger.event(ns, 'p4','view','b', expires_at: tomorrow),
171 ger.event(ns, 'p5','view','b', expires_at: tomorrow),
172 ])
173 .then(->
174 bb.all([
175 ger.recommendations_for_person(ns, 'p1', actions: {view: 1})
176 ger.recommendations_for_person(ns, 'p3', actions: {view: 1})
177 ])
178 )
179 .spread((recs1, recs2) ->
180
181 recs2.confidence.should.greaterThan recs1.confidence
182 )
183
184 it "longer history should mean more confidence", ->
185 init_ger(ESM)
186 .then (ger) ->
187 bb.all([
188 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
189 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
190
191 ger.event(ns, 'p3','view','x', expires_at: tomorrow),
192 ger.event(ns, 'p3','view','b', expires_at: tomorrow),
193 ger.event(ns, 'p4','view','x', expires_at: tomorrow),
194 ger.event(ns, 'p4','view','b', expires_at: tomorrow),
195 ])
196 .then(->
197 bb.all([
198 ger.recommendations_for_person(ns, 'p1', actions: {view: 1})
199 ger.recommendations_for_person(ns, 'p3', actions: {view: 1})
200 ])
201 )
202 .spread((recs1, recs2) ->
203
204 recs2.confidence.should.greaterThan recs1.confidence
205 )
206
207 it "should not return NaN as conifdence", ->
208 init_ger(ESM)
209 .then (ger) ->
210 bb.all([
211 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
212 ])
213 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {view: 1}))
214 .then((recs) ->
215 recs.confidence.should.equal 0
216 )
217
218 describe "weights", ->
219 it "weights should determine the order of the recommendations", ->
220 init_ger(ESM)
221 .then (ger) ->
222 bb.all([
223 ger.event(ns, 'p1','view','a', expires_at: tomorrow),
224 ger.event(ns, 'p1','buy','b', expires_at: tomorrow),
225
226 ger.event(ns, 'p2','view','a', expires_at: tomorrow),
227 ger.event(ns, 'p2','view','c', expires_at: tomorrow),
228
229 ger.event(ns, 'p3','buy','b', expires_at: tomorrow),
230 ger.event(ns, 'p3','buy','d', expires_at: tomorrow),
231 ])
232 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {view: 1, buy: 1}, filter_previous_actions: ['buy', 'view'] ))
233 .then((recs) ->
234 item_weights = recs.recommendations
235 item_weights.length.should.equal 2
236 item_weights[0].weight.should.equal item_weights[1].weight
237
238 ger.recommendations_for_person(ns, 'p1', actions: {view: 1, buy: 2}, filter_previous_actions: ['buy', 'view'])
239 )
240 .then((recs) ->
241 item_weights = recs.recommendations
242 item_weights[0].weight.should.be.greaterThan item_weights[1].weight
243 item_weights[0].thing.should.equal 'd'
244 item_weights[1].thing.should.equal 'c'
245 )
246
247 it 'should negative weights should reduce recommended item', ->
248 init_ger(ESM)
249 .then (ger) ->
250 bb.all([
251 ger.event(ns, 'p1','likes','a'),
252 ger.event(ns, 'p1','likes','b'),
253
254 ger.event(ns, 'p2','likes','a'),
255 ger.event(ns, 'p2','hates','b'),
256 ger.event(ns, 'p2','likes','x', expires_at: tomorrow),
257
258 ger.event(ns, 'p3','likes','a'),
259 ger.event(ns, 'p3','likes','b'),
260 ger.event(ns, 'p3','likes','y', expires_at: tomorrow),
261
262 ])
263 .then(-> ger.recommendations_for_person(ns, 'p1', actions: {likes: 1, hates: -1}))
264 .then((recs) ->
265
266 item_weights = recs.recommendations
267 item_weights.length.should.equal 2
268 item_weights[0].thing.should.equal 'y'
269 item_weights[1].thing.should.equal 'x'
270 item_weights[1].weight.should.be.lessThan item_weights[0].weight
271 )
272
273 describe "person exploits,", ->
274 it 'recommendations_per_neighbour should stop one persons recommendations eliminating the other recommendations', ->
275 init_ger(ESM)
276 .then (ger) ->
277 bb.all([
278 ger.event(ns, 'p1','view','a'),
279 ger.event(ns, 'p1','view','b'),
280 #p2 is closer to p1, but theie recommendation was 2 days ago. It should still be included
281 ger.event(ns, 'p2','view','a'),
282 ger.event(ns, 'p2','view','b'),
283 ger.event(ns, 'p2','buy','x', created_at: moment().subtract(2, 'days').toDate(), expires_at: tomorrow),
284
285 ger.event(ns, 'p3','view','a'),
286 ger.event(ns, 'p3','buy','l', created_at: moment().subtract(3, 'hours').toDate(), expires_at: tomorrow),
287 ger.event(ns, 'p3','buy','m', created_at: moment().subtract(2, 'hours').toDate(), expires_at: tomorrow),
288 ger.event(ns, 'p3','buy','n', created_at: moment().subtract(1, 'hours').toDate(), expires_at: tomorrow)
289 ])
290 .then(-> ger.recommendations_for_person(ns, 'p1', recommendations_per_neighbour: 1, actions: {buy: 5, view: 1}))
291 .then((recs) ->
292 item_weights = recs.recommendations
293 item_weights.length.should.equal 2
294 item_weights[0].thing.should.equal 'x'
295 item_weights[1].thing.should.equal 'n'
296 )
297
298
299 it "a single persons mass interaction should not outweigh 'real' interations", ->
300 init_ger(ESM)
301 .then (ger) ->
302 events = []
303 for x in [1..100]
304 events.push ger.event(ns, "bad_person",'view','t1', expires_at: tomorrow)
305 events.push ger.event(ns, "bad_person",'buy','t1', expires_at: tomorrow)
306
307 bb.all(events)
308 .then( ->
309 bb.all([
310 ger.event(ns, 'real_person', 'view', 't2', expires_at: tomorrow)
311 ger.event(ns, 'real_person', 'buy', 't2', expires_at: tomorrow)
312 ger.event(ns, 'person', 'view', 't1', expires_at: tomorrow)
313 ger.event(ns, 'person', 'view', 't2', expires_at: tomorrow)
314 ])
315 )
316 .then( ->
317 ger.recommendations_for_person(ns, 'person', actions: {buy:1, view:1})
318 )
319 .then((recs) ->
320 item_weights = recs.recommendations
321 temp = {}
322 (temp[tw.thing] = tw.weight for tw in item_weights)
323 temp['t1'].should.equal temp['t2']
324 )
325
326module.exports = ger_tests;