1 | require('./common');
|
2 | var crypto = require("crypto");
|
3 | var _ = require("underscore");
|
4 | var querystring = require("querystring");
|
5 | var fakes = require("./fakes");
|
6 | var LastFmRequest = fakes.LastFmRequest;
|
7 |
|
8 | (function() {
|
9 | var gently, lastfm, client;
|
10 | var options, expectations;
|
11 | var notExpected;
|
12 |
|
13 | describe("a lastfm request");
|
14 |
|
15 | before(function() {
|
16 | gently = new Gently();
|
17 | options = {};
|
18 | expectations = {
|
19 | pairs:[],
|
20 | handlers:[]
|
21 | };
|
22 | notExpected = {
|
23 | keys:[]
|
24 | }
|
25 | lastfm = new LastFmNode({
|
26 | api_key: "key",
|
27 | secret: "secret"
|
28 | });
|
29 | client = new fakes.Client();
|
30 | gently.expect(GENTLY_HIJACK.hijacked.http, "createClient", function(port, host) {
|
31 | verifyCreateClient(port, host);
|
32 | return client;
|
33 | });
|
34 | gently.expect(client, "request", function(method, url, header) {
|
35 | var request = new fakes.ClientRequest();
|
36 | if (method == "POST") {
|
37 | gently.expect(request, "write", function(data) {
|
38 | verifyRequest(method, url, header, data);
|
39 | });
|
40 | }
|
41 | else {
|
42 | verifyRequest(method, url, header);
|
43 | }
|
44 | return request;
|
45 | });
|
46 | });
|
47 |
|
48 | after(function() {
|
49 | var request = doRequest();
|
50 | verifyHandlers(request);
|
51 | });
|
52 |
|
53 | function verifyCreateClient(port, host) {
|
54 | if (expectations.port) {
|
55 | assert.equal(expectations.port, port);
|
56 | }
|
57 | if (expectations.host) {
|
58 | assert.equal(expectations.host, host);
|
59 | }
|
60 | }
|
61 |
|
62 | function verifyRequest(method, url, header, data) {
|
63 | if (expectations.url) {
|
64 | assert.equal(expectations.url, url);
|
65 | }
|
66 | var pairs = querystring.parse(data || url.substr("/2.0?".length));
|
67 | _(Object.keys(expectations.pairs)).each(function(key) {
|
68 | assert.equal(expectations.pairs[key], pairs[key]);
|
69 | });
|
70 | if (expectations.signed || expectations.signatureHash) {
|
71 | assert.ok(pairs.api_sig);
|
72 | }
|
73 | else if (expectations.signed === false) {
|
74 | assert.ok(!pairs.api_sig);
|
75 | }
|
76 | if (expectations.signatureHash) {
|
77 | assert.equal(expectations.signatureHash, pairs.api_sig);
|
78 | }
|
79 | if (expectations.method) {
|
80 | assert.equal(expectations.method, method);
|
81 | }
|
82 | _(notExpected.keys).each(function(key) {
|
83 | assert.ok(!pairs[key]);
|
84 | });
|
85 | if (expectations.requestData) {
|
86 | assert.ok(data);
|
87 | }
|
88 | }
|
89 |
|
90 | function whenMethodIs(method) {
|
91 | options.method = method;
|
92 | }
|
93 |
|
94 | function andParamsAre(params) {
|
95 | options.params = params;
|
96 | }
|
97 |
|
98 | function expectHttpMethod(method) {
|
99 | expectations.method = method;
|
100 | }
|
101 |
|
102 | function expectDataPair(key, value) {
|
103 | expectations.pairs[key] = value;
|
104 | }
|
105 |
|
106 | function expectSignature() {
|
107 | expectations.signed = true;
|
108 | }
|
109 |
|
110 | function expectUrl(url) {
|
111 | expectations.url = url;
|
112 | }
|
113 |
|
114 | function expectSignatureHashOf(unhashed) {
|
115 | var expectedHash = crypto.createHash("md5").update(unhashed, "utf8").digest("hex");
|
116 | expectSignatureHashToBe(expectedHash);
|
117 | };
|
118 |
|
119 | this.expectSignatureHashToBe = function(hash) {
|
120 | expectations.signatureHash = hash;
|
121 | }
|
122 |
|
123 | function expectRequestOnPort(port) {
|
124 | expectations.port = port;
|
125 | }
|
126 |
|
127 | function expectRequestToHost(host) {
|
128 | expectations.host = host;
|
129 | }
|
130 |
|
131 | function expectHandlerFor(event) {
|
132 | expectations.handlers.push(event);
|
133 | }
|
134 |
|
135 | function expectRequestData() {
|
136 | expectations.requestData = true;
|
137 | }
|
138 |
|
139 | function doNotExpectDataKey(key) {
|
140 | notExpected.keys.push(key);
|
141 | }
|
142 |
|
143 | function doRequest() {
|
144 | return lastfm.request(options.method, options.params);
|
145 | }
|
146 |
|
147 | function verifyHandlers(request) {
|
148 | _(expectations.handlers).each(function(event) {
|
149 | var listeners = request.listeners(event);
|
150 | assert.equal(1, listeners.length, "No handler for event: " + event);
|
151 | });
|
152 | }
|
153 |
|
154 | it("default to port 80", function() {
|
155 | whenMethodIs("any.method");
|
156 | expectRequestOnPort(80);
|
157 | });
|
158 |
|
159 | it("makes request to audioscrobbler", function() {
|
160 | whenMethodIs("any.method");
|
161 | expectRequestToHost("ws.audioscrobbler.com");
|
162 | });
|
163 |
|
164 | it("always requests as json", function() {
|
165 | whenMethodIs("any.method");
|
166 | expectDataPair("format", "json");
|
167 | });
|
168 |
|
169 | it("always passes api_key", function() {
|
170 | whenMethodIs("any.method");
|
171 | expectDataPair("api_key", "key");
|
172 | });
|
173 |
|
174 | it("defaults to get request", function() {
|
175 | whenMethodIs("any.method");
|
176 | expectHttpMethod("GET");
|
177 | });
|
178 |
|
179 | it("calls the method specified", function() {
|
180 | whenMethodIs("user.getinfo");
|
181 | expectDataPair("method", "user.getinfo");
|
182 | });
|
183 |
|
184 | it("passes through parameters", function() {
|
185 | whenMethodIs("user.getinfo");
|
186 | andParamsAre({ user: "jammus" });
|
187 | expectDataPair("user", "jammus");
|
188 | });
|
189 |
|
190 | it("does not pass through event handler parameters", function() {
|
191 | whenMethodIs("any.method");
|
192 | andParamsAre({ handlers: "handlers"});
|
193 | doNotExpectDataKey("handlers");
|
194 | });
|
195 |
|
196 | it("auth.getsession has signature", function() {
|
197 | whenMethodIs("auth.getsession");
|
198 | expectSignature();
|
199 | });
|
200 |
|
201 | it("attaches handlers to returned request", function() {
|
202 | whenMethodIs("any.method");
|
203 | andParamsAre({ handlers: {
|
204 | error: function() {console.log("errrors");},
|
205 | success: function() {},
|
206 | arbitrary: function() {},
|
207 | }});
|
208 | expectHandlerFor("error");
|
209 | expectHandlerFor("success");
|
210 | expectHandlerFor("arbitrary");
|
211 | });
|
212 |
|
213 | it("uses signed param to force signature", function() {
|
214 | whenMethodIs("any.method");
|
215 | andParamsAre({
|
216 | signed: true
|
217 | });
|
218 | expectSignature();
|
219 | });
|
220 |
|
221 | it("signature hashes api_key, method and secret", function() {
|
222 | whenMethodIs("auth.getsession");
|
223 | expectSignatureHashOf("api_keykeymethodauth.getsessionsecret");
|
224 | });
|
225 |
|
226 | it("signature includes other parameters", function() {
|
227 | whenMethodIs("auth.getsession");
|
228 | andParamsAre({ foo: "bar" });
|
229 | expectSignatureHashOf("api_keykeyfoobarmethodauth.getsessionsecret");
|
230 | });
|
231 |
|
232 | it("signature hashes all params alphabetically", function() {
|
233 | whenMethodIs("auth.getsession");
|
234 | andParamsAre({ foo : "bar", baz: "bash", flip : "flop" });
|
235 | expectSignatureHashOf("api_keykeybazbashflipflopfoobarmethodauth.getsessionsecret");
|
236 | });
|
237 |
|
238 | it("signature hash ignores format parameter", function() {
|
239 | whenMethodIs("auth.getsession");
|
240 | andParamsAre({ format: "json" });
|
241 | expectSignatureHashOf("api_keykeymethodauth.getsessionsecret");
|
242 | });
|
243 |
|
244 | it("signature hash ignores handlers parameter", function() {
|
245 | whenMethodIs("auth.getsession");
|
246 | andParamsAre({ handlers: "handlers" });
|
247 | expectSignatureHashOf("api_keykeymethodauth.getsessionsecret");
|
248 | });
|
249 |
|
250 | it("signature hash ignores write parameter", function() {
|
251 | whenMethodIs("auth.getsession");
|
252 | andParamsAre({ write: true });
|
253 | expectSignatureHashOf("api_keykeymethodauth.getsessionsecret");
|
254 | });
|
255 |
|
256 | it("signature hash ignores signed parameter", function() {
|
257 | whenMethodIs("any.method");
|
258 | andParamsAre({ signed: true });
|
259 | expectSignatureHashOf("api_keykeymethodany.methodsecret");
|
260 | });
|
261 |
|
262 | it("signature hash handles high characters as expected by last.fm (utf8)", function() {
|
263 | whenMethodIs("auth.getsession");
|
264 | andParamsAre({ track: "Tony’s Theme (Remastered)" });
|
265 | expectSignatureHashToBe("15f5159046bf1e76774b9dd46a4ed993");
|
266 | });
|
267 |
|
268 | it("write requests use post", function() {
|
269 | whenMethodIs("any.method");
|
270 | andParamsAre({ write: true });
|
271 | expectHttpMethod("POST");
|
272 | });
|
273 |
|
274 | it("write requests don't use get parameters", function() {
|
275 | whenMethodIs("any.method");
|
276 | andParamsAre({ write: true });
|
277 | expectUrl("/2.0");
|
278 | });
|
279 |
|
280 | it("write requests send data in request", function() {
|
281 | whenMethodIs("any.method");
|
282 | andParamsAre({
|
283 | write: true,
|
284 | foo: "bar"
|
285 | });
|
286 | expectRequestData();
|
287 | expectDataPair("foo", "bar");
|
288 | });
|
289 |
|
290 | it("write requests are always signed", function() {
|
291 | whenMethodIs("album.removeTag");
|
292 | andParamsAre({
|
293 | write: true
|
294 | });
|
295 | expectSignature();
|
296 | });
|
297 |
|
298 | _(["album.addTags", "album.removeTag", "album.share",
|
299 | "artist.addTags", "artist.removeTag", "artist.share", "artist.shout",
|
300 | "event.attend", "event.share", "event.shout",
|
301 | "library.addAlbum", "library.addArtist", "library.addTrack",
|
302 | "playlist.addTrack", "playlist.create",
|
303 | "radio.tune",
|
304 | "track.addTags", "track.ban", "track.love", "track.removeTag",
|
305 | "track.scrobble", "track.share", "track.unban", "track.unlove",
|
306 | "track.updateNowPlaying",
|
307 | "user.shout"]).each(function(method) {
|
308 | it(method + " is a write (post) request", function() {
|
309 | whenMethodIs(method);
|
310 | expectHttpMethod("POST");
|
311 | });
|
312 | });
|
313 |
|
314 | _(["auth.getMobileSession", "auth.getSession", "auth.getToken",
|
315 | "radio.getPlaylist"]).each(function(method) {
|
316 | it(method + " is signed", function() {
|
317 | whenMethodIs(method);
|
318 | expectSignature();
|
319 | });
|
320 | });
|
321 | })();
|