Skip to content

Commit 82f5a34

Browse files
committed
Add tests for Slack sample.
1 parent 9fc09e4 commit 82f5a34

File tree

3 files changed

+260
-53
lines changed

3 files changed

+260
-53
lines changed

functions/slack/README.md

+1-44
Original file line numberDiff line numberDiff line change
@@ -11,47 +11,4 @@ View the [source code][code].
1111

1212
## Deploy and Test
1313

14-
1. Follow the [Cloud Functions quickstart guide][quickstart] to setup Cloud
15-
Functions for your project.
16-
17-
1. Clone this repository:
18-
19-
git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
20-
cd nodejs-docs-samples/functions/sendgrid
21-
22-
1. Create or join a Slack community.
23-
24-
See [Creating a Slack community](TODO).
25-
26-
1. Create a Slash Command:
27-
28-
See [Creating a Slash Command](TODO).
29-
30-
1. Create a Cloud Storage bucket to stage your Cloud Functions files, where
31-
`[YOUR_STAGING_BUCKET_NAME]` is a globally-unique bucket name:
32-
33-
gsutil mb gs://[YOUR_STAGING_BUCKET_NAME]
34-
35-
1. Deploy the `kgSearch` function with an HTTP trigger, where
36-
`[YOUR_STAGING_BUCKET_NAME]` is the name of your staging bucket:
37-
38-
gcloud alpha functions deploy kgSearch --bucket [YOUR_STAGING_BUCKET_NAME] --trigger-http
39-
40-
1. Call the `kgSearch` function by making an HTTP request:
41-
42-
curl -X POST "https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/kgSearch" --data '{"token":"[YOUR_SLACK_TOKEN]","text":"giraffe"}'
43-
44-
* Replace `[YOUR_REGION]` with the region where you function is deployed. This is visible in your terminal when your function finishes deploying.
45-
* Replace `[YOUR_PROJECT_ID]` with your Cloud project ID. This is visible in your terminal when your function finishes deploying.
46-
* `[YOUR_SLACK_TOKEN]` is the token provided by Slack in the configured Slash Command.
47-
48-
1. Check the logs for the `subscribe` function:
49-
50-
gcloud alpha functions get-logs kgSearch
51-
52-
You should see something like this in your console:
53-
54-
D ... User function triggered, starting execution
55-
D ... Execution took 1 ms, user function completed successfully
56-
57-
[quickstart]: https://cloud.google.com/functions/quickstart
14+
Read the tutorial at https://cloud.google.com/functions/docs/tutorials/slack

functions/slack/index.js

+21-9
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,28 @@ function formatSlackMessage (query, response) {
4646
attachments: []
4747
};
4848

