package org.gbif.checklistbank.nub.validation; import org.apache.commons.lang3.StringUtils; import org.gbif.api.model.Constants; import org.gbif.api.model.checklistbank.NameUsage; import org.gbif.api.model.common.LinneanClassification; import org.gbif.api.model.common.paging.PagingRequest; import org.gbif.api.service.checklistbank.NameUsageService; import org.gbif.api.vocabulary.Kingdom; import org.gbif.api.vocabulary.Rank; import java.util.Iterator; import java.util.List; import javax.annotation.Nullable; import com.google.common.collect.Lists; import org.junit.Assert; import org.neo4j.helpers.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PgAssertionEngine implements AssertionEngine { private static final Logger LOG = LoggerFactory.getLogger(PgAssertionEngine.class); private final NameUsageService usageService; private boolean valid = true; public PgAssertionEngine(NameUsageService usageService) { this.usageService = usageService; } @Override public boolean isValid() { return valid; } @Override public void assertParentsContain(String searchName, Rank searchRank, String parent) { try { NameUsage start = findUsageByCanonical(searchName, searchRank); assertParentsContain(start.getKey(), null, parent); } catch (AssertionError e) { valid = false; LOG.error("Classification for {} {} lacks parent {}", searchRank, searchName, parent, e); } } @Override public void assertParentsContain(int usageKey, @Nullable Rank parentRank, String parent) { try { boolean found = false; for (NameUsage p : usageService.listParents(usageKey, null)) { if (parent.equalsIgnoreCase(p.getCanonicalOrScientificName()) && (parentRank == null || parentRank.equals(p.getRank()))) { found = true; break; } } Assert.assertTrue(found); } catch (AssertionError e) { valid = false; LOG.error("Classification for usage {} missing {}", usageKey, parent, e); } } @Override public void assertClassification(int usageKey, LinneanClassification classification) { try { NameUsage u = usageService.get(usageKey, null); for (Rank r : Rank.DWC_RANKS) { if (!StringUtils.isBlank(classification.getHigherRank(r))) { Assert.assertEquals(classification.getHigherRank(r), u.getHigherRank(r)); } } } catch (AssertionError e) { valid = false; LOG.error("Classification assertion failed for {}", usageKey, e); } } @Override public void assertClassification(int usageKey, String... classification) { Iterator<String> expected = Lists.newArrayList(classification).iterator(); Iterator<NameUsage> parents = Lists.reverse(usageService.listParents(usageKey, null)).iterator(); try { while (expected.hasNext()) { Assert.assertTrue(parents.hasNext()); Assert.assertEquals(expected.next(), parents.next().getCanonicalName()); } Assert.assertFalse(parents.hasNext()); } catch (AssertionError e) { valid = false; LOG.error("Classification for usage {} wrong", usageKey, e); } } @Override public void assertSearchMatch(int expectedSearchMatches, String name) { assertSearchMatch(expectedSearchMatches, name, null); } @Override public void assertSearchMatch(int expectedSearchMatches, String name, Rank rank) { List<NameUsage> matches = Lists.newArrayList(); try { matches = findUsagesByCanonical(name, rank); Assert.assertEquals(expectedSearchMatches, matches.size()); } catch (AssertionError e) { valid = false; LOG.error("Expected {} matches, but found {} for name {} with rank {}", expectedSearchMatches, matches.size(), name, rank); } } @Override public void assertNotExisting(String name, Rank rank) { List<NameUsage> matches = Lists.newArrayList(); try { matches = findUsagesByCanonical(name, rank); Assert.assertTrue(matches.isEmpty()); } catch (AssertionError e) { valid = false; LOG.error("Found {} {} expected to be missing: {}", rank, name, matches.get(0)); } } @Override public void assertUsage(int usageKey, Rank rank, String name, String accepted, Kingdom kingdom) { NameUsage u = null; try { u = usageService.get(usageKey, null); Assert.assertNotNull("Usage "+usageKey+" not existing", u); Assert.assertEquals("Wrong rank", rank, u.getRank()); Assert.assertTrue("Wrong scientific name", u.getScientificName().startsWith(name)); if (StringUtils.isBlank(accepted)) { Assert.assertFalse("Not accepted", u.isSynonym()); } else { Assert.assertTrue("Not a synonym", u.isSynonym()); Assert.assertTrue("Wrong accepted name", u.getAccepted().startsWith(accepted)); } if (kingdom != null) { Assert.assertEquals("Wrong kingdom", kingdom.nubUsageID(), u.getKingdomKey()); Assert.assertEquals("Wrong kingdom", kingdom.scientificName(), u.getKingdom()); } } catch (AssertionError e) { LOG.error("Usage {} {} wrong: {}\n{}", usageKey, name, e.getMessage(), u); valid = false; } } private NameUsage findUsageByCanonical(String name, Rank rank) { List<NameUsage> matches = findUsagesByCanonical(name, rank); if (matches.size() > 1 || matches.isEmpty()) { valid = false; LOG.error("{} matches when expecting single match for {} {}", matches.size(), rank, name); throw new AssertionError("No single match for " + name); } return matches.get(0); } private List<NameUsage> findUsagesByCanonical(String name, @Nullable Rank rank) { // avoid paging and do a large, single page PagingRequest req = new PagingRequest(0, 1000); List<NameUsage> matches = Lists.newArrayList(); for (NameUsage u : usageService.listByCanonicalName(null, name, req, Constants.NUB_DATASET_KEY).getResults()) { if (rank == null || rank.equals(u.getRank())) { matches.add(u); } } return matches; } }