forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Properly support CompletionStage as a return type in caching extension
Fixes: quarkusio#23816
- Loading branch information
Showing
6 changed files
with
236 additions
and
107 deletions.
There are no files selected for viewing
87 changes: 0 additions & 87 deletions
87
...src/test/java/io/quarkus/cache/test/runtime/CacheResultCompletionStageReturnTypeTest.java
This file was deleted.
Oops, something went wrong.
164 changes: 164 additions & 0 deletions
164
...deployment/src/test/java/io/quarkus/cache/test/runtime/CompletionStageReturnTypeTest.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,164 @@ | ||
package io.quarkus.cache.test.runtime; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotSame; | ||
import static org.junit.jupiter.api.Assertions.assertSame; | ||
|
||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutionException; | ||
|
||
import javax.enterprise.context.ApplicationScoped; | ||
import javax.inject.Inject; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.cache.CacheInvalidate; | ||
import io.quarkus.cache.CacheInvalidateAll; | ||
import io.quarkus.cache.CacheResult; | ||
import io.quarkus.test.QuarkusUnitTest; | ||
|
||
/** | ||
* Tests the caching annotations on methods returning {@link CompletableFuture}. | ||
*/ | ||
public class CompletionStageReturnTypeTest { | ||
|
||
private static final String CACHE_NAME_1 = "test-cache-1"; | ||
private static final String CACHE_NAME_2 = "test-cache-2"; | ||
private static final String KEY_1 = "key-1"; | ||
private static final String KEY_2 = "key-2"; | ||
|
||
@RegisterExtension | ||
static final QuarkusUnitTest TEST = new QuarkusUnitTest().withApplicationRoot((jar) -> jar.addClass(CachedService.class)); | ||
|
||
@Inject | ||
CachedService cachedService; | ||
|
||
@Test | ||
void testCacheResult() throws ExecutionException, InterruptedException { | ||
// STEP 1 | ||
// Action: a method annotated with @CacheResult and returning a CompletionStage is called. | ||
// Expected effect: the method is invoked, as CompletionStage is eager. | ||
// Verified by: invocations counter. | ||
CompletableFuture<String> cf1 = cachedService.cacheResult1(KEY_1); | ||
assertEquals(1, cachedService.getCacheResultInvocations()); | ||
|
||
// STEP 2 | ||
// Action: same call as STEP 1. | ||
// Expected effect: same as STEP 1 with a different CompletionStage instance returned. | ||
// Verified by: invocations counter and different objects references between STEPS 1 AND 2 results. | ||
CompletableFuture<String> cf2 = cachedService.cacheResult1(KEY_1); | ||
assertEquals(1, cachedService.getCacheResultInvocations()); | ||
assertNotSame(cf1, cf2); | ||
|
||
// STEP 3 | ||
// Action: the Uni returned in STEP 1 is subscribed to and we wait for an item event to be fired. | ||
// Expected effect: the method from STEP 1 is invoked and its result is cached. | ||
// Verified by: invocations counter and STEP 4. | ||
String emittedItem1 = cf1.get(); | ||
assertEquals(1, cachedService.getCacheResultInvocations()); | ||
|
||
// STEP 4 | ||
// Action: the Uni returned in STEP 2 is subscribed to and we wait for an item event to be fired. | ||
// Expected effect: the method from STEP 2 is not invoked and the value cached in STEP 3 is returned. | ||
// Verified by: invocations counter and same object reference between STEPS 3 and 4 emitted items. | ||
String emittedItem2 = cf2.get(); | ||
assertEquals(1, cachedService.getCacheResultInvocations()); | ||
assertSame(emittedItem1, emittedItem2); | ||
|
||
// STEP 5 | ||
// Action: same call as STEP 2 with a different key and an immediate subscription. | ||
// Expected effect: the method is invoked and a new item is emitted (also cached). | ||
// Verified by: invocations counter. | ||
String emittedItem3 = cachedService.cacheResult1("another-key").get(); | ||
assertEquals(2, cachedService.getCacheResultInvocations()); | ||
} | ||
|
||
@Test | ||
void testCacheInvalidate() throws ExecutionException, InterruptedException { | ||
// First, let's put some data into the caches. | ||
String value1 = cachedService.cacheResult1(KEY_1).get(); | ||
Object value2 = cachedService.cacheResult2(KEY_2).get(); | ||
|
||
// We will invalidate some data (only KEY_1) in all caches later. | ||
cachedService.cacheInvalidate(KEY_1).get(); | ||
// For now, the method that will invalidate the data should not be invoked, as CompletionStage is eager. | ||
assertEquals(1, cachedService.getCacheInvalidateInvocations()); | ||
|
||
// The data for the second key should still be cached at this point. | ||
Object value4 = cachedService.cacheResult2(KEY_2).get(); | ||
assertSame(value2, value4); | ||
|
||
// Let's call the methods annotated with @CacheResult again. | ||
String value7 = cachedService.cacheResult1(KEY_1).get(); | ||
|
||
// The objects references should be different for the invalidated key. | ||
assertNotSame(value1, value7); | ||
} | ||
|
||
@Test | ||
void testCacheInvalidateAll() throws ExecutionException, InterruptedException { | ||
// First, let's put some data into the caches. | ||
String value1 = cachedService.cacheResult1(KEY_1).get(); | ||
Object value2 = cachedService.cacheResult2(KEY_2).get(); | ||
|
||
// We will invalidate all the data in all caches later. | ||
cachedService.cacheInvalidateAll().get(); | ||
|
||
// For now, the method that will invalidate the data should not be invoked, as CompletionStage is eager. | ||
assertEquals(1, cachedService.getCacheInvalidateAllInvocations()); | ||
|
||
// Let's call the methods annotated with @CacheResult again. | ||
String value3 = cachedService.cacheResult1(KEY_1).get(); | ||
Object value4 = cachedService.cacheResult2(KEY_2).get(); | ||
|
||
// All objects references should be different. | ||
assertNotSame(value1, value3); | ||
assertNotSame(value2, value4); | ||
} | ||
|
||
@ApplicationScoped | ||
static class CachedService { | ||
|
||
private volatile int cacheResultInvocations; | ||
private volatile int cacheInvalidateInvocations; | ||
private volatile int cacheInvalidateAllInvocations; | ||
|
||
@CacheResult(cacheName = CACHE_NAME_1) | ||
public CompletableFuture<String> cacheResult1(String key) { | ||
cacheResultInvocations++; | ||
return CompletableFuture.completedFuture(new String()); | ||
} | ||
|
||
@CacheResult(cacheName = CACHE_NAME_2) | ||
public CompletableFuture<Object> cacheResult2(String key) { | ||
return CompletableFuture.completedFuture(new Object()); | ||
} | ||
|
||
@CacheInvalidate(cacheName = CACHE_NAME_1) | ||
@CacheInvalidate(cacheName = CACHE_NAME_2) | ||
public CompletableFuture<Void> cacheInvalidate(String key) { | ||
cacheInvalidateInvocations++; | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
@CacheInvalidateAll(cacheName = CACHE_NAME_1) | ||
@CacheInvalidateAll(cacheName = CACHE_NAME_2) | ||
public CompletableFuture<Void> cacheInvalidateAll() { | ||
cacheInvalidateAllInvocations++; | ||
return CompletableFuture.completedFuture(null); | ||
} | ||
|
||
public int getCacheResultInvocations() { | ||
return cacheResultInvocations; | ||
} | ||
|
||
public int getCacheInvalidateInvocations() { | ||
return cacheInvalidateInvocations; | ||
} | ||
|
||
public int getCacheInvalidateAllInvocations() { | ||
return cacheInvalidateAllInvocations; | ||
} | ||
} | ||
} |
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
Oops, something went wrong.