package uk.co.blackpepper.bowman; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; import org.springframework.hateoas.Links; import uk.co.blackpepper.bowman.annotation.RemoteResource; import uk.co.blackpepper.bowman.annotation.ResourceTypeInfo; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; public class DefaultTypeResolverTest { public static class TypeWithoutInfo { // no members } @ResourceTypeInfo public static class TypeWithUnderspecifiedInfo { // no members } @ResourceTypeInfo(subtypes = Object.class, typeResolver = Resolver.class) public static class TypeWithOverspecifiedInfo { // no members } @ResourceTypeInfo(subtypes = {TypeWithSubtypesSubtype1.class, TypeWithSubtypesSubtype2.class}) public static class TypeWithSubtypes { // no members } @RemoteResource("/1") public static class TypeWithSubtypesSubtype1 extends TypeWithSubtypes { // no members } @RemoteResource("/2") public static class TypeWithSubtypesSubtype2 extends TypeWithSubtypes { // no members } @ResourceTypeInfo(subtypes = TypeWithNonRemoteResourceSubtypeSubtype.class) public static class TypeWithNonRemoteResourceSubtype { // no members } public static class TypeWithNonRemoteResourceSubtypeSubtype extends TypeWithNonRemoteResourceSubtype { // no members } @ResourceTypeInfo(subtypes = TypeWithIllegalSubtypeSubtype.class) public static class TypeWithIllegalSubtype { // no members } @RemoteResource("/x") public static class TypeWithIllegalSubtypeSubtype { // no members } @ResourceTypeInfo(typeResolver = Resolver.class) public static class TypeWithResolver { // no members } public static class Resolver implements TypeResolver { // CHECKSTYLE:OFF public static TypeResolver mockResolver = mock(TypeResolver.class); // CHECKSTYLE:ON @Override public <T> Class<? extends T> resolveType(Class<T> declaredType, Links resourceLinks, Configuration configuration) { return mockResolver.resolveType(declaredType, resourceLinks, configuration); } } public static class TypeWithResolverSubtype extends TypeWithResolver { // no members } private DefaultTypeResolver resolver; private ExpectedException thrown = ExpectedException.none(); @Rule public ExpectedException getThrown() { return thrown; } @Before public void setUp() { resolver = new DefaultTypeResolver(); } @Test public void resolveTypeWithNoResourceTypeInfoReturnsDeclaredType() { Class<?> type = resolver.resolveType(TypeWithoutInfo.class, Links.of(new Link("http://x", IanaLinkRelations.SELF)), Configuration.build()); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithoutInfo.class)); } @Test public void resolveTypeWithUnderspecifiedResourceTypeInfoThrowsException() { thrown.expect(ClientProxyException.class); thrown.expectMessage("one of subtypes or typeResolver must be specified"); resolver.resolveType(TypeWithUnderspecifiedInfo.class, Links.of(), Configuration.build()); } @Test public void resolveTypeWithOverspecifiedResourceTypeInfoThrowsException() { thrown.expect(ClientProxyException.class); thrown.expectMessage("one of subtypes or typeResolver must be specified"); resolver.resolveType(TypeWithOverspecifiedInfo.class, Links.of(), Configuration.build()); } @Test public void resolveTypeWithSubtypesAndNoSelfLinkReturnsDeclaredType() { Class<?> type = resolver.resolveType(TypeWithSubtypes.class, Links.of(), Configuration.build()); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithSubtypes.class)); } @Test public void resolveTypeWithSubtypesAndNoMatchingSubtypeReturnsDeclaredType() { Class<?> type = resolver.resolveType(TypeWithSubtypes.class, Links.of(new Link("http://x", IanaLinkRelations.SELF)), Configuration.build()); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithSubtypes.class)); } @Test public void resolveTypeWithSubtypesAndMatchingAbsoluteUriSubtypeReturnsSubtype() { Configuration config = Configuration.builder().setBaseUri("http://x.com").build(); Class<?> type = resolver.resolveType(TypeWithSubtypes.class, Links.of(new Link("http://x.com/2/1", IanaLinkRelations.SELF)), config); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithSubtypesSubtype2.class)); } @Test public void resolveTypeWithSubtypesAndMatchingAbsolutePathReferenceUriSubtypeReturnsSubtype() { Configuration config = Configuration.builder().setBaseUri("http://x.com").build(); Class<?> type = resolver.resolveType(TypeWithSubtypes.class, Links.of(new Link("/2/1", IanaLinkRelations.SELF)), config); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithSubtypesSubtype2.class)); } @Test public void resolveTypeWithNonRemoteResourceSubtypeThrowsException() { thrown.expect(ClientProxyException.class); thrown.expectMessage(TypeWithNonRemoteResourceSubtypeSubtype.class.getName() + " is not annotated with @RemoteResource"); resolver.resolveType(TypeWithNonRemoteResourceSubtype.class, Links.of(new Link("/", IanaLinkRelations.SELF)), Configuration.build()); } @Test public void resolveTypeWithIllegalSubtypeThrowsException() { thrown.expect(ClientProxyException.class); thrown.expectMessage(TypeWithIllegalSubtypeSubtype.class.getName() + " is not a subtype of " + TypeWithIllegalSubtype.class.getName()); resolver.resolveType(TypeWithIllegalSubtype.class, Links.of(new Link("/x/1", IanaLinkRelations.SELF)), Configuration.build()); } @Test public void resolveTypeWithResolverReturnsResolvedType() { Links links = Links.of(new Link("_")); Configuration config = Configuration.build(); doReturn(TypeWithResolverSubtype.class) .when(Resolver.mockResolver).resolveType(TypeWithResolver.class, links, config); Class<?> type = resolver.resolveType(TypeWithResolver.class, links, config); assertThat(type, Matchers.<Class<?>>equalTo(TypeWithResolverSubtype.class)); } }