1 package com.atlassian.vcache.internal.memcached;
2
3 import com.atlassian.marshalling.jdk.StringMarshalling;
4 import com.atlassian.vcache.DirectExternalCache;
5 import com.atlassian.vcache.ExternalCacheException;
6 import com.atlassian.vcache.ExternalCacheSettings;
7 import com.atlassian.vcache.ExternalCacheSettingsBuilder;
8 import com.atlassian.vcache.JvmCache;
9 import com.atlassian.vcache.JvmCacheSettings;
10 import com.atlassian.vcache.JvmCacheSettingsBuilder;
11 import com.atlassian.vcache.RequestCache;
12 import com.atlassian.vcache.StableReadExternalCache;
13 import com.atlassian.vcache.VCacheException;
14 import com.atlassian.vcache.internal.BegunTransactionalActivityHandler;
15 import com.atlassian.vcache.internal.ExternalCacheDetails;
16 import com.atlassian.vcache.internal.JvmCacheDetails;
17 import com.atlassian.vcache.internal.RequestCacheDetails;
18 import com.atlassian.vcache.internal.VCacheCreationHandler;
19 import com.atlassian.vcache.internal.VCacheSettingsDefaultsProvider;
20 import com.atlassian.vcache.internal.core.metrics.DefaultMetricsCollector;
21 import com.atlassian.vcache.internal.core.metrics.MetricsCollector;
22 import com.atlassian.vcache.internal.test.ThreadLocalRequestContextSupplier;
23 import net.spy.memcached.MemcachedClientIF;
24 import org.junit.After;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.rules.ExpectedException;
29 import org.junit.runner.RunWith;
30 import org.mockito.ArgumentCaptor;
31 import org.mockito.Captor;
32 import org.mockito.Mock;
33 import org.mockito.runners.MockitoJUnitRunner;
34
35 import java.io.File;
36 import java.time.Duration;
37 import java.util.Map;
38 import java.util.Optional;
39
40 import static org.hamcrest.Matchers.containsInAnyOrder;
41 import static org.hamcrest.Matchers.is;
42 import static org.hamcrest.Matchers.notNullValue;
43 import static org.junit.Assert.assertThat;
44 import static org.mockito.Matchers.any;
45 import static org.mockito.Matchers.eq;
46 import static org.mockito.Mockito.doThrow;
47 import static org.mockito.Mockito.times;
48 import static org.mockito.Mockito.verify;
49 import static org.mockito.Mockito.verifyNoMoreInteractions;
50 import static org.mockito.Mockito.when;
51
52 @RunWith(MockitoJUnitRunner.class)
53 public class MemcachedVCacheServiceTest {
54 @Rule
55 public ExpectedException thrown = ExpectedException.none();
56
57 @Mock
58 private MemcachedClientIF client;
59 @Mock
60 private VCacheSettingsDefaultsProvider defaultsProvider;
61 @Mock
62 private VCacheCreationHandler creationHandler;
63 @Mock
64 private BegunTransactionalActivityHandler begunTransactionalActivityHandler;
65
66 @Captor
67 private ArgumentCaptor<JvmCacheDetails> jvmCacheDetailsCaptor;
68 @Captor
69 private ArgumentCaptor<ExternalCacheDetails> externalCacheDetailsCaptor;
70
71 private final ThreadLocalRequestContextSupplier requestContextSupplier = ThreadLocalRequestContextSupplier.strictSupplier();
72 private final MetricsCollector metricsCollector = new DefaultMetricsCollector(requestContextSupplier);
73
74
75 private MemcachedVCacheService service;
76
77 @Before
78 public void initService() {
79 final MemcachedVCacheServiceSettings settings = new MemcachedVCacheServiceSettingsBuilder()
80 .productIdentifier("confira")
81 .clientSupplier(() -> client)
82 .threadLocalContextSupplier(requestContextSupplier)
83 .workContextContextSupplier(requestContextSupplier)
84 .defaultsProvider(defaultsProvider)
85 .creationHandler(creationHandler)
86 .metricsCollector(metricsCollector)
87 .begunTransactionalActivityHandler(begunTransactionalActivityHandler)
88 .build();
89
90 service = new MemcachedVCacheService(settings);
91 }
92
93 @After
94 public void destroyService() {
95 verifyNoMoreInteractions(client);
96 }
97
98 @Test
99 public void getJvmCache_normal() throws Exception {
100 final JvmCacheSettings initSettings = new JvmCacheSettingsBuilder()
101 .defaultTtl(Duration.ofSeconds(10))
102 .maxEntries(20)
103 .build();
104 final JvmCacheSettings finalSettings = new JvmCacheSettingsBuilder()
105 .defaultTtl(Duration.ofSeconds(5))
106 .maxEntries(10)
107 .build();
108
109 when(defaultsProvider.getJvmDefaults(eq("fruit")))
110 .thenReturn(new JvmCacheSettingsBuilder().build());
111 when(creationHandler.jvmCacheCreation(any()))
112 .thenReturn(finalSettings);
113
114 final JvmCache<String, Object> cache = service.getJvmCache("fruit", initSettings);
115
116 verify(creationHandler).jvmCacheCreation(jvmCacheDetailsCaptor.capture());
117 assertThat(cache, notNullValue());
118 assertThat(cache.getName(), is("fruit"));
119
120 final JvmCacheDetails details = jvmCacheDetailsCaptor.getValue();
121 assertThat(details, notNullValue());
122 assertThat(details.getSettings(), notNullValue());
123 assertThat(details.getName(), notNullValue());
124 assertThat(details.getName(), is("fruit"));
125 assertThat(details.getSettings().getDefaultTtl(), is(Optional.of(Duration.ofSeconds(10))));
126 assertThat(details.getSettings().getMaxEntries(), is(Optional.of(20)));
127
128 final JvmCache<String, Object> cache2 = service.getJvmCache("fruit", initSettings);
129
130 assertThat(cache2, notNullValue());
131 assertThat(cache2, is(cache));
132
133 final Map<String, JvmCacheDetails> allJvmDetails = service.allJvmCacheDetails();
134
135 assertThat(allJvmDetails, notNullValue());
136 assertThat(allJvmDetails.keySet(), containsInAnyOrder("fruit"));
137 assertThat(allJvmDetails.get("fruit").getName(), is("fruit"));
138 assertThat(allJvmDetails.get("fruit").getSettings(), is(finalSettings));
139 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
140 }
141
142 @Test
143 public void getJvmCache_failure() {
144 final JvmCacheSettings initSettings = new JvmCacheSettingsBuilder()
145 .defaultTtl(Duration.ofSeconds(10))
146 .maxEntries(20)
147 .build();
148
149 when(defaultsProvider.getJvmDefaults(eq("bat")))
150 .thenReturn(new JvmCacheSettingsBuilder().build());
151 when(creationHandler.jvmCacheCreation(any())).thenThrow(new VCacheException("not allowed"));
152
153 thrown.expect(VCacheException.class);
154 thrown.expectMessage("not allowed");
155
156 final JvmCache<String, File> cache = service.getJvmCache("bat", initSettings);
157 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
158 }
159
160 @Test
161 public void getRequestCache_normal() throws Exception {
162 final RequestCache<File, String> cache = service.getRequestCache("threadlocal");
163
164 final ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
165 verify(creationHandler, times(1)).requestCacheCreation(argument.capture());
166
167 assertThat(cache, notNullValue());
168 assertThat(cache.getName(), is("threadlocal"));
169 assertThat(argument.getValue(), is("threadlocal"));
170
171 final Map<String, RequestCacheDetails> allRequestDetails = service.allRequestCacheDetails();
172
173 assertThat(allRequestDetails, notNullValue());
174 assertThat(allRequestDetails.keySet(), containsInAnyOrder("threadlocal"));
175 assertThat(allRequestDetails.get("threadlocal").getName(), is("threadlocal"));
176 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
177 }
178
179 @Test
180 public void getRequestCache_failure() {
181 doThrow(new VCacheException("not allowed")).when(creationHandler).requestCacheCreation(eq("threadlocal"));
182
183 thrown.expect(VCacheException.class);
184 thrown.expectMessage("not allowed");
185
186 final RequestCache<File, String> cache = service.getRequestCache("threadlocal");
187 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
188 }
189
190 @Test
191 public void getDirectExternalCache_normal() throws Exception {
192 final ExternalCacheSettings initSettings = new ExternalCacheSettingsBuilder()
193 .defaultTtl(Duration.ofSeconds(10))
194 .entryCountHint(10)
195 .build();
196 final ExternalCacheSettings finalSettings = new ExternalCacheSettingsBuilder()
197 .defaultTtl(Duration.ofSeconds(5))
198 .entryCountHint(5)
199 .build();
200
201 when(defaultsProvider.getExternalDefaults(eq("fruit")))
202 .thenReturn(new ExternalCacheSettingsBuilder().build());
203 when(creationHandler.externalCacheCreation(any()))
204 .thenReturn(finalSettings);
205
206 final DirectExternalCache<String> cache =
207 service.getDirectExternalCache("fruit", StringMarshalling.pair(), initSettings);
208
209 verify(creationHandler).externalCacheCreation(externalCacheDetailsCaptor.capture());
210 assertThat(cache, notNullValue());
211 assertThat(cache.getName(), is("fruit"));
212
213 final ExternalCacheDetails details = externalCacheDetailsCaptor.getValue();
214 assertThat(details, notNullValue());
215 assertThat(details.getSettings(), notNullValue());
216 assertThat(details.getName(), notNullValue());
217 assertThat(details.getName(), is("fruit"));
218 assertThat(details.getSettings().getDefaultTtl(), is(Optional.of(Duration.ofSeconds(10))));
219
220 final DirectExternalCache<String> cache2 =
221 service.getDirectExternalCache("fruit", StringMarshalling.pair(), initSettings);
222
223 assertThat(cache2, notNullValue());
224
225 final Map<String, ExternalCacheDetails> allExternalDetails = service.allExternalCacheDetails();
226
227 assertThat(allExternalDetails, notNullValue());
228 assertThat(allExternalDetails.keySet(), containsInAnyOrder("fruit"));
229 assertThat(allExternalDetails.get("fruit").getName(), is("fruit"));
230 assertThat(allExternalDetails.get("fruit").getSettings(), is(finalSettings));
231 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
232 }
233
234 @Test
235 public void getStableReadExternalCache_normal() throws Exception {
236 final ExternalCacheSettings initSettings = new ExternalCacheSettingsBuilder()
237 .defaultTtl(Duration.ofSeconds(10))
238 .entryCountHint(10)
239 .build();
240 final ExternalCacheSettings finalSettings = new ExternalCacheSettingsBuilder()
241 .defaultTtl(Duration.ofSeconds(5))
242 .entryCountHint(5)
243 .build();
244
245 when(defaultsProvider.getExternalDefaults(eq("fruit")))
246 .thenReturn(new ExternalCacheSettingsBuilder().build());
247 when(creationHandler.externalCacheCreation(any()))
248 .thenReturn(finalSettings);
249
250 final StableReadExternalCache<String> cache =
251 service.getStableReadExternalCache("fruit", StringMarshalling.pair(), initSettings);
252
253 verify(creationHandler).externalCacheCreation(externalCacheDetailsCaptor.capture());
254 assertThat(cache, notNullValue());
255 assertThat(cache.getName(), is("fruit"));
256
257 final ExternalCacheDetails details = externalCacheDetailsCaptor.getValue();
258 assertThat(details, notNullValue());
259 assertThat(details.getSettings(), notNullValue());
260 assertThat(details.getName(), notNullValue());
261 assertThat(details.getName(), is("fruit"));
262 assertThat(details.getSettings().getDefaultTtl(), is(Optional.of(Duration.ofSeconds(10))));
263
264 final StableReadExternalCache<String> cache2 =
265 service.getStableReadExternalCache("fruit", StringMarshalling.pair(), initSettings);
266
267 assertThat(cache2, notNullValue());
268 verify(begunTransactionalActivityHandler, times(0)).onRequest(any());
269 }
270
271 @Test
272 public void getDirectExternalCache_getStableReadExternalCache_failure() throws Exception {
273 final ExternalCacheSettings initSettings = new ExternalCacheSettingsBuilder()
274 .defaultTtl(Duration.ofSeconds(10))
275 .entryCountHint(10)
276 .build();
277 final ExternalCacheSettings finalSettings = new ExternalCacheSettingsBuilder()
278 .defaultTtl(Duration.ofSeconds(5))
279 .entryCountHint(5)
280 .build();
281
282 when(defaultsProvider.getExternalDefaults(eq("fruit")))
283 .thenReturn(new ExternalCacheSettingsBuilder().build());
284 when(creationHandler.externalCacheCreation(any()))
285 .thenReturn(finalSettings);
286
287 final DirectExternalCache<String> cache =
288 service.getDirectExternalCache("fruit", StringMarshalling.pair(), initSettings);
289
290 verify(creationHandler).externalCacheCreation(externalCacheDetailsCaptor.capture());
291 assertThat(cache, notNullValue());
292 assertThat(cache.getName(), is("fruit"));
293
294 thrown.expect(ExternalCacheException.class);
295 thrown.expectMessage("Failed due to CREATION_FAILURE");
296
297 final StableReadExternalCache<String> cache2 =
298 service.getStableReadExternalCache("fruit", StringMarshalling.pair(), initSettings);
299 }
300 }