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.models;
007
008import org.apache.jena.graph.Node;
009import org.apache.jena.graph.NodeFactory;
010import org.apache.jena.graph.Triple;
011import org.fcrepo.kernel.api.RdfLexicon;
012import org.fcrepo.kernel.api.Transaction;
013import org.fcrepo.kernel.api.exception.PathNotFoundException;
014import org.fcrepo.kernel.api.identifiers.FedoraId;
015import org.fcrepo.kernel.api.models.FedoraResource;
016import org.fcrepo.kernel.api.models.ResourceFactory;
017import org.fcrepo.kernel.api.services.VersionService;
018import org.fcrepo.persistence.api.PersistentStorageSession;
019import org.fcrepo.persistence.api.PersistentStorageSessionManager;
020import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
021import org.junit.Before;
022import org.junit.Test;
023import org.junit.runner.RunWith;
024import org.mockito.Mock;
025import org.mockito.junit.MockitoJUnitRunner;
026
027import java.net.URI;
028import java.time.Instant;
029import java.util.ArrayList;
030import java.util.Collections;
031import java.util.List;
032import java.util.UUID;
033import java.util.stream.Collectors;
034
035import static org.fcrepo.kernel.api.FedoraTypes.FCR_VERSIONS;
036import static org.fcrepo.kernel.api.FedoraTypes.FEDORA_ID_PREFIX;
037import static org.hamcrest.Matchers.contains;
038import static org.junit.Assert.assertEquals;
039import static org.junit.Assert.assertSame;
040import static org.junit.Assert.assertThat;
041import static org.junit.Assert.assertTrue;
042import static org.mockito.Mockito.mock;
043import static org.mockito.Mockito.when;
044
045/**
046 * @author pwinckles
047 */
048@RunWith(MockitoJUnitRunner.Silent.class)
049public class TimeMapImplTest {
050
051    @Mock
052    private PersistentStorageSessionManager sessionManager;
053
054    @Mock
055    private PersistentStorageSession session;
056
057    @Mock
058    private ResourceFactory resourceFactory;
059
060    @Mock
061    private Transaction transaction;
062
063    private final String defaultId = FEDORA_ID_PREFIX + "/resource";
064
065    private TimeMapImpl timeMap;
066
067    @Before
068    public void setup() {
069        when(transaction.getId()).thenReturn(UUID.randomUUID().toString());
070        when(transaction.isShortLived()).thenReturn(true);
071        timeMap = createTimeMap(defaultId);
072
073        when(sessionManager.getReadOnlySession()).thenReturn(session);
074    }
075
076    @Test
077    public void copyParentPropsWhenCreatingTimeMap() {
078        final var parent = createParent(FEDORA_ID_PREFIX + "id");
079        final var timeMap = new TimeMapImpl(parent, transaction, sessionManager, resourceFactory);
080
081        assertEquals(parent.getId(), timeMap.getId());
082        assertEquals(parent.getCreatedBy(), timeMap.getCreatedBy());
083        assertEquals(parent.getCreatedDate(), timeMap.getCreatedDate());
084        assertEquals(parent.getLastModifiedBy(), timeMap.getLastModifiedBy());
085        assertEquals(parent.getLastModifiedDate(), timeMap.getLastModifiedDate());
086        assertEquals(parent.getEtagValue(), timeMap.getEtagValue());
087        assertEquals(parent.getStateToken(), timeMap.getStateToken());
088        assertSame(parent, timeMap.getOriginalResource());
089        assertSame(timeMap, timeMap.getTimeMap());
090    }
091
092    @Test
093    public void shouldHaveTimeMapTypes() {
094        assertTrue(timeMap.getTypes().containsAll(
095                List.of(
096                URI.create(RdfLexicon.VERSIONING_TIMEMAP.getURI())
097                ))
098        );
099    }
100
101    @Test
102    public void returnChildMementosWhenExist() throws PersistentStorageException, PathNotFoundException {
103        final var version1 = instant("20200225131900");
104        final var version2 = instant("20200226131900");
105
106        mockListVersions(defaultId, version1, version2);
107
108        final var children = timeMap.getChildren();
109
110        assertTrue(children.map(FedoraResource::getMementoDatetime)
111                .collect(Collectors.toList()).containsAll(List.of(version1, version2)));
112    }
113
114    @Test
115    public void returnNoMementosWhenNoneExist() throws PersistentStorageException, PathNotFoundException {
116        mockListVersions(defaultId);
117
118        final var children = timeMap.getChildren();
119
120        assertEquals(0, children.count());
121    }
122
123    @Test
124    public void returnTriples() throws PersistentStorageException, PathNotFoundException {
125        final var version1 = instant("20200225131900");
126        final var version2 = instant("20200226131900");
127
128        final var mementos = mockListVersions(defaultId, version1, version2);
129
130        final var triples = timeMap.getTriples();
131
132        final var timeMapUri = node(timeMap);
133
134        assertThat(triples.collect(Collectors.toList()), contains(
135                Triple.create(timeMapUri, RdfLexicon.CONTAINS.asNode(), node(mementos.get(1))),
136                Triple.create(timeMapUri, RdfLexicon.CONTAINS.asNode(), node(mementos.get(0))),
137                Triple.create(timeMapUri, RdfLexicon.MEMENTO_ORIGINAL_RESOURCE.asNode(),
138                        NodeFactory.createURI(defaultId))));
139    }
140
141    private List<FedoraResource> mockListVersions(final String id, final Instant... versions)
142            throws PersistentStorageException, PathNotFoundException {
143        if (versions.length == 0) {
144            when(session.listVersions(FedoraId.create(id))).thenReturn(Collections.emptyList());
145        } else {
146            when(session.listVersions(FedoraId.create(id))).thenReturn(List.of(versions));
147        }
148
149        final var mementos = new ArrayList<FedoraResource>();
150
151        for (final var version : versions) {
152            final var memento = createMemento(id, version);
153            mementos.add(memento);
154            final FedoraId mementoID = FedoraId.create(id, FCR_VERSIONS, instantStr(version));
155            when(memento.getFedoraId()).thenReturn(mementoID);
156            when(resourceFactory.getResource(transaction, mementoID)).thenReturn(memento);
157        }
158        return mementos;
159    }
160
161    private Node node(final FedoraResource memento) {
162        return NodeFactory.createURI(memento.getFedoraId().getFullId());
163    }
164
165    private Instant instant(final String instantStr) {
166        return Instant.from(VersionService.MEMENTO_LABEL_FORMATTER.parse(instantStr));
167    }
168
169    private String instantStr(final Instant instant) {
170        return VersionService.MEMENTO_LABEL_FORMATTER.format(instant);
171    }
172
173    private FedoraResource createMemento(final String id, final Instant version) {
174        final var mock = mock(FedoraResource.class);
175        when(mock.getId()).thenReturn(id);
176        when(mock.isMemento()).thenReturn(true);
177        when(mock.getMementoDatetime()).thenReturn(version);
178        return mock;
179    }
180
181    private TimeMapImpl createTimeMap(final String id) {
182        return new TimeMapImpl(createParent(id), transaction, sessionManager, resourceFactory);
183    }
184
185    private FedoraResource createParent(final String id) {
186        final var fedoraId = FedoraId.create(id);
187        final var parent = new ContainerImpl(fedoraId, transaction, sessionManager, resourceFactory, null);
188
189        parent.setCreatedBy("createdBy");
190        parent.setCreatedDate(Instant.now());
191        parent.setLastModifiedBy("modifiedBy");
192        parent.setLastModifiedDate(Instant.now());
193        parent.setEtag("etag");
194        parent.setStateToken("stateToken");
195
196        return parent;
197    }
198
199}