/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.isis.core.wrapper;
import java.lang.reflect.Method;
import java.util.Collections;
import org.jmock.Expectations;
import org.jmock.auto.Mock;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.apache.isis.applib.services.command.Command;
import org.apache.isis.applib.services.command.CommandContext;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.wrapper.DisabledException;
import org.apache.isis.applib.services.wrapper.HiddenException;
import org.apache.isis.applib.services.wrapper.InvalidException;
import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
import org.apache.isis.core.commons.config.IsisConfiguration;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.deployment.DeploymentCategory;
import org.apache.isis.core.metamodel.deployment.DeploymentCategoryProvider;
import org.apache.isis.core.metamodel.facetapi.FacetUtil;
import org.apache.isis.core.metamodel.facets.FacetedMethod;
import org.apache.isis.core.metamodel.facets.all.named.NamedFacetInferred;
import org.apache.isis.core.metamodel.facets.members.disabled.method.DisableForContextFacetViaMethod;
import org.apache.isis.core.metamodel.facets.members.hidden.method.HideForContextFacetViaMethod;
import org.apache.isis.core.metamodel.facets.properties.accessor.PropertyAccessorFacetViaAccessor;
import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyClearFacetViaClearMethod;
import org.apache.isis.core.metamodel.facets.properties.update.init.PropertyInitializationFacetViaSetterMethod;
import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacetViaModifyMethod;
import org.apache.isis.core.metamodel.facets.properties.validating.method.PropertyValidateFacetViaMethod;
import org.apache.isis.core.metamodel.services.ServicesInjector;
import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal;
import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationDefault;
import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
import org.apache.isis.core.runtime.authentication.standard.SimpleSession;
import org.apache.isis.core.runtime.services.command.CommandDtoServiceInternalDefault;
import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
import org.apache.isis.progmodel.wrapper.dom.employees.Employee;
import org.apache.isis.progmodel.wrapper.dom.employees.EmployeeRepository;
import org.apache.isis.progmodel.wrapper.dom.employees.EmployeeRepositoryImpl;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
public class WrapperFactoryDefaultTest_wrappedObject {
@Rule
public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Mock
private AdapterManager mockAdapterManager;
@Mock
private AuthenticationSessionProvider mockAuthenticationSessionProvider;
@Mock
private PersistenceSessionServiceInternal mockPersistenceSessionServiceInternal;
@Mock
private MessageService mockMessageService;
@Mock
private ServicesInjector mockServicesInjector;
@Mock
private CommandContext mockCommandContext;
@Mock
private Command mockCommand;
@Mock
private CommandDtoServiceInternal mockCommandDtoServiceInternal;
@Mock
private SpecificationLoader mockSpecificationLoader;
@Mock
private DeploymentCategoryProvider mockDeploymentCategoryProvider;
@Mock
private IsisConfiguration mockConfiguration;
@Mock
private IsisSessionFactory mockIsisSessionFactory;
@Mock
private ObjectSpecificationDefault mockEmployeeSpec;
private ObjectMember employeeNameMember;
@Mock
private ObjectSpecificationDefault mockStringSpec;
@Mock
private ObjectAdapter mockEmployeeAdapter;
@Mock
private ObjectAdapter mockAdapterForStringSmith;
@Mock
private ObjectAdapter mockAdapterForStringJones;
private final SimpleSession session = new SimpleSession("tester", Collections.<String>emptyList());
private EmployeeRepository employeeRepository;
private Employee employeeDO;
private Employee employeeWO;
private WrapperFactoryDefault wrapperFactory;
@Before
public void setUp() {
employeeRepository = new EmployeeRepositoryImpl();
employeeDO = new Employee();
employeeDO.setName("Smith");
employeeDO.setEmployeeRepository(employeeRepository);
context.checking(new Expectations() {
{
allowing(mockEmployeeAdapter).getOid();
will(returnValue(RootOid.create(ObjectSpecId.of("EMP"), "1")));
allowing(mockEmployeeSpec).getCorrespondingClass();
will(returnValue(Employee.class));
allowing(mockStringSpec).getCorrespondingClass();
will(returnValue(String.class));
allowing(mockServicesInjector).lookupService(CommandContext.class);
will(returnValue(mockCommandContext));
allowing(mockCommandContext).getCommand();
will(returnValue(mockCommand));
allowing(mockServicesInjector).lookupService(CommandDtoServiceInternal.class);
will(returnValue(new CommandDtoServiceInternalDefault()));
allowing(mockServicesInjector).lookupService(AuthenticationSessionProvider.class);
will(returnValue(mockAuthenticationSessionProvider));
allowing(mockDeploymentCategoryProvider).getDeploymentCategory();
will(returnValue(DeploymentCategory.PRODUCTION));
allowing(mockServicesInjector).getSpecificationLoader();
will(returnValue(mockSpecificationLoader));
allowing(mockSpecificationLoader).loadSpecification(String.class);
will(returnValue(mockStringSpec));
allowing(mockStringSpec).getShortIdentifier();
will(returnValue(String.class.getName()));
allowing(mockDeploymentCategoryProvider).getDeploymentCategory();
will(returnValue(DeploymentCategory.PRODUCTION));
allowing(mockAuthenticationSessionProvider).getAuthenticationSession();
will(returnValue(session));
allowing(mockAdapterManager).getAdapterFor(employeeDO);
will(returnValue(mockEmployeeAdapter));
allowing(mockAdapterManager).adapterFor(employeeDO);
will(returnValue(mockEmployeeAdapter));
allowing(mockEmployeeAdapter).getSpecification();
will(returnValue(mockEmployeeSpec));
allowing(mockSpecificationLoader).loadSpecification(Employee.class);
will(returnValue(mockEmployeeSpec));
allowing(mockEmployeeSpec).getMember(methodOf(Employee.class, "getEmployeeRepository"));
will(returnValue(null));
}
});
wrapperFactory = createWrapperFactory();
wrapperFactory.persistenceSessionServiceInternal = mockPersistenceSessionServiceInternal;
wrapperFactory.isisSessionFactory = mockIsisSessionFactory;
wrapperFactory.authenticationSessionProvider = mockAuthenticationSessionProvider;
final Method employeeGetNameMethod = methodOf(Employee.class, "getName");
final Method employeeSetNameMethod = methodOf(Employee.class, "setName", String.class);
final Method employeeModifyNameMethod = methodOf(Employee.class, "modifyName", String.class);
final Method employeeHideNameMethod = methodOf(Employee.class, "hideName");
final Method employeeDisableNameMethod = methodOf(Employee.class, "disableName");
final Method employeeValidateNameMethod = methodOf(Employee.class, "validateName", String.class);
final Method employeeClearNameMethod = methodOf(Employee.class, "clearName");
employeeNameMember = new OneToOneAssociationDefault(
facetedMethodForProperty(
employeeSetNameMethod, employeeGetNameMethod, employeeModifyNameMethod, employeeClearNameMethod, employeeHideNameMethod, employeeDisableNameMethod, employeeValidateNameMethod),
mockServicesInjector);
context.checking(new Expectations() {
{
allowing(mockEmployeeSpec).getMember(employeeGetNameMethod);
will(returnValue(employeeNameMember));
allowing(mockEmployeeSpec).getMember(employeeSetNameMethod);
will(returnValue(employeeNameMember));
allowing(mockEmployeeSpec).getMember(employeeModifyNameMethod);
will(returnValue(employeeNameMember));
allowing(mockEmployeeSpec).getMember(employeeClearNameMethod);
will(returnValue(employeeNameMember));
allowing(mockEmployeeAdapter).getObject();
will(returnValue(employeeDO));
allowing(mockEmployeeAdapter).representsPersistent();
will(returnValue(true));
}
});
employeeWO = wrapperFactory.wrap(employeeDO);
}
protected WrapperFactoryDefault createWrapperFactory() {
return new WrapperFactoryDefault();
}
@Test
public void shouldWrapDomainObject() {
// then
assertThat(employeeWO, is(notNullValue()));
}
@Test
public void shouldBeAbleToInjectIntoDomainObjects() {
assertThat(employeeDO.getEmployeeRepository(), is(notNullValue()));
}
@Test
public void cannotAccessMethodNotCorrespondingToMember() {
expectedException.expectMessage(
"Method 'getEmployeeRepository' being invoked does not correspond to any of the object's fields or actions.");
// then
assertThat(employeeWO.getEmployeeRepository(), is(notNullValue()));
}
@Test
public void shouldBeAbleToReadVisibleProperty() {
allowingEmployeeHasSmithAdapter();
context.checking(new Expectations() {{
oneOf(mockConfiguration).getBoolean("isis.reflector.facet.filterVisibility", true);
will(returnValue(true));
allowing(mockAdapterForStringSmith).getSpecification();
will(returnValue(mockStringSpec));
ignoring(mockStringSpec);
allowing(mockAdapterForStringSmith).isDestroyed();
will(returnValue(false));
allowing(mockPersistenceSessionServiceInternal).adapterFor("Smith");
will(returnValue(mockAdapterForStringSmith));
}});
// then
assertThat(employeeWO.getName(), is(employeeDO.getName()));
}
@Test
public void shouldNotBeAbleToViewHiddenProperty() {
expectedException.expect(HiddenException.class);
allowingEmployeeHasSmithAdapter();
// given
employeeDO.whetherHideName = true;
// when
employeeWO.getName();
// then should throw exception
}
@Test
public void shouldBeAbleToModifyEnabledPropertyUsingSetter() {
allowingJonesStringValueAdapter();
context.checking(new Expectations() {
{
allowing(mockAdapterForStringJones).titleString(null);
ignoring(mockCommand);
oneOf(mockConfiguration).getBoolean("isis.reflector.facet.filterVisibility", true);
will(returnValue(true));
allowing(mockAdapterForStringJones).isDestroyed();
will(returnValue(false));
allowing(mockAdapterForStringJones).getSpecification();
will(returnValue(mockStringSpec));
allowing(mockPersistenceSessionServiceInternal).adapterFor("Jones");
will(returnValue(mockAdapterForStringJones));
ignoring(mockStringSpec);
}
});
// when
employeeWO.setName("Jones");
// then
assertThat(employeeDO.getName(), is("Jones"));
assertThat(employeeWO.getName(), is(employeeDO.getName()));
}
@Test
public void shouldNotBeAbleToModifyDisabledProperty() {
expectedException.expect(DisabledException.class);
// given
employeeDO.reasonDisableName = "sorry, no change allowed";
// when
employeeWO.setName("Jones");
// then should throw exception
}
@Test
public void shouldNotBeAbleToModifyPropertyUsingModify() {
allowingJonesStringValueAdapter();
expectedException.expect(UnsupportedOperationException.class);
// when
employeeWO.modifyName("Jones");
// then should throw exception
}
@Test
public void shouldNotBeAbleToModifyPropertyUsingClear() {
expectedException.expect(UnsupportedOperationException.class);
// when
employeeWO.clearName();
// then should throw exception
}
@Test
public void shouldNotBeAbleToModifyPropertyIfInvalid() {
allowingJonesStringValueAdapter();
expectedException.expect(InvalidException.class);
// given
employeeDO.reasonValidateName = "sorry, invalid data";
// when
employeeWO.setName("Jones");
// then should throw exception
}
// //////////////////////////////////////
private FacetedMethod facetedMethodForProperty(
Method init, Method accessor, Method modify, Method clear, Method hide, Method disable, Method validate) {
FacetedMethod facetedMethod = FacetedMethod.createForProperty(accessor.getDeclaringClass(), accessor);
FacetUtil.addFacet(new PropertyAccessorFacetViaAccessor(accessor, facetedMethod, mockDeploymentCategoryProvider.getDeploymentCategory(),
mockConfiguration, mockSpecificationLoader,
mockAuthenticationSessionProvider, mockAdapterManager
));
FacetUtil.addFacet(new PropertyInitializationFacetViaSetterMethod(init, facetedMethod));
FacetUtil.addFacet(new PropertySetterFacetViaModifyMethod(modify, facetedMethod));
FacetUtil.addFacet(new PropertyClearFacetViaClearMethod(clear, facetedMethod));
FacetUtil.addFacet(new HideForContextFacetViaMethod(hide, facetedMethod));
FacetUtil.addFacet(new DisableForContextFacetViaMethod(disable, null, null, facetedMethod));
FacetUtil.addFacet(new PropertyValidateFacetViaMethod(validate, null, null, facetedMethod));
FacetUtil.addFacet(new NamedFacetInferred(accessor.getName(), facetedMethod));
return facetedMethod;
}
private static Method methodOf(Class<?> cls, String methodName) {
return methodOf(cls, methodName, new Class<?>[]{});
}
private static Method methodOf(Class<?> cls, String methodName, Class<?>... args) {
try {
return cls.getMethod(methodName, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// //////////////////////////////////////
private void allowingEmployeeHasSmithAdapter() {
context.checking(new Expectations() {
{
allowing(mockAdapterManager).adapterFor("Smith");
will(returnValue(mockAdapterForStringSmith));
allowing(mockAdapterForStringSmith).getObject();
will(returnValue("Smith"));
}
});
}
private void allowingJonesStringValueAdapter() {
context.checking(new Expectations() {
{
allowing(mockAdapterManager).adapterFor("Jones");
will(returnValue(mockAdapterForStringJones));
allowing(mockAdapterForStringJones).getObject();
will(returnValue("Jones"));
allowing(mockAdapterForStringJones).isTransient();
will(returnValue(false));
}
});
}
}