49-
console.log(JSON.stringify(entity, null, 2));
50-
5149
if (entity) {
52-
slackMessage.attachments.push({
53-
color: '#3367d6',
54-
title: entity.name + ': ' + entity.description,
55-
title_link: entity.detailedDescription.url,
56-
text: entity.detailedDescription.articleBody,
57-
image_url: entity.image ? entity.image.contentUrl : undefined
58-
});
50+
var attachment = {
51+
color: '#3367d6'
52+
};
53+
if (entity.name) {
54+
attachment.title = entity.name;
55+
if (entity.description) {
56+
attachment.title = attachment.title + ': ' + entity.description;
57+
}
58+
}
59+
if (entity.detailedDescription) {
60+
if (entity.detailedDescription.url) {
61+
attachment.title_link = entity.detailedDescription.url;
62+
}
63+
if (entity.detailedDescription.articleBody) {
64+
attachment.text = entity.detailedDescription.articleBody;
65+
}
66+
}
67+
if (entity.image && entity.image.contentUrl) {
68+
attachment.image_url = entity.image.contentUrl;
69+
}
70+
slackMessage.attachments.push(attachment);
5971
} else {
6072
slackMessage.attachments.push({
6173
text: 'No results match your query...'

test/functions/slack.test.js

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// Copyright 2016, Google, Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
'use strict';
15+
16+
var proxyquire = require('proxyquire').noCallThru();
17+
var util = require('util');
18+
var EventEmitter = require('events');
19+
20+
var method = 'POST';
21+
var query = 'giraffe';
22+
var SLACK_TOKEN = 'slack-token';
23+
var KG_API_KEY = 'kg-api-key';
24+
25+
function getSample () {
26+
var config = {
27+
SLACK_TOKEN: SLACK_TOKEN,
28+
KG_API_KEY: KG_API_KEY
29+
};
30+
var kgsearch = {
31+
entities: {
32+
search: sinon.stub().callsArg(1)
33+
}
34+
};
35+
var googleapis = {
36+
kgsearch: sinon.stub().returns(kgsearch)
37+
};
38+
return {
39+
sample: proxyquire('../../functions/slack', {
40+
googleapis: googleapis,
41+
'./config.json': config
42+
}),
43+
mocks: {
44+
googleapis: googleapis,
45+
kgsearch: kgsearch,
46+
config: config
47+
}
48+
};
49+
}
50+
51+
function getMocks () {
52+
var req = {
53+
headers: {},
54+
query: {},
55+
body: {},
56+
get: function (header) {
57+
return this.headers[header];
58+
}
59+
};
60+
sinon.spy(req, 'get');
61+
var res = {
62+
headers: {},
63+
send: sinon.stub().returnsThis(),
64+
json: sinon.stub().returnsThis(),
65+
end: sinon.stub().returnsThis(),
66+
status: function (statusCode) {
67+
this.statusCode = statusCode;
68+
return this;
69+
},
70+
set: function (header, value) {
71+
this.headers[header] = value;
72+
return this;
73+
}
74+
};
75+
sinon.spy(res, 'status');
76+
sinon.spy(res, 'set');
77+
return {
78+
req: req,
79+
res: res
80+
};
81+
}
82+
83+
function getMockContext () {
84+
return {
85+
done: sinon.stub(),
86+
success: sinon.stub(),
87+
failure: sinon.stub()
88+
};
89+
}
90+
91+
describe('functions:slack', function () {
92+
it('Send fails if not a POST request', function () {
93+
var expectedMsg = 'Only POST requests are accepted';
94+
var mocks = getMocks();
95+
96+
getSample().sample.kgSearch(mocks.req, mocks.res);
97+
98+
assert.equal(mocks.res.status.calledOnce, true);
99+
assert.equal(mocks.res.status.firstCall.args[0], 405);
100+
assert.equal(mocks.res.send.calledOnce, true);
101+
assert.equal(mocks.res.send.firstCall.args[0], expectedMsg);
102+
assert(console.error.called);
103+
});
104+
105+
it('Throws if invalid slack token', function () {
106+
var expectedMsg = 'Invalid credentials';
107+
var mocks = getMocks();
108+
109+
mocks.req.method = method;
110+
mocks.req.body.token = 'wrong';
111+
var slackSample = getSample();
112+
slackSample.sample.kgSearch(mocks.req, mocks.res);
113+
114+
assert.equal(mocks.res.status.calledOnce, true);
115+
assert.equal(mocks.res.status.firstCall.args[0], 401);
116+
assert.equal(mocks.res.send.calledOnce, true);
117+
assert.equal(mocks.res.send.firstCall.args[0], expectedMsg);
118+
assert(console.error.called);
119+
});
120+
121+
it('Handles search error', function () {
122+
var mocks = getMocks();
123+
124+
mocks.req.method = method;
125+
mocks.req.body.token = SLACK_TOKEN;
126+
mocks.req.body.text = query;
127+
var slackSample = getSample();
128+
slackSample.mocks.kgsearch.entities.search = sinon.stub().callsArgWith(1, 'error');
129+
slackSample.sample.kgSearch(mocks.req, mocks.res);
130+
131+
assert.equal(mocks.res.status.calledOnce, true);
132+
assert.equal(mocks.res.status.firstCall.args[0], 500);
133+
assert.equal(mocks.res.send.called, false);
134+
assert(console.error.calledWith('error'));
135+
});
136+
137+
it('Makes search request, receives empty results', function () {
138+
var mocks = getMocks();
139+
140+
mocks.req.method = method;
141+
mocks.req.body.token = SLACK_TOKEN;
142+
mocks.req.body.text = query;
143+
var slackSample = getSample();
144+
slackSample.mocks.kgsearch.entities.search = sinon.stub().callsArgWith(1, null, {
145+
itemListElement: []
146+
});
147+
slackSample.sample.kgSearch(mocks.req, mocks.res);
148+
149+
assert.equal(mocks.res.status.called, false);
150+
assert.equal(mocks.res.json.called, true);
151+
assert.deepEqual(mocks.res.json.firstCall.args[0], {
152+
text: 'Query: ' + query,
153+
response_type: 'in_channel',
154+
attachments: [
155+
{
156+
text: 'No results match your query...'
157+
}
158+
]
159+
});
160+
});
161+
162+
it('Makes search request, receives non-empty results', function () {
163+
var mocks = getMocks();
164+
165+
mocks.req.method = method;
166+
mocks.req.body.token = SLACK_TOKEN;
167+
mocks.req.body.text = query;
168+
var slackSample = getSample();
169+
slackSample.mocks.kgsearch.entities.search = sinon.stub().callsArgWith(1, null, {
170+
itemListElement: [
171+
{
172+
result: {
173+
name: 'Giraffe',
174+
description: 'Animal',
175+
detailedDescription: {
176+
url: 'http://domain.com/giraffe',
177+
articleBody: 'giraffe is a tall animal'
178+
},
179+
image: {
180+
contentUrl: 'http://domain.com/image.jpg'
181+
}
182+
}
183+
}
184+
]
185+
});
186+
slackSample.sample.kgSearch(mocks.req, mocks.res);
187+
188+
assert.equal(mocks.res.status.called, false);
189+
assert.equal(mocks.res.json.called, true);
190+
assert.deepEqual(mocks.res.json.firstCall.args[0], {
191+
text: 'Query: ' + query,
192+
response_type: 'in_channel',
193+
attachments: [
194+
{
195+
color: '#3367d6',
196+
title: 'Giraffe: Animal',
197+
title_link: 'http://domain.com/giraffe',
198+
text: 'giraffe is a tall animal',
199+
image_url: 'http://domain.com/image.jpg'
200+
}
201+
]
202+
});
203+
});
204+
205+
it('Makes search request, receives non-empty results but partial data', function () {
206+
var mocks = getMocks();
207+
208+
mocks.req.method = method;
209+
mocks.req.body.token = SLACK_TOKEN;
210+
mocks.req.body.text = query;
211+
var slackSample = getSample();
212+
slackSample.mocks.kgsearch.entities.search = sinon.stub().callsArgWith(1, null, {
213+
itemListElement: [
214+
{
215+
result: {
216+
name: 'Giraffe',
217+
detailedDescription: {},
218+
image: {}
219+
}
220+
}
221+
]
222+
});
223+
slackSample.sample.kgSearch(mocks.req, mocks.res);
224+
225+
assert.equal(mocks.res.status.called, false);
226+
assert.equal(mocks.res.json.called, true);
227+
assert.deepEqual(mocks.res.json.firstCall.args[0], {
228+
text: 'Query: ' + query,
229+
response_type: 'in_channel',
230+
attachments: [
231+
{
232+
color: '#3367d6',
233+
title: 'Giraffe'
234+
}
235+
]
236+
});
237+
});
238+
});

0 commit comments

Comments
 (0)