1
2
3
4
5
6
7 package com.atlassian.plugin.descriptors;
8
9 import com.atlassian.plugin.ModuleDescriptor;
10 import com.atlassian.plugin.ModulePermissionException;
11 import com.atlassian.plugin.Permissions;
12 import com.atlassian.plugin.Plugin;
13 import com.atlassian.plugin.PluginParseException;
14 import com.atlassian.plugin.RequirePermission;
15 import com.atlassian.plugin.StateAware;
16 import com.atlassian.plugin.elements.ResourceDescriptor;
17 import com.atlassian.plugin.impl.StaticPlugin;
18 import com.atlassian.plugin.mock.MockAnimal;
19 import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
20 import com.atlassian.plugin.mock.MockMineral;
21 import com.atlassian.plugin.module.ModuleFactory;
22 import com.atlassian.plugin.util.ClassLoaderUtils;
23 import com.google.common.collect.ImmutableSet;
24 import org.dom4j.DocumentException;
25 import org.dom4j.DocumentHelper;
26 import org.dom4j.Element;
27 import org.junit.Test;
28
29 import java.util.List;
30 import java.util.Optional;
31 import java.util.Set;
32 import java.util.concurrent.atomic.AtomicBoolean;
33
34 import static java.lang.Boolean.FALSE;
35 import static org.hamcrest.CoreMatchers.equalTo;
36 import static org.hamcrest.CoreMatchers.is;
37 import static org.junit.Assert.assertEquals;
38 import static org.junit.Assert.assertNotNull;
39 import static org.junit.Assert.assertNull;
40 import static org.junit.Assert.assertSame;
41 import static org.junit.Assert.assertThat;
42 import static org.junit.Assert.assertTrue;
43 import static org.junit.Assert.fail;
44 import static org.mockito.Mockito.mock;
45 import static org.mockito.Mockito.never;
46 import static org.mockito.Mockito.spy;
47 import static org.mockito.Mockito.verify;
48 import static org.mockito.Mockito.when;
49
50 public class TestAbstractModuleDescriptor {
51 public static final String ANNOTATION_PERMISSION = "annotation_permission";
52
53 @Test
54 public void testAssertModuleClassImplements() throws DocumentException, PluginParseException {
55 ModuleDescriptor descriptor = new AbstractModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY) {
56 public void init(Plugin plugin, Element element) throws PluginParseException {
57 super.init(plugin, element);
58 enabled();
59 assertModuleClassImplements(MockMineral.class);
60 }
61
62 public Object getModule() {
63 return null;
64 }
65 };
66
67 try {
68 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
69 ((StateAware) descriptor).enabled();
70 fail("Should have blown up.");
71 } catch (PluginParseException e) {
72 assertEquals("Given module class: com.atlassian.plugin.mock.MockBear does not implement com.atlassian.plugin.mock.MockMineral", e.getMessage());
73 }
74
75
76 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockGold\" />").getRootElement());
77 }
78
79 @Test
80 public void testLoadClassFromNewModuleFactory() {
81 ModuleFactory moduleFactory = mock(ModuleFactory.class);
82 AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(moduleFactory, "foo");
83 Plugin plugin = mock(Plugin.class);
84 moduleDescriptor.loadClass(plugin, "foo");
85 assertEquals(String.class, moduleDescriptor.getModuleClass());
86 }
87
88 @Test
89 public void testLoadClassFromNewModuleFactoryWithExtendsNumberType() {
90 ModuleFactory moduleFactory = mock(ModuleFactory.class);
91 AbstractModuleDescriptor moduleDescriptor = new ExtendsNumberModuleDescriptor(moduleFactory, "foo");
92 Plugin plugin = mock(Plugin.class);
93
94 try {
95 moduleDescriptor.loadClass(plugin, "foo");
96 fail("Should have complained about extends type");
97 } catch (IllegalStateException ex) {
98
99 }
100 }
101
102 @Test
103 public void testLoadClassFromNewModuleFactoryWithExtendsNothingType() {
104 ModuleFactory moduleFactory = mock(ModuleFactory.class);
105 AbstractModuleDescriptor moduleDescriptor = new ExtendsNothingModuleDescriptor(moduleFactory, "foo");
106 Plugin plugin = mock(Plugin.class);
107
108 try {
109 moduleDescriptor.loadClass(plugin, "foo");
110 fail("Should have complained about extends type");
111 } catch (IllegalStateException ex) {
112
113 }
114 }
115
116 @Test
117 public void testGetModuleReturnClass() {
118 AbstractModuleDescriptor desc = new MockAnimalModuleDescriptor();
119 assertEquals(MockAnimal.class, desc.getModuleReturnClass());
120 }
121
122 @Test
123 public void testGetModuleReturnClassWithExtendsNumber() {
124 ModuleFactory moduleFactory = mock(ModuleFactory.class);
125 AbstractModuleDescriptor moduleDescriptor = new ExtendsNothingModuleDescriptor(moduleFactory, "foo");
126 assertEquals(Object.class, moduleDescriptor.getModuleReturnClass());
127 }
128
129 @Test
130 public void testLoadClassFromNewModuleFactoryButUnknownType() {
131 ModuleFactory moduleFactory = mock(ModuleFactory.class);
132 AbstractModuleDescriptor moduleDescriptor = new AbstractModuleDescriptor(moduleFactory) {
133 public AbstractModuleDescriptor init() {
134 moduleClassName = "foo";
135 return this;
136 }
137
138 @Override
139 public Object getModule() {
140 return null;
141 }
142 }.init();
143 try {
144 Plugin plugin = mock(Plugin.class);
145 moduleDescriptor.loadClass(plugin, "foo");
146 fail("Should have complained about unknown type");
147 } catch (IllegalStateException ex) {
148
149 }
150 }
151
152 @Test
153 public void testSingletonness() throws DocumentException, PluginParseException {
154 ModuleDescriptor descriptor = makeSingletonDescriptor();
155
156
157 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
158 ((StateAware) descriptor).enabled();
159 Object module = descriptor.getModule();
160 assertTrue(module == descriptor.getModule());
161
162
163 descriptor = makeSingletonDescriptor();
164 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" singleton=\"true\" />").getRootElement());
165 ((StateAware) descriptor).enabled();
166 module = descriptor.getModule();
167 assertTrue(module == descriptor.getModule());
168
169
170 descriptor = makeSingletonDescriptor();
171 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" singleton=\"false\" />").getRootElement());
172 ((StateAware) descriptor).enabled();
173 module = descriptor.getModule();
174 assertTrue(module != descriptor.getModule());
175 }
176
177 @Test
178 public void testGetResourceDescriptor() throws DocumentException, PluginParseException {
179 ModuleDescriptor descriptor = makeSingletonDescriptor();
180 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
181 "<resource type='velocity' name='view' location='foo' />" +
182 "</animal>").getRootElement());
183
184 assertNull(descriptor.getResourceLocation("foo", "bar"));
185 assertNull(descriptor.getResourceLocation("velocity", "bar"));
186 assertNull(descriptor.getResourceLocation("foo", "view"));
187 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()).getResourceLocationForName("view").getLocation(), descriptor.getResourceLocation("velocity", "view").getLocation());
188 }
189
190 @Test
191 public void testGetResourceDescriptorByType() throws DocumentException, PluginParseException {
192 ModuleDescriptor descriptor = makeSingletonDescriptor();
193 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
194 "<resource type='velocity' name='view' location='foo' />" +
195 "<resource type='velocity' name='input-params' location='bar' />" +
196 "</animal>").getRootElement());
197
198 final List resourceDescriptors = descriptor.getResourceDescriptors("velocity");
199 assertNotNull(resourceDescriptors);
200 assertEquals(2, resourceDescriptors.size());
201
202 ResourceDescriptor resourceDescriptor = (ResourceDescriptor) resourceDescriptors.get(0);
203 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()), resourceDescriptor);
204
205 resourceDescriptor = (ResourceDescriptor) resourceDescriptors.get(1);
206 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='input-params' location='bar' />").getRootElement()), resourceDescriptor);
207 }
208
209 @Test
210 public void testDestroy() {
211 AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
212 Plugin plugin = mock(Plugin.class);
213 descriptor.setPlugin(plugin);
214
215 descriptor.enabled();
216
217 descriptor.destroy();
218
219 verify(descriptor).disabled();
220 assertSame(plugin, descriptor.getPlugin());
221 }
222
223 @Test
224 public void testDestroyWhenDisabled() {
225 AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
226 Plugin plugin = mock(Plugin.class);
227 descriptor.setPlugin(plugin);
228
229 descriptor.destroy();
230
231
232 verify(descriptor, never()).disabled();
233 assertSame(plugin, descriptor.getPlugin());
234 }
235
236 @Test
237 public void testDestroyDelegatesToDeprecatedOverride() {
238 final AtomicBoolean called = new AtomicBoolean();
239
240 AbstractModuleDescriptor descriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null) {
241 @Override
242 @Deprecated
243 public void destroy(Plugin plugin) {
244 called.set(true);
245 }
246 };
247
248 Plugin plugin = mock(Plugin.class);
249 descriptor.setPlugin(plugin);
250
251 descriptor.destroy();
252
253 assertTrue(called.get());
254 }
255
256 @Test
257 public void testCheckPermissionsForModuleDescriptorAndPluginDefinedWithNoPermission() {
258 final AbstractModuleDescriptor moduleDescriptor = makeSingletonDescriptor();
259 moduleDescriptor.setPlugin(mock(Plugin.class));
260
261 try {
262 moduleDescriptor.checkPermissions();
263 } catch (ModulePermissionException e) {
264 fail("Didn't expected the exception here" + e);
265 }
266 }
267
268 @Test
269 public void testCheckPermissionsForModuleDescriptorAndPluginWithSamePermission() {
270 final String somePermission = "some_permission";
271 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
272 @Override
273 protected Set<String> getRequiredPermissions() {
274 return ImmutableSet.of(somePermission);
275 }
276 };
277
278 final Plugin plugin = mock(Plugin.class);
279 moduleDescriptor.setPlugin(plugin);
280 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(somePermission, ANNOTATION_PERMISSION));
281
282 try {
283 moduleDescriptor.checkPermissions();
284 } catch (ModulePermissionException e) {
285 fail("Didn't expected the exception here" + e);
286 }
287 }
288
289 @Test
290 public void testCheckPermissionsForModuleDescriptorWithPermissionsAndPluginWithAllPermission() {
291 final Set<String> permissions = ImmutableSet.of("some_permission");
292 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
293 @Override
294 protected Set<String> getRequiredPermissions() {
295 return permissions;
296 }
297 };
298
299 final Plugin plugin = mock(Plugin.class);
300 moduleDescriptor.setPlugin(plugin);
301 when(plugin.hasAllPermissions()).thenReturn(true);
302 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
303
304 try {
305 moduleDescriptor.checkPermissions();
306 } catch (ModulePermissionException e) {
307 fail("Didn't expected the exception here" + e);
308 }
309 }
310
311 @Test
312 public void testCheckPermissionsForModuleDescriptorWithPermissionsNotInPluginPermissions() {
313 final Set<String> permissions = ImmutableSet.of("some_module_permission", "one_plugin_permission");
314 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
315 @Override
316 protected Set<String> getRequiredPermissions() {
317 return permissions;
318 }
319 };
320
321 final Plugin plugin = mock(Plugin.class);
322 moduleDescriptor.setPlugin(plugin);
323 ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", ANNOTATION_PERMISSION);
324 when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
325
326 try {
327 moduleDescriptor.checkPermissions();
328 fail("We expected some exception here");
329 } catch (ModulePermissionException e) {
330 assertEquals(ImmutableSet.of("some_module_permission"), e.getPermissions());
331 }
332 }
333
334 @Test
335 public void testCheckPermissionsForModuleDescriptorWithPermissionsAllInPluginPermissions() {
336 final Set<String> permissions = ImmutableSet.of("two_plugin_permission", "one_plugin_permission");
337 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
338 @Override
339 protected Set<String> getRequiredPermissions() {
340 return permissions;
341 }
342 };
343
344 final Plugin plugin = mock(Plugin.class);
345 moduleDescriptor.setPlugin(plugin);
346 ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", "three_plugin_permission", ANNOTATION_PERMISSION);
347 when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
348
349 moduleDescriptor.checkPermissions();
350 }
351
352 @Test
353 public void setModuleIsNotBrokenAfterConstruction() {
354 final ModuleDescriptor<String> md = newStringModuleDescriptor();
355 assertThat(md.isBroken(), is(false));
356 }
357
358 @Test
359 public void setBrokenMakesModuleBroken() {
360 final ModuleDescriptor<String> md = newStringModuleDescriptor();
361 md.setBroken();
362 assertThat(md.isBroken(), is(true));
363 }
364
365 @Test
366 public void enableClearesBrokenState() throws Exception {
367 final StringModuleDescriptor md = newStringModuleDescriptor();
368 md.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
369 md.setBroken();
370 md.enabled();
371 assertThat(md.isBroken(), is(false));
372 }
373
374 @Test
375 public void verifyInheritingEmptyScopeFromPlugin() throws Exception {
376 StaticPlugin plugin = new StaticPlugin() {
377 public Optional<String> getScopeKey() {
378 return Optional.empty();
379 }
380 };
381 ModuleDescriptor descriptor = makeSingletonDescriptor();
382 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
383
384 assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
385 }
386
387 @Test
388 public void verifyInheritingPresentScopeFromPlugin() throws Exception {
389 StaticPlugin plugin = new StaticPlugin() {
390 public Optional<String> getScopeKey() {
391 return Optional.of("licenses/jira-service-desk");
392 }
393 };
394 ModuleDescriptor descriptor = makeSingletonDescriptor();
395 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
396
397 assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
398 }
399
400 @Test
401 public void scopeIsEmptyWhenModuleDescopedAndPluginIsScoped() throws Exception {
402 StaticPlugin plugin = new StaticPlugin() {
403 public Optional<String> getScopeKey() {
404 return Optional.of("licenses/jira-service-desk");
405 }
406 };
407 ModuleDescriptor descriptor = makeSingletonDescriptor();
408 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"false\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
409
410 assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
411 }
412
413 @Test
414 public void scopeIsDerivedFromPluginWhenModuleIsScoped() throws Exception {
415 StaticPlugin plugin = new StaticPlugin() {
416 public Optional<String> getScopeKey() {
417 return Optional.of("licenses/jira-service-desk");
418 }
419 };
420 ModuleDescriptor descriptor = makeSingletonDescriptor();
421 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"true\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
422
423 assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
424 }
425
426 private StringModuleDescriptor newStringModuleDescriptor() {
427 return new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName());
428 }
429
430 private AbstractModuleDescriptor makeSingletonDescriptor() {
431 AbstractModuleDescriptor descriptor = new AbstractModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY) {
432 Object module;
433
434 public void init(Plugin plugin, Element element) throws PluginParseException {
435 super.init(plugin, element);
436 }
437
438 public Object getModule() {
439 try {
440 if (!isSingleton()) {
441 return ClassLoaderUtils.loadClass(getModuleClass().getName(), TestAbstractModuleDescriptor.class).newInstance();
442 } else {
443 if (module == null) {
444 module = ClassLoaderUtils.loadClass(getModuleClass().getName(), TestAbstractModuleDescriptor.class).newInstance();
445 }
446
447 return module;
448 }
449 } catch (Exception e) {
450 throw new RuntimeException("What happened Dave?");
451 }
452 }
453 };
454 return descriptor;
455 }
456
457 @RequirePermission(ANNOTATION_PERMISSION)
458 private static class StringModuleDescriptor extends AbstractModuleDescriptor<String> {
459 public StringModuleDescriptor(ModuleFactory moduleFactory, String className) {
460 super(moduleFactory);
461 moduleClassName = className;
462 }
463
464 @Override
465 public String getModule() {
466 return null;
467 }
468
469 }
470
471 private static class ExtendsNumberModuleDescriptor<T extends Number> extends AbstractModuleDescriptor<T> {
472 public ExtendsNumberModuleDescriptor(ModuleFactory moduleFactory, String className) {
473 super(moduleFactory);
474 moduleClassName = className;
475 }
476
477 @Override
478 public T getModule() {
479 return null;
480 }
481 }
482
483 private static class ExtendsNothingModuleDescriptor<T> extends AbstractModuleDescriptor<T> {
484 public ExtendsNothingModuleDescriptor(ModuleFactory moduleFactory, String className) {
485 super(moduleFactory);
486 moduleClassName = className;
487 }
488
489 @Override
490 public T getModule() {
491 return null;
492 }
493 }
494 }