Skip to content

Commit a3453bf

Browse files
Remove PowerMock from the Slack GCF sample. (#3186)
* Remove PowerMock from the Slack GCF sample. Instead of using Whitebox, use package-private constructor overloads to supply the values needed by the test. * fix lint * Fix the tests Co-authored-by: Éamonn McManus <[email protected]>
1 parent 5d3d1dc commit a3453bf

File tree

3 files changed

+38
-69
lines changed

3 files changed

+38
-69
lines changed

functions/slack/pom.xml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
</parent>
3232

3333
<properties>
34-
<powermock.version>2.0.7</powermock.version>
3534
<maven.compiler.target>11</maven.compiler.target>
3635
<maven.compiler.source>11</maven.compiler.source>
3736
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -101,24 +100,6 @@
101100
<version>29.0-jre</version>
102101
<scope>test</scope>
103102
</dependency>
104-
<dependency>
105-
<groupId>org.powermock</groupId>
106-
<artifactId>powermock-core</artifactId>
107-
<version>${powermock.version}</version>
108-
<scope>test</scope>
109-
</dependency>
110-
<dependency>
111-
<groupId>org.powermock</groupId>
112-
<artifactId>powermock-module-junit4</artifactId>
113-
<version>${powermock.version}</version>
114-
<scope>test</scope>
115-
</dependency>
116-
<dependency>
117-
<groupId>org.powermock</groupId>
118-
<artifactId>powermock-api-mockito2</artifactId>
119-
<version>${powermock.version}</version>
120-
<scope>test</scope>
121-
</dependency>
122103
</dependencies>
123104

124105
<!-- Disable tests during GCF builds (from parent POM) -->

functions/slack/src/main/java/functions/SlackSlashCommand.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
import java.io.IOException;
3131
import java.net.HttpURLConnection;
3232
import java.security.GeneralSecurityException;
33-
import java.util.HashMap;
34-
import java.util.List;
33+
import java.util.Optional;
3534
import java.util.logging.Logger;
3635
import java.util.stream.Collectors;
3736

@@ -43,14 +42,24 @@ public class SlackSlashCommand implements HttpFunction {
4342
private static final String SLACK_SECRET = getenv("SLACK_SECRET");
4443
private static final Gson gson = new Gson();
4544

46-
private Kgsearch kgClient;
47-
private SlackSignature.Verifier verifier;
45+
private final String apiKey;
46+
private final Kgsearch kgClient;
47+
private final SlackSignature.Verifier verifier;
4848

4949
public SlackSlashCommand() throws IOException, GeneralSecurityException {
50-
kgClient = new Kgsearch.Builder(
51-
GoogleNetHttpTransport.newTrustedTransport(), new JacksonFactory(), null).build();
50+
this(new SlackSignature.Verifier(new SlackSignature.Generator(SLACK_SECRET)));
51+
}
52+
53+
SlackSlashCommand(SlackSignature.Verifier verifier) throws IOException, GeneralSecurityException {
54+
this(verifier, API_KEY);
55+
}
5256

53-
verifier = new SlackSignature.Verifier(new SlackSignature.Generator(SLACK_SECRET));
57+
SlackSlashCommand(SlackSignature.Verifier verifier, String apiKey)
58+
throws IOException, GeneralSecurityException {
59+
this.verifier = verifier;
60+
this.apiKey = apiKey;
61+
this.kgClient = new Kgsearch.Builder(
62+
GoogleNetHttpTransport.newTrustedTransport(), new JacksonFactory(), null).build();
5463
}
5564

5665
// Avoid ungraceful deployment failures due to unset environment variables.
@@ -73,18 +82,13 @@ private static String getenv(String name) {
7382
* @return true if the provided request came from Slack, false otherwise
7483
*/
7584
boolean isValidSlackWebhook(HttpRequest request, String requestBody) {
76-
7785
// Check for headers
78-
HashMap<String, List<String>> headers = new HashMap(request.getHeaders());
79-
if (!headers.containsKey("X-Slack-Request-Timestamp")
80-
|| !headers.containsKey("X-Slack-Signature")) {
86+
Optional<String> maybeTimestamp = request.getFirstHeader("X-Slack-Request-Timestamp");
87+
Optional<String> maybeSignature = request.getFirstHeader("X-Slack-Signature");
88+
if (!maybeTimestamp.isPresent() || !maybeSignature.isPresent()) {
8189
return false;
8290
}
83-
return verifier.isValid(
84-
headers.get("X-Slack-Request-Timestamp").get(0),
85-
requestBody,
86-
headers.get("X-Slack-Signature").get(0),
87-
1L);
91+
return verifier.isValid(maybeTimestamp.get(), requestBody, maybeSignature.get(), 1L);
8892
}
8993
// [END functions_verify_webhook]
9094

@@ -158,7 +162,7 @@ String formatSlackMessage(JsonObject kgResponse, String query) {
158162
JsonObject searchKnowledgeGraph(String query) throws IOException {
159163
Kgsearch.Entities.Search kgRequest = kgClient.entities().search();
160164
kgRequest.setQuery(query);
161-
kgRequest.setKey(API_KEY);
165+
kgRequest.setKey(apiKey);
162166

163167
return gson.fromJson(kgRequest.execute().toString(), JsonObject.class);
164168
}

functions/slack/src/test/java/functions/SlackSlashCommandTest.java

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
package functions;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static org.junit.Assert.assertThrows;
21+
import static org.mockito.Mockito.any;
22+
import static org.mockito.Mockito.anyLong;
2023
import static org.mockito.Mockito.times;
2124
import static org.mockito.Mockito.verify;
22-
import static org.powermock.api.mockito.PowerMockito.mock;
23-
import static org.powermock.api.mockito.PowerMockito.when;
25+
import static org.mockito.Mockito.when;
2426

2527
import com.github.seratch.jslack.app_backend.SlackSignature;
2628
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
@@ -34,15 +36,12 @@
3436
import java.io.StringWriter;
3537
import java.net.HttpURLConnection;
3638
import java.security.GeneralSecurityException;
37-
import java.util.Arrays;
38-
import java.util.HashMap;
3939
import java.util.List;
4040
import java.util.Map;
4141
import org.junit.Before;
4242
import org.junit.Test;
43-
import org.mockito.ArgumentMatchers;
4443
import org.mockito.Mock;
45-
import org.powermock.reflect.Whitebox;
44+
import org.mockito.MockitoAnnotations;
4645

4746
public class SlackSlashCommandTest {
4847

@@ -58,37 +57,27 @@ public class SlackSlashCommandTest {
5857

5958
@Before
6059
public void beforeTest() throws IOException {
61-
request = mock(HttpRequest.class);
62-
when(request.getReader()).thenReturn(new BufferedReader(new StringReader("")));
60+
MockitoAnnotations.initMocks(this);
6361

64-
response = mock(HttpResponse.class);
62+
when(request.getReader()).thenReturn(new BufferedReader(new StringReader("")));
6563

6664
responseOut = new StringWriter();
6765

6866
writerOut = new BufferedWriter(responseOut);
6967
when(response.getWriter()).thenReturn(writerOut);
7068

71-
alwaysValidVerifier = mock(SlackSignature.Verifier.class);
72-
when(alwaysValidVerifier.isValid(
73-
ArgumentMatchers.any(),
74-
ArgumentMatchers.any(),
75-
ArgumentMatchers.any(),
76-
ArgumentMatchers.anyLong())
77-
).thenReturn(true);
69+
when(alwaysValidVerifier.isValid(any(), any(), any(), anyLong())).thenReturn(true);
7870

7971
// Construct valid header list
8072
String validSlackSignature = System.getenv("SLACK_TEST_SIGNATURE");
8173
String timestamp = "0"; // start of Unix epoch
8274

8375
Map<String, List<String>> validHeaders = Map.of(
84-
"X-Slack-Signature", Arrays.asList(validSlackSignature),
85-
"X-Slack-Request-Timestamp", Arrays.asList(timestamp)
86-
);
76+
"X-Slack-Signature", List.of(validSlackSignature),
77+
"X-Slack-Request-Timestamp", List.of(timestamp));
8778

8879
when(request.getHeaders()).thenReturn(validHeaders);
89-
90-
// Reset knowledge graph API key
91-
Whitebox.setInternalState(SlackSlashCommand.class, "API_KEY", System.getenv("KG_API_KEY"));
80+
when(request.getFirstHeader(any())).thenCallRealMethod();
9281
}
9382

9483
@Test
@@ -126,20 +115,19 @@ public void recognizesValidSlackTokenTest() throws IOException, GeneralSecurityE
126115
verify(response, times(1)).setStatusCode(HttpURLConnection.HTTP_BAD_REQUEST);
127116
}
128117

129-
@Test(expected = GoogleJsonResponseException.class)
118+
@Test
130119
public void handlesSearchErrorTest() throws IOException, GeneralSecurityException {
131120
String jsonStr = gson.toJson(Map.of("text", "foo"));
132121
StringReader requestReadable = new StringReader(jsonStr);
133122

134123
when(request.getReader()).thenReturn(new BufferedReader(requestReadable));
135124
when(request.getMethod()).thenReturn("POST");
136125

137-
SlackSlashCommand functionInstance = new SlackSlashCommand();
138-
Whitebox.setInternalState(functionInstance, "verifier", alwaysValidVerifier);
139-
Whitebox.setInternalState(SlackSlashCommand.class, "API_KEY", "gibberish");
126+
SlackSlashCommand functionInstance = new SlackSlashCommand(alwaysValidVerifier, "gibberish");
140127

141128
// Should throw a GoogleJsonResponseException (due to invalid API key)
142-
functionInstance.service(request, response);
129+
assertThrows(
130+
GoogleJsonResponseException.class, () -> functionInstance.service(request, response));
143131
}
144132

145133
@Test
@@ -150,9 +138,7 @@ public void handlesEmptyKgResultsTest() throws IOException, GeneralSecurityExcep
150138
when(request.getReader()).thenReturn(new BufferedReader(requestReadable));
151139
when(request.getMethod()).thenReturn("POST");
152140

153-
SlackSlashCommand functionInstance = new SlackSlashCommand();
154-
Whitebox.setInternalState(functionInstance, "verifier", alwaysValidVerifier);
155-
141+
SlackSlashCommand functionInstance = new SlackSlashCommand(alwaysValidVerifier);
156142

157143
functionInstance.service(request, response);
158144

@@ -168,9 +154,7 @@ public void handlesPopulatedKgResultsTest() throws IOException, GeneralSecurityE
168154
when(request.getReader()).thenReturn(new BufferedReader(requestReadable));
169155
when(request.getMethod()).thenReturn("POST");
170156

171-
SlackSlashCommand functionInstance = new SlackSlashCommand();
172-
Whitebox.setInternalState(functionInstance, "verifier", alwaysValidVerifier);
173-
157+
SlackSlashCommand functionInstance = new SlackSlashCommand(alwaysValidVerifier);
174158

175159
functionInstance.service(request, response);
176160

0 commit comments

Comments
 (0)