1 | 'use strict';
|
2 | const lib = require('./rest'),
|
3 | url = 'http://domain.com/foo',
|
4 | url2 = 'https://domain.com/bar';
|
5 |
|
6 | describe('rest', () => {
|
7 | afterEach(() => {
|
8 | fetch.resetMocks();
|
9 | });
|
10 |
|
11 | it('catches on rejection', () => {
|
12 | fetch.mockRejectOnce(new Error('nope'));
|
13 | return lib.get(url).toPromise(Promise).catch((e) => {
|
14 | expect(e.message).toBe('nope');
|
15 | expect(e.details).toBe(url);
|
16 | });
|
17 | });
|
18 |
|
19 | it('catches on auth redirect', () => {
|
20 | fetch.mockResponseOnce('{}', { status: 401 });
|
21 | return lib.get(url).toPromise(Promise).catch((e) => {
|
22 | expect(e.message).toBe('Unauthorized');
|
23 | expect(e.details).toBe(url);
|
24 | });
|
25 | });
|
26 |
|
27 | it('catches on client errors', () => {
|
28 | fetch.mockResponseOnce('{}', { status: 400 });
|
29 | return lib.get(url).toPromise(Promise).catch((e) => {
|
30 | expect(e.message).toBe('Bad Request');
|
31 | expect(e.details).toBe(url);
|
32 | });
|
33 | });
|
34 |
|
35 | it('catches on server errors', () => {
|
36 | fetch.mockResponseOnce('{}', { status: 500 });
|
37 | return lib.get(url).toPromise(Promise).catch((e) => {
|
38 | expect(e.message).toBe('Internal Server Error');
|
39 | expect(e.details).toBe(url);
|
40 | });
|
41 | });
|
42 |
|
43 | describe('get', () => {
|
44 | it('gets json', () => {
|
45 | fetch.mockResponseOnce(JSON.stringify({ a: 'b' }));
|
46 | return lib.get(url).toPromise(Promise).then((res) => {
|
47 | expect(res).toEqual({ a: 'b' });
|
48 | });
|
49 | });
|
50 |
|
51 | it('gets text', () => {
|
52 | fetch.mockResponseOnce('hi');
|
53 | return lib.get(url, { type: 'text' }).toPromise(Promise).then((res) => {
|
54 | expect(res).toBe('hi');
|
55 | });
|
56 | });
|
57 |
|
58 | it('uses https agent for ssl calls', () => {
|
59 | fetch.mockResponseOnce(JSON.stringify({ a: 'b' }));
|
60 | return lib.get(url2).toPromise(Promise).then(() => {
|
61 | expect(fetch.mock.calls[0][1].agent).not.toBeNull();
|
62 | });
|
63 | });
|
64 |
|
65 | it('uses headers', () => {
|
66 | fetch.mockResponseOnce(JSON.stringify({ a: 'b' }));
|
67 | return lib.get(url, { headers: { Authorization: 'Token abc' }}).toPromise(Promise).then(() => {
|
68 | expect(fetch.mock.calls[0][1].headers).toEqual({ Authorization: 'Token abc' });
|
69 | });
|
70 | });
|
71 | });
|
72 |
|
73 | describe('put', () => {
|
74 | const key = 'some api key',
|
75 | json = { a: 'b' },
|
76 | jsonString = JSON.stringify(json);
|
77 |
|
78 | it('throws error if no api key specified', () => {
|
79 | expect(() => lib.put(url, json)).toThrow('Please specify API key to do PUT requests against Clay!');
|
80 | });
|
81 |
|
82 | it('sends json', () => {
|
83 | fetch.mockResponseOnce(json);
|
84 | return lib.put(url, json, { key }).toPromise(Promise).then((res) => {
|
85 | expect(res).toEqual({ type: 'success', message: url });
|
86 | expect(fetch).toHaveBeenCalledWith(url, {
|
87 | method: 'PUT',
|
88 | agent: null,
|
89 | body: jsonString,
|
90 | headers: {
|
91 | Authorization: `Token ${key}`,
|
92 | 'Content-Type': 'application/json; charset=UTF-8'
|
93 | }
|
94 | });
|
95 | });
|
96 | });
|
97 |
|
98 | it('sends json with empty body', () => {
|
99 | fetch.mockResponseOnce(json);
|
100 | return lib.put(url, null, { key }).toPromise(Promise).then((res) => {
|
101 | expect(res).toEqual({ type: 'success', message: url });
|
102 | expect(fetch).toHaveBeenCalledWith(url, {
|
103 | method: 'PUT',
|
104 | agent: null,
|
105 | body: undefined,
|
106 | headers: {
|
107 | Authorization: `Token ${key}`,
|
108 | 'Content-Type': 'application/json; charset=UTF-8'
|
109 | }
|
110 | });
|
111 | });
|
112 | });
|
113 |
|
114 | it('sends text', () => {
|
115 | fetch.mockResponseOnce('hi');
|
116 | return lib.put(url, 'hi', { type: 'text', key }).toPromise(Promise).then((res) => {
|
117 | expect(res).toEqual({ type: 'success', message: url });
|
118 | expect(fetch).toHaveBeenCalledWith(url, {
|
119 | method: 'PUT',
|
120 | agent: null,
|
121 | body: 'hi',
|
122 | headers: {
|
123 | Authorization: `Token ${key}`,
|
124 | 'Content-Type': 'text/plain; charset=UTF-8'
|
125 | }
|
126 | });
|
127 | });
|
128 | });
|
129 |
|
130 | it('uses https agent for ssl calls', () => {
|
131 | fetch.mockResponseOnce(json);
|
132 | return lib.put(url2, json, { key }).toPromise(Promise).then(() => {
|
133 | expect(fetch.mock.calls[0][1].agent).not.toBeNull();
|
134 | });
|
135 | });
|
136 |
|
137 | it('uses headers', () => {
|
138 | fetch.mockResponseOnce(json);
|
139 | return lib.put(url, json, { key, headers: { some_header: 'value' }}).toPromise(Promise).then(() => {
|
140 | expect(fetch.mock.calls[0][1].headers).toEqual({
|
141 | Authorization: `Token ${key}`,
|
142 | 'Content-Type': 'application/json; charset=UTF-8',
|
143 | some_header: 'value'
|
144 | });
|
145 | });
|
146 | });
|
147 |
|
148 | it('returns stream of errors if PUT fails', () => {
|
149 | fetch.mockResponseOnce('{}', { status: 500 });
|
150 | return lib.put(url, json, { key }).toPromise(Promise).then((res) => {
|
151 | expect(res).toEqual({ type: 'error', details: url, message: 'Internal Server Error' });
|
152 | });
|
153 | });
|
154 | });
|
155 |
|
156 | describe('query', () => {
|
157 | const key = 'some api key',
|
158 | json = {
|
159 | index: 'pages',
|
160 | body: {
|
161 | query: {
|
162 | match_all: {}
|
163 | }
|
164 | }
|
165 | },
|
166 | jsonString = JSON.stringify(json),
|
167 | results = {
|
168 | hits: {
|
169 | total: 1,
|
170 | hits: [{
|
171 | _id: 'foo',
|
172 | _source: {
|
173 | uri: 'foo'
|
174 | }
|
175 | }]
|
176 | }
|
177 | },
|
178 | resultsString = JSON.stringify(results),
|
179 | noResults = {
|
180 | hits: {
|
181 | total: 0,
|
182 | hits: []
|
183 | }
|
184 | },
|
185 | noResultsString = JSON.stringify(noResults);
|
186 |
|
187 | it('throws error if no api key specified', () => {
|
188 | expect(() => lib.query(url, json)).toThrow('Please specify API key to do POST requests against Clay!');
|
189 | });
|
190 |
|
191 | it('returns error if elastic errors', () => {
|
192 | fetch.mockResponseOnce('[parsing_exception] [prefix] malformed query, expected [END_OBJECT] but found [FIELD_NAME], with { line=1 & col=50 } :: {"path":"/pages/_doc/_search","query"...', { headers: { 'content-type': 'text/html; charset=utf-8' }});
|
193 | return lib.query(url, json, { key }).toPromise(Promise).then((res) => {
|
194 | expect(res).toEqual({ type: 'error', details: url, message: '[parsing_exception] [prefix] malformed query, expected [END_OBJECT] but found [FIELD_NAME], with { line=1 & col=50 }', url });
|
195 | });
|
196 | });
|
197 |
|
198 | it('fetches results', () => {
|
199 | fetch.mockResponseOnce(resultsString);
|
200 | return lib.query(url, json, { key }).toPromise(Promise).then((res) => {
|
201 | expect(res).toEqual({ type: 'success', details: url, message: '1 result', data: [{ _id: 'foo', uri: 'foo' }], total: 1 });
|
202 | expect(fetch).toHaveBeenCalledWith(url, {
|
203 | method: 'POST',
|
204 | agent: null,
|
205 | body: jsonString,
|
206 | headers: {
|
207 | Authorization: `Token ${key}`,
|
208 | 'Content-Type': 'application/json; charset=UTF-8'
|
209 | }
|
210 | });
|
211 | });
|
212 | });
|
213 |
|
214 | it('fetches zero results', () => {
|
215 | fetch.mockResponseOnce(noResultsString);
|
216 | return lib.query(url, json, { key }).toPromise(Promise).then((res) => {
|
217 | expect(res).toEqual({ type: 'error', details: url, message: 'No results', url });
|
218 | expect(fetch).toHaveBeenCalledWith(url, {
|
219 | method: 'POST',
|
220 | agent: null,
|
221 | body: jsonString,
|
222 | headers: {
|
223 | Authorization: `Token ${key}`,
|
224 | 'Content-Type': 'application/json; charset=UTF-8'
|
225 | }
|
226 | });
|
227 | });
|
228 | });
|
229 |
|
230 | it('uses https agent for ssl calls', () => {
|
231 | fetch.mockResponseOnce(resultsString);
|
232 | return lib.query(url2, json, { key }).toPromise(Promise).then(() => {
|
233 | expect(fetch.mock.calls[0][1].agent).not.toBeNull();
|
234 | });
|
235 | });
|
236 |
|
237 | it('uses headers', () => {
|
238 | fetch.mockResponseOnce(resultsString);
|
239 | return lib.query(url, json, { key, headers: { some_header: 'value' }}).toPromise(Promise).then(() => {
|
240 | expect(fetch.mock.calls[0][1].headers).toEqual({
|
241 | Authorization: `Token ${key}`,
|
242 | 'Content-Type': 'application/json; charset=UTF-8',
|
243 | some_header: 'value'
|
244 | });
|
245 | });
|
246 | });
|
247 |
|
248 | it('returns stream of errors if POST fails', () => {
|
249 | fetch.mockResponseOnce('{}', { status: 500 });
|
250 | return lib.query(url, json, { key }).toPromise(Promise).then((res) => {
|
251 | expect(res).toEqual({ type: 'error', details: url, message: 'Internal Server Error' });
|
252 | });
|
253 | });
|
254 | });
|
255 |
|
256 | describe('findURI', () => {
|
257 | it('finds page uri with one hop', () => {
|
258 | fetch.mockResponseOnce('domain.com/_pages/foo');
|
259 | return lib.findURI('http://domain.com/some-slug.html').toPromise(Promise).then((res) => {
|
260 | expect(res).toEqual({ uri: 'domain.com/_pages/foo', prefix: 'http://domain.com'});
|
261 | });
|
262 | });
|
263 |
|
264 | it('works with ssl', () => {
|
265 | fetch.mockResponseOnce('domain.com/_pages/foo');
|
266 | return lib.findURI('https://domain.com/some-slug.html').toPromise(Promise).then((res) => {
|
267 | expect(res).toEqual({ uri: 'domain.com/_pages/foo', prefix: 'https://domain.com' });
|
268 | });
|
269 | });
|
270 |
|
271 | it('finds page uri with two hops', () => {
|
272 | fetch.mockResponseOnce('', { status: 404 });
|
273 | fetch.mockResponseOnce('domain.com/_pages/foo');
|
274 | return lib.findURI('http://domain.com/path/some-slug.html').toPromise(Promise).then((res) => {
|
275 | expect(res).toEqual({ uri: 'domain.com/_pages/foo', prefix: 'http://domain.com' });
|
276 | });
|
277 | });
|
278 |
|
279 | it('fails if no relevant api route', () => {
|
280 | fetch.mockResponse('', { status: 404 });
|
281 | return lib.findURI('http://domain.com/some-slug.html').toPromise(Promise).catch((e) => {
|
282 | expect(e.message).toBe('Unable to find a Clay api for domain.com/some-slug.html');
|
283 | });
|
284 | });
|
285 | });
|
286 |
|
287 | describe('isElasticPrefix', () => {
|
288 | it('returns true if _search endpoint exists at prefix', () => {
|
289 | fetch.mockResponseOnce('{}');
|
290 | return lib.isElasticPrefix('http://domain.com').toPromise(Promise).then((res) => {
|
291 | expect(res).toBe(true);
|
292 | });
|
293 | });
|
294 |
|
295 | it('returns false if _search endpoint does not exist at prefix', () => {
|
296 | fetch.mockResponseOnce('{}', { status: 404 });
|
297 | return lib.isElasticPrefix('http://domain.com').toPromise(Promise).then((res) => {
|
298 | expect(res).toBe(false);
|
299 | });
|
300 | });
|
301 |
|
302 | it('works for ssl', () => {
|
303 | fetch.mockResponseOnce('{}');
|
304 | return lib.isElasticPrefix('https://domain.com').toPromise(Promise).then(() => {
|
305 | expect(fetch.mock.calls[0][1].agent).not.toBeNull();
|
306 | });
|
307 | });
|
308 | });
|
309 | });
|