001/*
002 * Copyright 2020 Global Biodiversity Information Facility (GBIF)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.gbif.api.model.occurrence;
017
018import org.gbif.api.model.common.MediaObject;
019import org.gbif.api.vocabulary.BasisOfRecord;
020import org.gbif.api.vocabulary.Continent;
021import org.gbif.api.vocabulary.Country;
022import org.gbif.api.vocabulary.EndpointType;
023import org.gbif.api.vocabulary.GbifRegion;
024import org.gbif.api.vocabulary.MediaType;
025import org.gbif.api.vocabulary.OccurrenceIssue;
026import org.gbif.api.vocabulary.Rank;
027import org.gbif.dwc.terms.DcTerm;
028import org.gbif.dwc.terms.DwcTerm;
029import org.gbif.dwc.terms.GbifTerm;
030import org.gbif.dwc.terms.IucnTerm;
031import org.gbif.dwc.terms.Term;
032import org.gbif.dwc.terms.TermFactory;
033import org.gbif.dwc.terms.UnknownTerm;
034
035import java.io.IOException;
036import java.net.URI;
037import java.util.ArrayList;
038import java.util.Date;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Set;
042import java.util.UUID;
043
044import org.apache.commons.text.RandomStringGenerator;
045import org.junit.jupiter.api.BeforeEach;
046import org.junit.jupiter.api.Test;
047
048import com.fasterxml.jackson.annotation.JsonInclude;
049import com.fasterxml.jackson.databind.DeserializationFeature;
050import com.fasterxml.jackson.databind.ObjectMapper;
051import com.fasterxml.jackson.databind.SerializationFeature;
052
053import static org.junit.jupiter.api.Assertions.assertEquals;
054import static org.junit.jupiter.api.Assertions.assertFalse;
055import static org.junit.jupiter.api.Assertions.assertNotEquals;
056import static org.junit.jupiter.api.Assertions.assertNotNull;
057import static org.junit.jupiter.api.Assertions.assertNull;
058import static org.junit.jupiter.api.Assertions.assertTrue;
059
060public class OccurrenceTest {
061
062  private ObjectMapper mapper;
063
064  private final UUID datasetKey = UUID.randomUUID();
065  private final Country country = Country.ALGERIA;
066  private final Date interpreted = new Date();
067  private final Date crawled = new Date(interpreted.getTime() - 99999);
068
069  @BeforeEach
070  public void init() {
071    mapper = new ObjectMapper();
072    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
073    mapper.enable(SerializationFeature.INDENT_OUTPUT);
074    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
075  }
076
077  private Occurrence buildTestOccurrence() {
078    Occurrence o = new Occurrence();
079    Long key = 321L;
080    o.setKey(key);
081    o.setDatasetKey(datasetKey);
082    String sciName = "Abies alba";
083    o.setScientificName(sciName);
084    o.setCountry(country);
085    o.setLastInterpreted(interpreted);
086    o.setLastCrawled(crawled);
087    return o;
088  }
089
090  @Test
091  public void testEquals() {
092    Occurrence o1 = buildTestOccurrence();
093    Occurrence o2 = buildTestOccurrence();
094    Occurrence o3 = buildTestOccurrence();
095    o3.setCountry(Country.POLAND);
096
097    // All of them are equal to themselves
098    assertEquals(o1, o1);
099    assertEquals(o2, o2);
100    assertEquals(o3, o3);
101
102    // But not always equal among themselves
103    assertEquals(o1, o2);
104    assertNotEquals(o1, o3);
105    assertNotEquals(o2, o3);
106  }
107
108  @Test
109  public void testHashcode() {
110    Occurrence o1 = buildTestOccurrence();
111    Occurrence o2 = buildTestOccurrence();
112    Occurrence o3 = buildTestOccurrence();
113    o3.setCountry(Country.POLAND);
114
115    assertEquals(o1.hashCode(), o2.hashCode());
116    assertNotEquals(o1.hashCode(), o3.hashCode());
117    assertNotEquals(o2.hashCode(), o3.hashCode());
118  }
119
120  @Test
121  public void testNullConstructor() {
122    Occurrence o = new Occurrence(null);
123    assertNotNull(o);
124  }
125
126  @Test
127  public void testGetHigherClassificationMap() {
128    Occurrence occ = new Occurrence();
129    occ.setFamily("Plants family");
130    occ.setFamilyKey(16);
131    occ.setKingdom("Plants");
132    occ.setKingdomKey(6);
133
134    assertEquals(2, occ.getHigherClassificationMap().size());
135    assertTrue(occ.getHigherClassificationMap().containsKey(16));
136    assertTrue(occ.getHigherClassificationMap().containsKey(6));
137    assertTrue(occ.getHigherClassificationMap().containsValue("Plants"));
138    assertTrue(occ.getHigherClassificationMap().containsValue("Plants family"));
139
140    occ.setTaxonKey(200);
141    assertEquals(2, occ.getHigherClassificationMap().size());
142
143    occ.setTaxonKey(16);
144    assertEquals(1, occ.getHigherClassificationMap().size());
145    assertTrue(occ.getHigherClassificationMap().containsKey(6));
146    assertTrue(occ.getHigherClassificationMap().containsValue("Plants"));
147    assertFalse(occ.getHigherClassificationMap().containsKey(16));
148    assertFalse(occ.getHigherClassificationMap().containsValue("Plants family"));
149  }
150
151  @Test
152  public void testGetHigherTaxon() {
153    Occurrence occ = new Occurrence();
154    occ.setFamily("Plants family");
155    occ.setFamilyKey(16);
156    occ.setKingdom("Plants");
157    occ.setKingdomKey(6);
158
159    assertEquals(6, occ.getHigherRankKey(Rank.KINGDOM));
160    assertEquals("Plants", occ.getHigherRank(Rank.KINGDOM));
161  }
162
163  @Test
164  public void testNullDates() {
165    Occurrence occ = new Occurrence();
166    occ.setEventDate(null);
167    assertNull(occ.getEventDate());
168    occ.setDateIdentified(null);
169    assertNull(occ.getDateIdentified());
170    occ.setModified(null);
171    assertNull(occ.getModified());
172    occ.setLastInterpreted(null);
173    assertNull(occ.getLastInterpreted());
174  }
175
176  @Test
177  public void testProtocolAndPublishingCountry() {
178    Occurrence occ = new Occurrence();
179    occ.setKey(1L);
180    occ.setDatasetKey(UUID.randomUUID());
181    occ.setProtocol(EndpointType.BIOCASE);
182    occ.setPublishingCountry(Country.AFGHANISTAN);
183
184    assertEquals(EndpointType.BIOCASE, occ.getProtocol());
185    assertEquals(Country.AFGHANISTAN, occ.getPublishingCountry());
186  }
187
188  @Test
189  public void addIssues() {
190    Occurrence occ = new Occurrence();
191    assertTrue(occ.getIssues().isEmpty());
192    assertFalse(occ.hasSpatialIssue());
193
194    occ.addIssue(OccurrenceIssue.ELEVATION_NON_NUMERIC);
195    assertEquals(1, occ.getIssues().size());
196    assertFalse(occ.hasSpatialIssue());
197
198    occ.addIssue(OccurrenceIssue.COORDINATE_OUT_OF_RANGE);
199    assertEquals(2, occ.getIssues().size());
200    assertTrue(occ.hasSpatialIssue());
201  }
202
203  @Test
204  public void testJsonSerde() throws IOException {
205    Occurrence occ = new Occurrence();
206    occ.setFamily("Plants family");
207    occ.setFamilyKey(16);
208    occ.setKingdom("Plants");
209    occ.setKingdomKey(6);
210    occ.setCoordinateUncertaintyInMeters(25.5);
211
212    occ.getVerbatimFields().put(DwcTerm.catalogNumber, "MD10782");
213    occ.addIssue(OccurrenceIssue.COORDINATE_OUT_OF_RANGE);
214
215    String json = mapper.writeValueAsString(occ);
216    System.out.println(json);
217    Occurrence occ2 = mapper.readValue(json, Occurrence.class);
218    assertEquals(occ, occ2);
219  }
220
221  @Test
222  public void testVerbatimConstructor() {
223    VerbatimOccurrence verb = new VerbatimOccurrence();
224    verb.setKey(123L);
225    verb.setDatasetKey(UUID.randomUUID());
226    verb.setPublishingOrgKey(UUID.randomUUID());
227    verb.setPublishingCountry(Country.AFGHANISTAN);
228    verb.setLastCrawled(new Date());
229    verb.setProtocol(EndpointType.BIOCASE);
230    for (Term term : DwcTerm.values()) {
231      verb.setVerbatimField(term, "I am " + term);
232    }
233
234    Occurrence occ = new Occurrence(verb);
235    assertEquals(occ.getKey(), verb.getKey());
236    assertEquals(occ.getDatasetKey(), verb.getDatasetKey());
237    assertEquals(occ.getPublishingOrgKey(), verb.getPublishingOrgKey());
238    assertEquals(occ.getPublishingCountry(), verb.getPublishingCountry());
239    assertEquals(occ.getProtocol(), verb.getProtocol());
240    assertEquals(occ.getLastCrawled(), verb.getLastCrawled());
241    assertEquals(occ.getVerbatimFields().size(), verb.getVerbatimFields().size());
242    assertEquals(occ.getVerbatimField(DwcTerm.acceptedNameUsage), verb.getVerbatimField(DwcTerm.acceptedNameUsage));
243  }
244
245  @Test
246  public void testBuildFromNullVerbatim() {
247    Occurrence occ = new Occurrence(null);
248    assertNotNull(occ);
249
250    VerbatimOccurrence verb = new VerbatimOccurrence();
251    occ = new Occurrence(verb);
252    assertNotNull(occ);
253  }
254
255  @Test
256  public void testVerbatimMapSerde() throws Exception {
257    Occurrence o = new Occurrence();
258    o.setKey(7L);
259    o.setLastParsed(new Date());
260    o.setDatasetKey(UUID.randomUUID());
261    o.setCountry(Country.ALBANIA);
262    o.setContinent(Continent.AFRICA);
263    o.setBasisOfRecord(BasisOfRecord.OBSERVATION);
264    o.setTaxonKey(212);
265    o.setDay(21);
266    o.setMonth(1);
267    o.setYear(1973);
268    o.setCoordinateAccuracy(0.002);
269
270    char [][] alphabeticPairs = {{'a','z'},{'A','Z'}};
271    RandomStringGenerator generatorAlphabetic = new RandomStringGenerator.Builder()
272        .withinRange(alphabeticPairs)
273        .build();
274
275    for (DwcTerm term : DwcTerm.values()) {
276      if (!term.isClass()) {
277        o.setVerbatimField(term, generatorAlphabetic.generate(20));
278      }
279    }
280
281    for (DcTerm term : DcTerm.values()) {
282      if (!term.isClass()) {
283        o.setVerbatimField(term, generatorAlphabetic.generate(20));
284      }
285    }
286
287    for (GbifTerm term : GbifTerm.values()) {
288      if (!term.isClass()) {
289        o.setVerbatimField(term, generatorAlphabetic.generate(20));
290      }
291    }
292
293    for (IucnTerm term : IucnTerm.values()) {
294      if (!term.isClass()) {
295        o.setVerbatimField(term, generatorAlphabetic.generate(20));
296      }
297    }
298
299    o.setVerbatimField(DwcTerm.scientificName, "Abies alba");
300    o.setVerbatimField(DwcTerm.collectionCode, "BUGS");
301    o.setVerbatimField(DwcTerm.catalogNumber, "MD10782");
302    o.setVerbatimField(UnknownTerm.build("http://rs.un.org/terms/temperatur"), generatorAlphabetic.generate(30));
303    o.setVerbatimField(UnknownTerm.build("http://rs.un.org/terms/co2"), generatorAlphabetic.generate(30));
304    o.setVerbatimField(UnknownTerm.build("http://rs.un.org/terms/modified"), new Date().toString());
305    o.setVerbatimField(UnknownTerm.build("http://rs.un.org/terms/scientificName"),
306        generatorAlphabetic.generate(30));
307
308    String json = mapper.writeValueAsString(o);
309    System.out.println(json);
310
311    Occurrence o2 = mapper.readValue(json, Occurrence.class);
312
313    Set<Term> diff = new HashSet<>(o.getVerbatimFields().keySet());
314    diff.removeAll(o2.getVerbatimFields().keySet());
315
316    for (Term t : diff) {
317      System.out.println("TERM DIFF: " + t.qualifiedName());
318    }
319
320    assertNull(o2.getVerbatimField(TermFactory.instance().findTerm("coordinateAccuracyInMeters")));
321    assertEquals(o.getVerbatimFields().size() - 1, o2.getVerbatimFields().size());
322    assertEquals(1, diff.size());
323    assertTrue(diff.contains(GbifTerm.verbatimScientificName));
324  }
325
326  /**
327   * checks that countryCode, geodeticDatum and class are nicely exposed in json
328   */
329  @Test
330  public void testCustomSerlializations() throws Exception {
331    Occurrence o = new Occurrence();
332    o.setKey(7L);
333    o.setCountry(Country.ALGERIA);
334    o.setClassKey(999);
335    o.setClazz("Insecta");
336    o.setVerbatimField(DwcTerm.recordedBy, "Sankt Nikolaus");
337
338    String json = mapper.writeValueAsString(o);
339    System.out.println(json);
340    assertTrue(json.contains("\"class\" : \"Insecta\""));
341    assertTrue(json.contains("\"countryCode\" : \"DZ\""));
342    assertTrue(json.contains("\"country\" : \"Algeria\""));
343    assertTrue(json.contains("\"gbifRegion\" : \"AFRICA\""));
344
345    Occurrence o2 = mapper.readValue(json, Occurrence.class);
346
347    assertEquals(Country.ALGERIA, o2.getCountry());
348    assertEquals(GbifRegion.AFRICA, o2.getGbifRegion());
349    assertEquals("Insecta", o2.getClazz());
350    assertNull(o2.getGeodeticDatum());
351
352    // now add coords, datum shows
353    o.setDecimalLatitude(12d);
354    o.setDecimalLongitude(23d);
355    json = mapper.writeValueAsString(o);
356    System.out.println(json);
357    assertTrue(json.contains("\"geodeticDatum\" : \"WGS84\""));
358
359    o2 = mapper.readValue(json, Occurrence.class);
360    assertEquals("WGS84", o2.getGeodeticDatum());
361
362    Set<Term> diff = new HashSet<>(o.getVerbatimFields().keySet());
363    diff.removeAll(o2.getVerbatimFields().keySet());
364
365    for (Term t : diff) {
366      System.out.println(t.qualifiedName());
367    }
368
369    assertEquals(o.getVerbatimFields().size(), o2.getVerbatimFields().size());
370    assertTrue(diff.isEmpty());
371  }
372
373  /**
374   * checks that countryCode, geodeticDatum and class are nicely exposed in json
375   */
376  @Test
377  public void testExtensionsSerializations() throws Exception {
378    Occurrence o = new Occurrence();
379    o.setKey(7L);
380    o.setCountry(Country.ALGERIA);
381    o.setClassKey(999);
382    o.setClazz("Insecta");
383    o.setVerbatimField(DwcTerm.recordedBy, "Sankt Nikolaus");
384    MediaObject mediaObject = new MediaObject();
385    mediaObject.setCreated(new Date());
386    mediaObject.setCreator("fede");
387    mediaObject.setFormat("jpeg");
388    mediaObject.setLicense("testLicense");
389    mediaObject.setPublisher("testPublisher");
390    mediaObject.setReferences(new URI("http://www.gbif.org"));
391    mediaObject.setTitle("mediaTestTitle");
392    mediaObject.setType(MediaType.StillImage);
393    mediaObject.setIdentifier(new URI("http://www.gbif.org"));
394    List<MediaObject> medias = new ArrayList<>();
395    medias.add(mediaObject);
396    o.setMedia(medias);
397    String json = mapper.writeValueAsString(o);
398    System.out.println(json);
399    assertTrue(json.contains("\"country\" : \"Algeria\""));
400
401    Occurrence o2 = mapper.readValue(json, Occurrence.class);
402
403    assertEquals(o2.getMedia().get(0), mediaObject);
404  }
405}