UNPKG

6.23 kBJavaScriptView Raw
1/*global describe:true, it:true, before:true, after:true */
2
3var
4 demand = require('must'),
5 Lightcycle = require('../index')
6 ;
7
8function MockResource(name)
9{
10 this._name = name;
11}
12
13MockResource.prototype.name = function name()
14{
15 return this._name;
16};
17
18function makeFruitCycle()
19{
20 var cycle = new Lightcycle({ size: 10, replicas: 3 });
21 var r1 = new MockResource('kiwi');
22 var r2 = new MockResource('papaya');
23 var r3 = new MockResource('litchi');
24
25 cycle.add(r1, r1.name());
26 cycle.add(r2, r2.name());
27 cycle.add(r3, r3.name());
28
29 return cycle;
30}
31
32describe('light-cycle', () =>
33{
34 describe('constructor', () =>
35 {
36 it('demands a positive integer size setting', () =>
37 {
38 function mustThrow()
39 {
40 return new Lightcycle({ size: -3 });
41 }
42 mustThrow.must.throw(Error);
43 });
44
45 it('provides a default hash seed', () =>
46 {
47 var cycle = new Lightcycle({ });
48 cycle.seed.must.equal(0xcafed00d);
49 });
50
51 it('respects the hash seed setting', () =>
52 {
53 var cycle = new Lightcycle({ seed: 0xdeadbeef });
54 cycle.seed.must.equal(0xdeadbeef);
55 });
56
57 it('defaults size to 128', () =>
58 {
59 var cycle = new Lightcycle({ });
60 cycle.size.must.equal(128);
61 });
62
63 it('defaults replica count to 128', () =>
64 {
65 var cycle = new Lightcycle({ });
66 cycle.replicas.must.equal(128);
67 });
68
69 it('defaults replica count to size if size is passed in', () =>
70 {
71 var cycle = new Lightcycle({ size: 1024 });
72 cycle.replicas.must.equal(1024);
73 });
74
75 it('obeys both size & replica settings if provided', () =>
76 {
77 var cycle = new Lightcycle({ size: 10, replicas: 3 });
78 cycle.size.must.equal(10);
79 cycle.replicas.must.equal(3);
80 });
81 });
82
83 describe('add()', () =>
84 {
85 it('demands both resource and id parameters', () =>
86 {
87 var cycle = new Lightcycle({ size: 10, replicas: 3 });
88 var resource = { name: 'nameless' };
89
90 function mustThrow()
91 {
92 cycle.add(resource);
93 }
94
95 mustThrow.must.throw(Error);
96 });
97
98 it('adds a resource to the cycle', () =>
99 {
100 var cycle = new Lightcycle({ size: 10, replicas: 3 });
101 var resource = new MockResource('kiwi');
102
103 cycle.add(resource, resource.name());
104
105 var key1 = cycle.hashit(resource.name() + '0');
106 var item = cycle.resources.match(key1);
107
108 item.must.exist();
109 item.must.equal(resource);
110 });
111
112 it('adds `replicas` count replicas to the cycle', () =>
113 {
114 var cycle = new Lightcycle({ size: 10, replicas: 3 });
115 var resource = new MockResource('kiwi');
116
117 cycle.add(resource, resource.name());
118
119 var allEntries = cycle.resources.find();
120 allEntries.length.must.equal(3);
121 });
122
123 it('adding twice has no ill effect', () =>
124 {
125 var cycle = new Lightcycle({ size: 10, replicas: 3 });
126 var resource = new MockResource('kiwi');
127
128 cycle.add(resource, resource.name());
129 cycle.add(resource, resource.name());
130 var allEntries = cycle.resources.find();
131 allEntries.length.must.equal(3);
132 });
133 });
134
135 describe('all()', () =>
136 {
137 it('returns a hash of the resources & ids', () =>
138 {
139 var cycle = makeFruitCycle();
140 var entries = cycle.all();
141
142 entries.must.be.an.object();
143 Object.keys(entries).length.must.equal(3);
144 entries.kiwi.must.exist();
145 entries.papaya.must.exist();
146 entries.litchi.must.exist();
147 });
148 });
149
150 describe('remove()', () =>
151 {
152 it('removes all replicas from the cycle', () =>
153 {
154 var cycle = new Lightcycle({ size: 10, replicas: 3 });
155 var r1 = new MockResource('kiwi');
156 var r2 = new MockResource('papaya');
157
158 cycle.add(r1, r1.name());
159 cycle.add(r2, r2.name());
160
161 var allItems = cycle.resources.find();
162 allItems.length.must.equal(6);
163
164 cycle.remove(r1.name());
165
166 var afterItems = cycle.resources.find();
167 afterItems.length.must.equal(3);
168
169 var key1 = cycle.hashit(r1.name() + '0');
170
171 var found = cycle.resources.match(key1);
172 demand(found).be.null();
173 });
174
175 it('silently ignores items that are not in the cycle', () =>
176 {
177 var cycle = new Lightcycle({ size: 10, replicas: 3 });
178 var r1 = new MockResource('kiwi');
179 var r2 = new MockResource('papaya');
180 var r3 = new MockResource('litchi');
181
182 cycle.add(r1, r1.name());
183 cycle.add(r2, r2.name());
184
185 cycle.remove(r3.name());
186 });
187 });
188
189 describe('locate()', () =>
190 {
191 it('returns a single resource for a given id', () =>
192 {
193 var cycle = makeFruitCycle();
194 var loc = cycle.locate('pomegranate');
195 loc.must.exist();
196 });
197
198 it('handles the case of resources at the end of the circle by returning the first resource', () =>
199 {
200 var cycle = new Lightcycle();
201
202 var r1 = new MockResource('durian');
203 var r2 = new MockResource('gooseberry');
204
205 cycle.resources.insert('a', r1);
206 cycle.resources.insert('b', r2);
207
208 var key = cycle.hashit('pomegranate');
209 key.must.be.a.string();
210 var loc = cycle.locate('pomegranate');
211 loc.must.equal(r1);
212 });
213
214 it('handles ids that are buffers', () =>
215 {
216 var cycle = makeFruitCycle();
217 var loc = cycle.locate(new Buffer('mangosteen'));
218 loc.must.exist();
219 });
220
221 it('returns null when asked to locate an id when no resources are in the cycle', () =>
222 {
223 var cycle = new Lightcycle();
224 var location = cycle.locate('kumquat');
225 demand(location).be.null();
226 });
227
228 it('gives the correct new location for items that used to live on the removed resource', () =>
229 {
230 var cycle = makeFruitCycle();
231
232 var originalLoc = cycle.locate('pomegranate');
233 cycle.remove(originalLoc.name());
234
235 var newLoc = cycle.locate('pomegranate');
236 newLoc.must.be.truthy();
237 newLoc.name().must.not.equal(originalLoc.name());
238 });
239 });
240
241 describe('rebalance', () =>
242 {
243 it('is triggered when adding makes the number of entries greater than the size', () =>
244 {
245 var cycle = new Lightcycle({ size: 2, replicas: 2 });
246 var r1 = new MockResource('durian');
247 var r2 = new MockResource('gooseberry');
248 var r3 = new MockResource('kumquat');
249
250 cycle.add(r1, r1.name());
251 cycle.add(r2, r2.name());
252 cycle.add(r3, r3.name());
253
254 cycle.size.must.equal(3 + Lightcycle.SIZE_PAD,
255 'expected size to be ' + Lightcycle.SIZE_PAD + ' + the number of entries');
256 cycle.replicas.must.equal(3 + Lightcycle.REPLICAS_PAD,
257 'expected replica count to be ' + Lightcycle.REPLICAS_PAD + ' + the number of entries');
258 });
259 });
260});