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