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}