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.stream.Collectors;
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 testGetResourceDescriptor() throws DocumentException, PluginParseException {
154 ModuleDescriptor descriptor = makeSingletonDescriptor();
155 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
156 "<resource type='velocity' name='view' location='foo' />" +
157 "</animal>").getRootElement());
158
159 assertNull(descriptor.getResourceLocation("foo", "bar"));
160 assertNull(descriptor.getResourceLocation("velocity", "bar"));
161 assertNull(descriptor.getResourceLocation("foo", "view"));
162 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()).getResourceLocationForName("view").getLocation(), descriptor.getResourceLocation("velocity", "view").getLocation());
163 }
164
165 @Test
166 public void testGetResourceDescriptorByType() throws DocumentException, PluginParseException {
167 ModuleDescriptor descriptor = makeSingletonDescriptor();
168 descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
169 "<resource type='velocity' name='view' location='foo' />" +
170 "<resource type='velocity' name='input-params' location='bar' />" +
171 "</animal>").getRootElement());
172
173 final List<ResourceDescriptor> resourceDescriptors = descriptor.getResourceDescriptors().stream()
174 .filter(resource -> resource.getType().equals("velocity"))
175 .collect(Collectors.toList());
176 assertNotNull(resourceDescriptors);
177 assertEquals(2, resourceDescriptors.size());
178
179 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()), resourceDescriptors.get(0));
180 assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='input-params' location='bar' />").getRootElement()), resourceDescriptors.get(1));
181 }
182
183 @Test
184 public void testDestroy() {
185 AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
186 Plugin plugin = mock(Plugin.class);
187 descriptor.setPlugin(plugin);
188
189 descriptor.enabled();
190
191 descriptor.destroy();
192
193 verify(descriptor).disabled();
194 assertSame(plugin, descriptor.getPlugin());
195 }
196
197 @Test
198 public void testDestroyWhenDisabled() {
199 AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
200 Plugin plugin = mock(Plugin.class);
201 descriptor.setPlugin(plugin);
202
203 descriptor.destroy();
204
205
206 verify(descriptor, never()).disabled();
207 assertSame(plugin, descriptor.getPlugin());
208 }
209
210 @Test
211 public void testCheckPermissionsForModuleDescriptorAndPluginDefinedWithNoPermission() {
212 final AbstractModuleDescriptor moduleDescriptor = makeSingletonDescriptor();
213 moduleDescriptor.setPlugin(mock(Plugin.class));
214
215 try {
216 moduleDescriptor.checkPermissions();
217 } catch (ModulePermissionException e) {
218 fail("Didn't expected the exception here" + e);
219 }
220 }
221
222 @Test
223 public void testCheckPermissionsForModuleDescriptorAndPluginWithSamePermission() {
224 final String somePermission = "some_permission";
225 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
226 @Override
227 protected Set<String> getRequiredPermissions() {
228 return ImmutableSet.of(somePermission);
229 }
230 };
231
232 final Plugin plugin = mock(Plugin.class);
233 moduleDescriptor.setPlugin(plugin);
234 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(somePermission, ANNOTATION_PERMISSION));
235
236 try {
237 moduleDescriptor.checkPermissions();
238 } catch (ModulePermissionException e) {
239 fail("Didn't expected the exception here" + e);
240 }
241 }
242
243 @Test
244 public void testCheckPermissionsForModuleDescriptorWithPermissionsAndPluginWithAllPermission() {
245 final Set<String> permissions = ImmutableSet.of("some_permission");
246 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
247 @Override
248 protected Set<String> getRequiredPermissions() {
249 return permissions;
250 }
251 };
252
253 final Plugin plugin = mock(Plugin.class);
254 moduleDescriptor.setPlugin(plugin);
255 when(plugin.hasAllPermissions()).thenReturn(true);
256 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
257
258 try {
259 moduleDescriptor.checkPermissions();
260 } catch (ModulePermissionException e) {
261 fail("Didn't expected the exception here" + e);
262 }
263 }
264
265 @Test
266 public void testCheckPermissionsForModuleDescriptorWithPermissionsNotInPluginPermissions() {
267 final Set<String> permissions = ImmutableSet.of("some_module_permission", "one_plugin_permission");
268 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
269 @Override
270 protected Set<String> getRequiredPermissions() {
271 return permissions;
272 }
273 };
274
275 final Plugin plugin = mock(Plugin.class);
276 moduleDescriptor.setPlugin(plugin);
277 ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", ANNOTATION_PERMISSION);
278 when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
279
280 try {
281 moduleDescriptor.checkPermissions();
282 fail("We expected some exception here");
283 } catch (ModulePermissionException e) {
284 assertEquals(ImmutableSet.of("some_module_permission"), e.getPermissions());
285 }
286 }
287
288 @Test
289 public void testCheckPermissionsForModuleDescriptorWithPermissionsAllInPluginPermissions() {
290 final Set<String> permissions = ImmutableSet.of("two_plugin_permission", "one_plugin_permission");
291 final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
292 @Override
293 protected Set<String> getRequiredPermissions() {
294 return permissions;
295 }
296 };
297
298 final Plugin plugin = mock(Plugin.class);
299 moduleDescriptor.setPlugin(plugin);
300 ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", "three_plugin_permission", ANNOTATION_PERMISSION);
301 when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
302
303 moduleDescriptor.checkPermissions();
304 }
305
306 @Test
307 public void setModuleIsNotBrokenAfterConstruction() {
308 final ModuleDescriptor<String> md = newStringModuleDescriptor();
309 assertThat(md.isBroken(), is(false));
310 }
311
312 @Test
313 public void setBrokenMakesModuleBroken() {
314 final ModuleDescriptor<String> md = newStringModuleDescriptor();
315 md.setBroken();
316 assertThat(md.isBroken(), is(true));
317 }
318
319 @Test
320 public void enableClearesBrokenState() throws Exception {
321 final StringModuleDescriptor md = newStringModuleDescriptor();
322 md.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
323 md.setBroken();
324 md.enabled();
325 assertThat(md.isBroken(), is(false));
326 }
327
328 @Test
329 public void verifyInheritingEmptyScopeFromPlugin() throws Exception {
330 StaticPlugin plugin = new StaticPlugin() {
331 public Optional<String> getScopeKey() {
332 return Optional.empty();
333 }
334 };
335 ModuleDescriptor descriptor = makeSingletonDescriptor();
336 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
337
338 assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
339 }
340
341 @Test
342 public void verifyInheritingPresentScopeFromPlugin() throws Exception {
343 StaticPlugin plugin = new StaticPlugin() {
344 public Optional<String> getScopeKey() {
345 return Optional.of("licenses/jira-service-desk");
346 }
347 };
348 ModuleDescriptor descriptor = makeSingletonDescriptor();
349 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
350
351 assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
352 }
353
354 @Test
355 public void scopeIsEmptyWhenModuleDescopedAndPluginIsScoped() throws Exception {
356 StaticPlugin plugin = new StaticPlugin() {
357 public Optional<String> getScopeKey() {
358 return Optional.of("licenses/jira-service-desk");
359 }
360 };
361 ModuleDescriptor descriptor = makeSingletonDescriptor();
362 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"false\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
363
364 assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
365 }
366
367 @Test
368 public void scopeIsDerivedFromPluginWhenModuleIsScoped() throws Exception {
369 StaticPlugin plugin = new StaticPlugin() {
370 public Optional<String> getScopeKey() {
371 return Optional.of("licenses/jira-service-desk");
372 }
373 };
374 ModuleDescriptor descriptor = makeSingletonDescriptor();
375 descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"true\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
376
377 assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
378 }
379
380 private StringModuleDescriptor newStringModuleDescriptor() {
381 return new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName());
382 }
383
384 private AbstractModuleDescriptor makeSingletonDescriptor() {
385 AbstractModuleDescriptor descriptor = new AbstractModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY) {
386 Object module;
387
388 public void init(Plugin plugin, Element element) throws PluginParseException {
389 super.init(plugin, element);
390 }
391
392 public Object getModule() {
393 try {
394 if (module == null) {
395 module = ClassLoaderUtils.loadClass(getModuleClass().getName(), TestAbstractModuleDescriptor.class).newInstance();
396 }
397
398 return module;
399 } catch (Exception e) {
400 throw new RuntimeException("What happened Dave?");
401 }
402 }
403 };
404 return descriptor;
405 }
406
407 @RequirePermission(ANNOTATION_PERMISSION)
408 private static class StringModuleDescriptor extends AbstractModuleDescriptor<String> {
409 public StringModuleDescriptor(ModuleFactory moduleFactory, String className) {
410 super(moduleFactory);
411 moduleClassName = className;
412 }
413
414 @Override
415 public String getModule() {
416 return null;
417 }
418
419 }
420
421 private static class ExtendsNumberModuleDescriptor<T extends Number> extends AbstractModuleDescriptor<T> {
422 public ExtendsNumberModuleDescriptor(ModuleFactory moduleFactory, String className) {
423 super(moduleFactory);
424 moduleClassName = className;
425 }
426
427 @Override
428 public T getModule() {
429 return null;
430 }
431 }
432
433 private static class ExtendsNothingModuleDescriptor<T> extends AbstractModuleDescriptor<T> {
434 public ExtendsNothingModuleDescriptor(ModuleFactory moduleFactory, String className) {
435 super(moduleFactory);
436 moduleClassName = className;
437 }
438
439 @Override
440 public T getModule() {
441 return null;
442 }
443 }
444 }