-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix request context leak in the Funqy Knative runtime.
- Loading branch information
1 parent
8e620fb
commit af89f4f
Showing
5 changed files
with
278 additions
and
17 deletions.
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
...y-knative-events/deployment/src/test/java/io/quarkus/funqy/test/RequestScopeLeakTest.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,151 @@ | ||
package io.quarkus.funqy.test; | ||
|
||
import static org.hamcrest.Matchers.equalTo; | ||
import static org.hamcrest.Matchers.nullValue; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import javax.annotation.PreDestroy; | ||
import javax.enterprise.context.RequestScoped; | ||
import javax.inject.Inject; | ||
|
||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.funqy.Funq; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
import io.smallrye.common.vertx.VertxContext; | ||
import io.smallrye.mutiny.Uni; | ||
import io.vertx.core.Context; | ||
import io.vertx.core.Vertx; | ||
|
||
public class RequestScopeLeakTest { | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest() | ||
.withApplicationRoot((jar) -> jar | ||
.addClasses(MyBean.class, Identity.class, Greeting.class, MyFunction.class) | ||
.addAsResource("greeting-uni.properties", "application.properties")); | ||
|
||
@BeforeEach | ||
void cleanup() { | ||
MyBean.DISPOSED.set(0); | ||
} | ||
|
||
@Test | ||
public void testRequestScope() { | ||
RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"Roxanne\"}") | ||
.post("/") | ||
.then().statusCode(200) | ||
.header("ce-id", nullValue()) | ||
.body("name", equalTo("Roxanne")) | ||
.body("message", equalTo("Hello Roxanne!")); | ||
|
||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@Test | ||
public void testRequestScopeWithSyncFailure() { | ||
RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"sync-failure\"}") | ||
.post("/") | ||
.then().statusCode(500); | ||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@Test | ||
public void testRequestScopeWithSyncFailureInPipeline() { | ||
RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"sync-failure-pipeline\"}") | ||
.post("/") | ||
.then().statusCode(500); | ||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@Test | ||
public void testRequestScopeWithASyncFailure() { | ||
RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"async-failure\"}") | ||
.post("/") | ||
.then().statusCode(500); | ||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@RequestScoped | ||
public static class MyBean { | ||
public static AtomicInteger DISPOSED = new AtomicInteger(); | ||
|
||
private final AtomicInteger counter = new AtomicInteger(); | ||
|
||
public int inc() { | ||
return counter.getAndIncrement(); | ||
} | ||
|
||
public void get() { | ||
counter.get(); | ||
} | ||
|
||
@PreDestroy | ||
public void destroy() { | ||
DISPOSED.incrementAndGet(); | ||
} | ||
} | ||
|
||
public static class MyFunction { | ||
|
||
@Inject | ||
MyBean bean; | ||
@Inject | ||
Vertx vertx; | ||
|
||
@Funq | ||
public Uni<Greeting> greeting(Identity name) { | ||
Context context = Vertx.currentContext(); | ||
Assertions.assertTrue(VertxContext.isOnDuplicatedContext()); | ||
|
||
if (name.getName().equals("sync-failure")) { | ||
Assertions.assertEquals(0, bean.inc()); | ||
throw new IllegalArgumentException("expected sync-failure"); | ||
} | ||
|
||
return Uni.createFrom().item("Hello " + name.getName() + "!") | ||
.invoke(() -> { | ||
Assertions.assertEquals(0, bean.inc()); | ||
Assertions.assertSame(context, Vertx.currentContext()); | ||
}) | ||
.chain(this::nap) | ||
.invoke(() -> { | ||
Assertions.assertEquals(1, bean.inc()); | ||
Assertions.assertSame(context, Vertx.currentContext()); | ||
}) | ||
.invoke(() -> { | ||
if (name.getName().equals("sync-failure-pipeline")) { | ||
throw new IllegalArgumentException("expected sync-failure-in-pipeline"); | ||
} | ||
}) | ||
.map(s -> { | ||
Greeting greeting = new Greeting(); | ||
greeting.setName(name.getName()); | ||
greeting.setMessage(s); | ||
return greeting; | ||
}) | ||
.chain(greeting -> { | ||
if (greeting.getName().equals("async-failure")) { | ||
return Uni.createFrom().failure(() -> new IllegalArgumentException("expected async-failure")); | ||
} | ||
return Uni.createFrom().item(greeting); | ||
}); | ||
} | ||
|
||
public Uni<String> nap(String s) { | ||
return Uni.createFrom().emitter(e -> { | ||
vertx.setTimer(100, x -> e.complete(s)); | ||
}); | ||
} | ||
|
||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
...funqy-knative-events/deployment/src/test/java/io/quarkus/funqy/test/RequestScopeTest.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,103 @@ | ||
package io.quarkus.funqy.test; | ||
|
||
import static org.hamcrest.Matchers.equalTo; | ||
import static org.hamcrest.Matchers.nullValue; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import javax.annotation.PreDestroy; | ||
import javax.enterprise.context.RequestScoped; | ||
import javax.inject.Inject; | ||
|
||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.funqy.Funq; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.restassured.RestAssured; | ||
import io.smallrye.common.vertx.VertxContext; | ||
|
||
public class RequestScopeTest { | ||
|
||
@RegisterExtension | ||
static QuarkusUnitTest test = new QuarkusUnitTest() | ||
.withApplicationRoot((jar) -> jar | ||
.addClasses(MyBean.class, Identity.class, Greeting.class, MyFunction.class) | ||
.addAsResource("greeting.properties", "application.properties")); | ||
|
||
@BeforeEach | ||
void cleanup() { | ||
MyBean.DISPOSED.set(0); | ||
} | ||
|
||
@Test | ||
public void testRequestScope() { | ||
RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"Roxanne\"}") | ||
.post("/") | ||
.then().statusCode(200) | ||
.header("ce-id", nullValue()) | ||
.body("name", equalTo("Roxanne")) | ||
.body("message", equalTo("Hello Roxanne!")); | ||
|
||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@Test | ||
public void testRequestScopeTerminationWithSynchronousFailure() { | ||
String body = RestAssured.given().contentType("application/json") | ||
.body("{\"name\": \"failure\"}") | ||
.post("/") | ||
.then().statusCode(500).extract().asString(); | ||
|
||
Assertions.assertTrue(body.contains("expected failure")); | ||
Assertions.assertEquals(1, MyBean.DISPOSED.get()); | ||
} | ||
|
||
@RequestScoped | ||
public static class MyBean { | ||
|
||
public static AtomicInteger DISPOSED = new AtomicInteger(); | ||
|
||
private final AtomicInteger counter = new AtomicInteger(); | ||
|
||
public int inc() { | ||
return counter.getAndIncrement(); | ||
} | ||
|
||
public void get() { | ||
counter.get(); | ||
} | ||
|
||
@PreDestroy | ||
public void destroy() { | ||
DISPOSED.incrementAndGet(); | ||
} | ||
} | ||
|
||
public static class MyFunction { | ||
|
||
@Inject | ||
MyBean bean; | ||
|
||
@Funq | ||
public Greeting greet(Identity name) { | ||
Assertions.assertTrue(VertxContext.isOnDuplicatedContext()); | ||
Assertions.assertEquals(0, bean.inc()); | ||
|
||
if (name.getName().equals("failure")) { | ||
throw new IllegalArgumentException("expected failure"); | ||
} | ||
|
||
Greeting greeting = new Greeting(); | ||
greeting.setName(name.getName()); | ||
greeting.setMessage("Hello " + name.getName() + "!"); | ||
|
||
Assertions.assertEquals(1, bean.inc()); | ||
return greeting; | ||
} | ||
|
||
} | ||
} |
1 change: 1 addition & 0 deletions
1
extensions/funqy/funqy-knative-events/deployment/src/test/resources/greeting-uni.properties
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 @@ | ||
quarkus.funqy.export=greeting |
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