001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006package org.fcrepo.kernel.impl.services;
007
008import static org.apache.jena.vocabulary.DC_11.title;
009import static org.junit.Assert.assertEquals;
010import static org.junit.Assert.assertTrue;
011import static org.mockito.ArgumentMatchers.any;
012import static org.mockito.ArgumentMatchers.nullable;
013import static org.mockito.Mockito.times;
014import static org.mockito.Mockito.verify;
015import static org.mockito.Mockito.when;
016import static org.springframework.test.util.ReflectionTestUtils.setField;
017
018import java.time.Instant;
019import java.util.List;
020
021import org.fcrepo.config.FedoraPropsConfig;
022import org.fcrepo.config.ServerManagedPropsMode;
023import org.fcrepo.kernel.api.RdfCollectors;
024import org.fcrepo.kernel.api.RdfLexicon;
025import org.fcrepo.kernel.api.RdfStream;
026import org.fcrepo.kernel.api.Transaction;
027import org.fcrepo.kernel.api.cache.UserTypesCache;
028import org.fcrepo.kernel.api.identifiers.FedoraId;
029import org.fcrepo.kernel.api.models.ResourceHeaders;
030import org.fcrepo.kernel.api.observer.EventAccumulator;
031import org.fcrepo.kernel.api.operations.NonRdfSourceOperationFactory;
032import org.fcrepo.kernel.api.operations.RdfSourceOperation;
033import org.fcrepo.kernel.api.operations.RdfSourceOperationFactory;
034import org.fcrepo.kernel.api.services.MembershipService;
035import org.fcrepo.kernel.api.services.ReferenceService;
036import org.fcrepo.kernel.impl.operations.NonRdfSourceOperationFactoryImpl;
037import org.fcrepo.kernel.impl.operations.RdfSourceOperationFactoryImpl;
038import org.fcrepo.kernel.impl.operations.UpdateRdfSourceOperation;
039import org.fcrepo.persistence.api.PersistentStorageSession;
040import org.fcrepo.persistence.api.PersistentStorageSessionManager;
041
042import org.apache.commons.io.IOUtils;
043import org.apache.jena.rdf.model.Model;
044import org.apache.jena.rdf.model.ModelFactory;
045import org.apache.jena.rdf.model.ResourceFactory;
046import org.apache.jena.riot.Lang;
047import org.apache.jena.riot.RDFDataMgr;
048import org.fcrepo.search.api.SearchIndex;
049import org.junit.Before;
050import org.junit.Test;
051import org.junit.runner.RunWith;
052import org.mockito.ArgumentCaptor;
053import org.mockito.Captor;
054import org.mockito.InjectMocks;
055import org.mockito.Mock;
056import org.mockito.junit.MockitoJUnitRunner;
057
058/**
059 * DeleteResourceServiceTest
060 *
061 * @author bseeger
062 */
063@RunWith(MockitoJUnitRunner.Strict.class)
064public class ReplacePropertiesServiceImplTest {
065
066    private static final String USER_PRINCIPAL = "fedoraUser";
067
068    @Mock
069    private Transaction tx;
070
071    @Mock
072    private PersistentStorageSession pSession;
073
074    @Mock
075    private PersistentStorageSessionManager psManager;
076
077    @Mock
078    private EventAccumulator eventAccumulator;
079
080    @Mock
081    private ReferenceService referenceService;
082
083    @Mock
084    private MembershipService membershipService;
085
086    @Mock
087    private SearchIndex searchIndex;
088
089    @Mock
090    private ResourceHeaders headers;
091
092    @Mock
093    private UserTypesCache userTypesCache;
094
095    @InjectMocks
096    private UpdateRdfSourceOperation operation;
097
098    @InjectMocks
099    private ReplacePropertiesServiceImpl service;
100
101    private RdfSourceOperationFactory factory;
102
103    private NonRdfSourceOperationFactory nonRdfFactory;
104
105    @Captor
106    private ArgumentCaptor<RdfSourceOperation> operationCaptor;
107
108    private FedoraPropsConfig propsConfig;
109
110    private static final FedoraId FEDORA_ID = FedoraId.create("info:fedora/resource1");
111    private static final String TX_ID = "tx-1234";
112    private static final String RDF =
113            "<" + FEDORA_ID + "> <" + title + "> 'fancy title' .\n" +
114            "<" + FEDORA_ID + "> <" + title + "> 'another fancy title' .";
115
116    @Before
117    public void setup() {
118        propsConfig = new FedoraPropsConfig();
119        factory = new RdfSourceOperationFactoryImpl();
120        nonRdfFactory = new NonRdfSourceOperationFactoryImpl();
121        setField(service, "factory", factory);
122        setField(service, "nonRdfFactory", nonRdfFactory);
123        setField(service, "eventAccumulator", eventAccumulator);
124        setField(service, "referenceService", referenceService);
125        setField(service, "membershipService", membershipService);
126        setField(service, "searchIndex", searchIndex);
127        setField(service, "fedoraPropsConfig", propsConfig);
128        setField(service, "userTypesCache", userTypesCache);
129        propsConfig.setServerManagedPropsMode(ServerManagedPropsMode.STRICT);
130        when(psManager.getSession(any(Transaction.class))).thenReturn(pSession);
131        when(pSession.getHeaders(any(FedoraId.class), nullable(Instant.class))).thenReturn(headers);
132    }
133
134    @Test
135    public void testReplaceProperties() throws Exception {
136        final Model model = ModelFactory.createDefaultModel();
137        RDFDataMgr.read(model, IOUtils.toInputStream(RDF, "UTF-8"), Lang.NTRIPLES);
138
139        when(headers.getInteractionModel()).thenReturn(RdfLexicon.RDF_SOURCE.toString());
140
141        service.perform(tx, USER_PRINCIPAL, FEDORA_ID, model);
142        verify(tx).lockResource(FEDORA_ID);
143        verify(pSession).persist(operationCaptor.capture());
144        assertEquals(FEDORA_ID, operationCaptor.getValue().getResourceId());
145        final RdfStream stream = operationCaptor.getValue().getTriples();
146        final Model captureModel = stream.collect(RdfCollectors.toModel());
147
148        assertTrue(captureModel.contains(ResourceFactory.createResource(FEDORA_ID.getResourceId()),
149                ResourceFactory.createProperty(title.getURI()),
150                "another fancy title"));
151        assertTrue(captureModel.contains(ResourceFactory.createResource(FEDORA_ID.getResourceId()),
152                ResourceFactory.createProperty(title.getURI()),
153                "fancy title"));
154    }
155
156    @Test
157    public void lockRelatedResourcesOnBinaryDescInAg() throws Exception {
158        final Model model = ModelFactory.createDefaultModel();
159        RDFDataMgr.read(model, IOUtils.toInputStream(RDF, "UTF-8"), Lang.NTRIPLES);
160
161        final var agId = FedoraId.create("ag");
162        final var binaryId = agId.resolve("bin");
163        final var descId = binaryId.asDescription();
164
165        when(headers.getInteractionModel()).thenReturn(RdfLexicon.FEDORA_NON_RDF_SOURCE_DESCRIPTION_URI);
166        when(headers.getArchivalGroupId()).thenReturn(agId);
167
168        service.perform(tx, USER_PRINCIPAL, descId, model);
169        verify(tx).lockResource(agId);
170        verify(tx).lockResource(binaryId);
171        verify(tx).lockResource(descId);
172        verify(pSession, times(2)).persist(operationCaptor.capture());
173
174        final var updateOp = getOperation(operationCaptor.getAllValues(), UpdateRdfSourceOperation.class);
175        assertEquals(descId, updateOp.getResourceId());
176    }
177
178    private <T extends RdfSourceOperation> T getOperation(final List<RdfSourceOperation> ops, final Class<T> clazz) {
179        return ops.stream()
180            .filter(clazz::isInstance)
181            .findFirst()
182            .map(clazz::cast)
183            .orElseThrow();
184    }
185
186}
187