001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.kernel.impl.models;
019
020import org.apache.jena.graph.Node;
021import org.apache.jena.graph.NodeFactory;
022import org.apache.jena.graph.Triple;
023import org.fcrepo.kernel.api.RdfLexicon;
024import org.fcrepo.kernel.api.Transaction;
025import org.fcrepo.kernel.api.exception.PathNotFoundException;
026import org.fcrepo.kernel.api.identifiers.FedoraId;
027import org.fcrepo.kernel.api.models.FedoraResource;
028import org.fcrepo.kernel.api.models.ResourceFactory;
029import org.fcrepo.kernel.api.services.VersionService;
030import org.fcrepo.persistence.api.PersistentStorageSession;
031import org.fcrepo.persistence.api.PersistentStorageSessionManager;
032import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
033import org.junit.Before;
034import org.junit.Test;
035import org.junit.runner.RunWith;
036import org.mockito.Mock;
037import org.mockito.junit.MockitoJUnitRunner;
038
039import java.net.URI;
040import java.time.Instant;
041import java.util.ArrayList;
042import java.util.Collections;
043import java.util.List;
044import java.util.UUID;
045import java.util.stream.Collectors;
046
047import static org.fcrepo.kernel.api.FedoraTypes.FCR_VERSIONS;
048import static org.fcrepo.kernel.api.FedoraTypes.FEDORA_ID_PREFIX;
049import static org.hamcrest.Matchers.contains;
050import static org.junit.Assert.assertEquals;
051import static org.junit.Assert.assertSame;
052import static org.junit.Assert.assertThat;
053import static org.junit.Assert.assertTrue;
054import static org.mockito.Mockito.mock;
055import static org.mockito.Mockito.when;
056
057/**
058 * @author pwinckles
059 */
060@RunWith(MockitoJUnitRunner.Silent.class)
061public class TimeMapImplTest {
062
063    @Mock
064    private PersistentStorageSessionManager sessionManager;
065
066    @Mock
067    private PersistentStorageSession session;
068
069    @Mock
070    private ResourceFactory resourceFactory;
071
072    @Mock
073    private Transaction transaction;
074
075    private final String defaultId = FEDORA_ID_PREFIX + "/resource";
076
077    private TimeMapImpl timeMap;
078
079    @Before
080    public void setup() {
081        when(transaction.getId()).thenReturn(UUID.randomUUID().toString());
082        when(transaction.isShortLived()).thenReturn(true);
083        timeMap = createTimeMap(defaultId);
084
085        when(sessionManager.getReadOnlySession()).thenReturn(session);
086    }
087
088    @Test
089    public void copyParentPropsWhenCreatingTimeMap() {
090        final var parent = createParent(FEDORA_ID_PREFIX + "id");
091        final var timeMap = new TimeMapImpl(parent, transaction, sessionManager, resourceFactory);
092
093        assertEquals(parent.getId(), timeMap.getId());
094        assertEquals(parent.getCreatedBy(), timeMap.getCreatedBy());
095        assertEquals(parent.getCreatedDate(), timeMap.getCreatedDate());
096        assertEquals(parent.getLastModifiedBy(), timeMap.getLastModifiedBy());
097        assertEquals(parent.getLastModifiedDate(), timeMap.getLastModifiedDate());
098        assertEquals(parent.getEtagValue(), timeMap.getEtagValue());
099        assertEquals(parent.getStateToken(), timeMap.getStateToken());
100        assertSame(parent, timeMap.getOriginalResource());
101        assertSame(timeMap, timeMap.getTimeMap());
102    }
103
104    @Test
105    public void shouldHaveTimeMapTypes() {
106        assertTrue(timeMap.getTypes().containsAll(
107                List.of(
108                URI.create(RdfLexicon.VERSIONING_TIMEMAP.getURI())
109                ))
110        );
111    }
112
113    @Test
114    public void returnChildMementosWhenExist() throws PersistentStorageException, PathNotFoundException {
115        final var version1 = instant("20200225131900");
116        final var version2 = instant("20200226131900");
117
118        mockListVersions(defaultId, version1, version2);
119
120        final var children = timeMap.getChildren();
121
122        assertTrue(children.map(FedoraResource::getMementoDatetime)
123                .collect(Collectors.toList()).containsAll(List.of(version1, version2)));
124    }
125
126    @Test
127    public void returnNoMementosWhenNoneExist() throws PersistentStorageException, PathNotFoundException {
128        mockListVersions(defaultId);
129
130        final var children = timeMap.getChildren();
131
132        assertEquals(0, children.count());
133    }
134
135    @Test
136    public void returnTriples() throws PersistentStorageException, PathNotFoundException {
137        final var version1 = instant("20200225131900");
138        final var version2 = instant("20200226131900");
139
140        final var mementos = mockListVersions(defaultId, version1, version2);
141
142        final var triples = timeMap.getTriples();
143
144        final var timeMapUri = node(timeMap);
145
146        assertThat(triples.collect(Collectors.toList()), contains(
147                Triple.create(timeMapUri, RdfLexicon.CONTAINS.asNode(), node(mementos.get(1))),
148                Triple.create(timeMapUri, RdfLexicon.CONTAINS.asNode(), node(mementos.get(0))),
149                Triple.create(timeMapUri, RdfLexicon.MEMENTO_ORIGINAL_RESOURCE.asNode(),
150                        NodeFactory.createURI(defaultId))));
151    }
152
153    private List<FedoraResource> mockListVersions(final String id, final Instant... versions)
154            throws PersistentStorageException, PathNotFoundException {
155        if (versions.length == 0) {
156            when(session.listVersions(FedoraId.create(id))).thenReturn(Collections.emptyList());
157        } else {
158            when(session.listVersions(FedoraId.create(id))).thenReturn(List.of(versions));
159        }
160
161        final var mementos = new ArrayList<FedoraResource>();
162
163        for (final var version : versions) {
164            final var memento = createMemento(id, version);
165            mementos.add(memento);
166            final FedoraId mementoID = FedoraId.create(id, FCR_VERSIONS, instantStr(version));
167            when(memento.getFedoraId()).thenReturn(mementoID);
168            when(resourceFactory.getResource(transaction, mementoID)).thenReturn(memento);
169        }
170        return mementos;
171    }
172
173    private Node node(final FedoraResource memento) {
174        return NodeFactory.createURI(memento.getFedoraId().getFullId());
175    }
176
177    private Instant instant(final String instantStr) {
178        return Instant.from(VersionService.MEMENTO_LABEL_FORMATTER.parse(instantStr));
179    }
180
181    private String instantStr(final Instant instant) {
182        return VersionService.MEMENTO_LABEL_FORMATTER.format(instant);
183    }
184
185    private FedoraResource createMemento(final String id, final Instant version) {
186        final var mock = mock(FedoraResource.class);
187        when(mock.getId()).thenReturn(id);
188        when(mock.isMemento()).thenReturn(true);
189        when(mock.getMementoDatetime()).thenReturn(version);
190        return mock;
191    }
192
193    private TimeMapImpl createTimeMap(final String id) {
194        return new TimeMapImpl(createParent(id), transaction, sessionManager, resourceFactory);
195    }
196
197    private FedoraResource createParent(final String id) {
198        final var fedoraId = FedoraId.create(id);
199        final var parent = new ContainerImpl(fedoraId, transaction, sessionManager, resourceFactory);
200
201        parent.setCreatedBy("createdBy");
202        parent.setCreatedDate(Instant.now());
203        parent.setLastModifiedBy("modifiedBy");
204        parent.setLastModifiedDate(Instant.now());
205        parent.setEtag("etag");
206        parent.setStateToken("stateToken");
207
208        return parent;
209    }
210
211}