-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
allow for using multiple services with the same path
- Loading branch information
1 parent
997ba46
commit da8bcf6
Showing
9 changed files
with
308 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
package com.meta.cp4m; | ||
|
||
import io.javalin.Javalin; | ||
import io.javalin.http.HandlerType; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.concurrent.BlockingQueue; | ||
import java.util.concurrent.LinkedBlockingDeque; | ||
import java.util.concurrent.TimeUnit; | ||
import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
||
public class DummyWebServer implements AutoCloseable { | ||
private final Javalin javalin; | ||
private final BlockingQueue<ReceivedRequest> receivedRequests = new LinkedBlockingDeque<>(); | ||
|
||
private DummyWebServer() { | ||
this.javalin = | ||
Javalin.create() | ||
.addHandler( | ||
HandlerType.GET, | ||
"/<path>", | ||
ctx -> | ||
receivedRequests.put( | ||
new ReceivedRequest( | ||
ctx.path(), | ||
ctx.body(), | ||
ctx.contentType(), | ||
ctx.headerMap(), | ||
ctx.queryParamMap()))) | ||
.addHandler( | ||
HandlerType.POST, | ||
"/<path>", | ||
ctx -> | ||
receivedRequests.put( | ||
new ReceivedRequest( | ||
ctx.path(), | ||
ctx.body(), | ||
ctx.contentType(), | ||
ctx.headerMap(), | ||
ctx.queryParamMap()))) | ||
.start(0); | ||
} | ||
|
||
public static DummyWebServer create() { | ||
return new DummyWebServer(); | ||
} | ||
|
||
public @Nullable ReceivedRequest poll() { | ||
return receivedRequests.poll(); | ||
} | ||
|
||
public @Nullable ReceivedRequest poll(long milliseconds) throws InterruptedException { | ||
return receivedRequests.poll(milliseconds, TimeUnit.MILLISECONDS); | ||
} | ||
|
||
public ReceivedRequest take(long milliseconds) throws InterruptedException { | ||
return Objects.requireNonNull(receivedRequests.poll(milliseconds, TimeUnit.MILLISECONDS)); | ||
} | ||
|
||
public int port() { | ||
return javalin.port(); | ||
} | ||
|
||
public Javalin javalin() { | ||
return javalin; | ||
} | ||
|
||
@Override | ||
public void close() { | ||
javalin.close(); | ||
} | ||
|
||
public record ReceivedRequest( | ||
String path, | ||
String body, | ||
@Nullable String contentType, | ||
Map<String, String> headerMap, | ||
Map<String, java.util.List<String>> stringListMap) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
package com.meta.cp4m.message; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.meta.cp4m.Identifier; | ||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
import java.net.UnknownHostException; | ||
import java.util.function.Function; | ||
import org.apache.hc.client5.http.fluent.Request; | ||
import org.apache.hc.core5.http.ContentType; | ||
import org.apache.hc.core5.http.Method; | ||
import org.apache.hc.core5.net.URIBuilder; | ||
|
||
public final class HandlerTestUtils { | ||
|
||
private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
|
||
// static class, do not instantiate | ||
private HandlerTestUtils() {} | ||
|
||
public static Function<Identifier, URI> baseURLFactory(String path, int port) { | ||
return identifier -> { | ||
try { | ||
return URIBuilder.localhost().setPort(port).appendPath(path).setScheme("http").build(); | ||
} catch (UnknownHostException | URISyntaxException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}; | ||
} | ||
|
||
public static Function<JsonNode, Request> MessageRequestFactory( | ||
Method method, String path, String appSecret, int port) | ||
throws UnknownHostException, URISyntaxException { | ||
Request request = | ||
Request.create( | ||
method, | ||
URIBuilder.localhost().setScheme("http").appendPath(path).setPort(port).build()); | ||
return jn -> { | ||
try { | ||
String body = MAPPER.writeValueAsString(jn); | ||
return request | ||
.bodyString(body, ContentType.APPLICATION_JSON) | ||
.setHeader("X-Hub-Signature-256", "sha256=" + MetaHandlerUtils.hmac(body, appSecret)); | ||
} catch (JsonProcessingException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}; | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
src/test/java/com/meta/cp4m/message/MultiServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
package com.meta.cp4m.message; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.meta.cp4m.DummyWebServer; | ||
import com.meta.cp4m.DummyWebServer.ReceivedRequest; | ||
import com.meta.cp4m.Identifier; | ||
import com.meta.cp4m.Service; | ||
import com.meta.cp4m.ServicesRunner; | ||
import com.meta.cp4m.llm.DummyLLMPlugin; | ||
import com.meta.cp4m.store.MemoryStore; | ||
import com.meta.cp4m.store.MemoryStoreConfig; | ||
import java.net.URI; | ||
import java.util.List; | ||
import java.util.function.Function; | ||
import org.apache.hc.client5.http.fluent.Request; | ||
import org.apache.hc.core5.http.Method; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class MultiServiceTest { | ||
|
||
private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
|
||
private static final String META_PATH = "/meta"; | ||
private final DummyWebServer metaWebServer = DummyWebServer.create(); | ||
|
||
private final Function<Identifier, URI> baseURLFactory = | ||
HandlerTestUtils.baseURLFactory(META_PATH, metaWebServer.port()); | ||
|
||
@Test | ||
void waAndFBTest() throws Exception { | ||
final String path = "/path"; | ||
final String fb1VerifyToken = "fb1VerifyToken"; | ||
final String fb1AppSecret = "fb1AppSecret"; | ||
final String fb1PageAccessToken = "fb1PageAccessToken"; | ||
|
||
MemoryStore<FBMessage> fb1Store = MemoryStoreConfig.of(1, 1).toStore(); | ||
FBMessageHandler fb1Handler = | ||
FBMessengerConfig.of(fb1VerifyToken, fb1AppSecret, fb1PageAccessToken) | ||
.toMessageHandler() | ||
.baseURLFactory(baseURLFactory); | ||
DummyLLMPlugin<FBMessage> fb1Plugin = new DummyLLMPlugin<>("i'm a fb1 dummy"); | ||
Service<FBMessage> fb1Service = new Service<>(fb1Store, fb1Handler, fb1Plugin, path); | ||
|
||
final String fb2VerifyToken = "fb2VerifyToken"; | ||
final String fb2AppSecret = "fb2AppSecret"; | ||
final String fb2PageAccessToken = "fb2PageAccessToken"; | ||
|
||
MemoryStore<FBMessage> fb2Store = MemoryStoreConfig.of(1, 1).toStore(); | ||
FBMessageHandler fb2Handler = | ||
FBMessengerConfig.of(fb2VerifyToken, fb2AppSecret, fb2PageAccessToken) | ||
.toMessageHandler() | ||
.baseURLFactory(baseURLFactory); | ||
DummyLLMPlugin<FBMessage> fb2Plugin = new DummyLLMPlugin<>("i'm a fb2 dummy"); | ||
Service<FBMessage> fb2Service = new Service<>(fb2Store, fb2Handler, fb2Plugin, path); | ||
|
||
final String wa1VerifyToken = "wa1VerifyToken"; | ||
final String wa1AppSecret = "wa1AppSecret"; | ||
final String wa1PageAccessToken = "wa1PageAccessToken"; | ||
MemoryStore<WAMessage> wa1Store = MemoryStoreConfig.of(1, 1).toStore(); | ||
WAMessageHandler wa1Handler = | ||
WAMessengerConfig.of(wa1VerifyToken, wa1AppSecret, wa1PageAccessToken) | ||
.toMessageHandler() | ||
.baseUrlFactory(baseURLFactory); | ||
DummyLLMPlugin<WAMessage> wa1Plugin = new DummyLLMPlugin<>("i'm a wa1 dummy"); | ||
Service<WAMessage> wa1Service = new Service<>(wa1Store, wa1Handler, wa1Plugin, path); | ||
|
||
final String wa2VerifyToken = "wa2VerifyToken"; | ||
final String wa2AppSecret = "wa2AppSecret"; | ||
final String wa2PageAccessToken = "wa2PageAccessToken"; | ||
MemoryStore<WAMessage> wa2Store = MemoryStoreConfig.of(1, 1).toStore(); | ||
WAMessageHandler wa2Handler = | ||
WAMessengerConfig.of(wa2VerifyToken, wa2AppSecret, wa2PageAccessToken) | ||
.toMessageHandler() | ||
.baseUrlFactory(baseURLFactory); | ||
DummyLLMPlugin<WAMessage> wa2Plugin = new DummyLLMPlugin<>("i'm a wa2 dummy"); | ||
Service<WAMessage> wa2Service = new Service<>(wa2Store, wa2Handler, wa2Plugin, path); | ||
|
||
ServicesRunner runner = | ||
ServicesRunner.newInstance() | ||
.service(fb1Service) | ||
.service(fb2Service) | ||
.service(wa1Service) | ||
.service(wa2Service) | ||
.port(0) | ||
.start(); | ||
|
||
// FB1 test | ||
Function<JsonNode, Request> fb1RequestFactory = | ||
HandlerTestUtils.MessageRequestFactory(Method.POST, path, fb1AppSecret, runner.port()); | ||
fb1RequestFactory.apply(FBMessageHandlerTest.PARSED_SAMPLE_MESSAGE).execute(); | ||
fb1Plugin.take(500); | ||
ReceivedRequest receivedRequest = metaWebServer.take(500); | ||
assertThat(receivedRequest.path()).isEqualTo(META_PATH); | ||
assertThat(receivedRequest.body()).contains("i'm a fb1 dummy"); | ||
|
||
// FB2 test | ||
Function<JsonNode, Request> fb2RequestFactory = | ||
HandlerTestUtils.MessageRequestFactory(Method.POST, path, fb2AppSecret, runner.port()); | ||
fb2RequestFactory.apply(FBMessageHandlerTest.PARSED_SAMPLE_MESSAGE).execute(); | ||
fb2Plugin.take(500); | ||
receivedRequest = metaWebServer.take(500); | ||
assertThat(receivedRequest.path()).isEqualTo(META_PATH); | ||
assertThat(receivedRequest.body()).contains("i'm a fb2 dummy"); | ||
|
||
// WA1 test | ||
Function<JsonNode, Request> wa1RequestFactory = | ||
HandlerTestUtils.MessageRequestFactory(Method.POST, path, wa1AppSecret, runner.port()); | ||
wa1RequestFactory.apply(MAPPER.readTree(WAMessageHandlerTest.VALID)).execute(); | ||
wa1Plugin.take(500); | ||
receivedRequest = metaWebServer.take(500); | ||
ReceivedRequest receivedRequest2 = metaWebServer.take(500); | ||
assertThat(receivedRequest.path()).isEqualTo(META_PATH); | ||
assertThat(receivedRequest2.path()).isEqualTo(META_PATH); | ||
assertThat(List.of(receivedRequest, receivedRequest2)) | ||
.satisfiesOnlyOnce(r -> assertThat(r.body()).contains("i'm a wa1 dummy")); | ||
|
||
// WA2 test | ||
Function<JsonNode, Request> wa2RequestFactory = | ||
HandlerTestUtils.MessageRequestFactory(Method.POST, path, wa2AppSecret, runner.port()); | ||
wa2RequestFactory.apply(MAPPER.readTree(WAMessageHandlerTest.VALID)).execute(); | ||
wa2Plugin.take(500); | ||
receivedRequest = metaWebServer.take(500); | ||
receivedRequest2 = metaWebServer.take(500); | ||
assertThat(receivedRequest.path()).isEqualTo(META_PATH); | ||
assertThat(receivedRequest2.path()).isEqualTo(META_PATH); | ||
assertThat(List.of(receivedRequest, receivedRequest2)) | ||
.satisfiesOnlyOnce(r -> assertThat(r.body()).contains("i'm a wa2 dummy")); | ||
} | ||
} |
Oops, something went wrong.