View Javadoc
1   /*
2    * Created by IntelliJ IDEA.
3    * User: Mike
4    * Date: Jul 29, 2004
5    * Time: 4:28:18 PM
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          // now succeed
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              // success
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             // success
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             // success
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         // enable the module descriptor first; otherwise, disabled() won't be called from destroy()
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         // it's never enabled, so disabled() won't be called
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 }