ref #10322, ref #9524 allow loading annotations without type
[cdmlib.git] / cdmlib-services / src / test / java / eu / etaxonomy / cdm / api / service / portal / TaxonPageDtoLoaderTest.java
index 9ac46c66988a8487253cd37264b2b1fed8377979..3a9495536418abd2149d572cbc1182063adcb7af 100644 (file)
@@ -31,6 +31,7 @@ import eu.etaxonomy.cdm.api.dto.portal.IFactDto;
 import eu.etaxonomy.cdm.api.dto.portal.IndividualsAssociationDto;
 import eu.etaxonomy.cdm.api.dto.portal.MediaDto2;
 import eu.etaxonomy.cdm.api.dto.portal.NamedAreaDto;
+import eu.etaxonomy.cdm.api.dto.portal.SourceDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonBaseDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonBaseDto.TaxonNameDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonInteractionDto;
@@ -39,21 +40,29 @@ import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.HomotypicGroupDTO;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.MediaRepresentationDTO;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.NameRelationDTO;
 import eu.etaxonomy.cdm.api.dto.portal.config.CondensedDistributionConfiguration;
+import eu.etaxonomy.cdm.api.dto.portal.config.DistributionInfoConfiguration;
 import eu.etaxonomy.cdm.api.dto.portal.config.TaxonPageDtoConfiguration;
 import eu.etaxonomy.cdm.api.service.INameService;
 import eu.etaxonomy.cdm.api.service.ITaxonService;
 import eu.etaxonomy.cdm.api.service.ITermService;
+import eu.etaxonomy.cdm.api.service.ITermTreeService;
 import eu.etaxonomy.cdm.api.service.geo.DistributionInfoBuilderTest;
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.DOI;
 import eu.etaxonomy.cdm.common.TreeNode;
 import eu.etaxonomy.cdm.common.URI;
 import eu.etaxonomy.cdm.common.UTF8;
+import eu.etaxonomy.cdm.format.common.TypedLabel;
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.model.common.Annotation;
 import eu.etaxonomy.cdm.model.common.AnnotationType;
 import eu.etaxonomy.cdm.model.common.ExtendedTimePeriod;
 import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.common.TimePeriod;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
+import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
 import eu.etaxonomy.cdm.model.description.Distribution;
 import eu.etaxonomy.cdm.model.description.Feature;
 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
@@ -66,6 +75,7 @@ import eu.etaxonomy.cdm.model.description.TaxonInteraction;
 import eu.etaxonomy.cdm.model.description.TemporalData;
 import eu.etaxonomy.cdm.model.description.TextData;
 import eu.etaxonomy.cdm.model.location.Country;
+import eu.etaxonomy.cdm.model.location.NamedArea;
 import eu.etaxonomy.cdm.model.media.ImageFile;
 import eu.etaxonomy.cdm.model.media.Media;
 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
@@ -80,6 +90,8 @@ import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.term.TermTree;
+import eu.etaxonomy.cdm.model.term.TermType;
 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
 import eu.etaxonomy.cdm.strategy.cache.TaggedTextFormatter;
 import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
@@ -99,6 +111,14 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
     private UUID specimenUuid2 = UUID.fromString("c9c69fa0-1179-48e6-b03a-a843048b16e6");
     private UUID mediaUuid1 = UUID.fromString("7e6a7d5e-a579-4ba8-98b0-cd10b48d1c04");
 
+    private UUID statusTreeUuid = UUID.fromString("4f018fe2-97c3-4818-9aa4-7a6ba70fc4a1");
+    private UUID areaTreeUuid = UUID.fromString("39cae3d4-36e4-4cc7-84a2-b748bbcbe800");
+    private UUID featureTreeUuid = UUID.fromString("d7089eb1-2fdc-4ec7-b5b5-2bb74f8fe578");
+
+    private UUID td4Uuid = UUID.fromString("9582cd0e-caa4-4c4e-a1c6-ec37f8b6df86");
+//    453923bb-b2a9-4843-a97f-9f02e6e13fbb
+//    28df5e01-0248-40d4-bff0-618a5aae3a30
+
     @SpringBeanByType
     private ITaxonService taxonService;
 
@@ -108,6 +128,9 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
     @SpringBeanByType
     private ITermService termService;
 
+    @SpringBeanByType
+    private ITermTreeService termTreeService;
+
     @SpringBeanByType
     private IPortalService portalService;
 
@@ -163,14 +186,136 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml")
     })
     public void testFacts() {
+        //create test data
         createTestData();
         commitAndStartNewTransaction();
+
+        //test
+        testAllFacts();
+        testDistributionWithFilter();
+    }
+
+    private void testDistributionWithFilter() {
+        //config general
         TaxonPageDtoConfiguration config = new TaxonPageDtoConfiguration();
-        CondensedDistributionConfiguration cc = config.getDistributionInfoConfiguration().getCondensedDistributionConfiguration();
+        DistributionInfoConfiguration distConfig = config.getDistributionInfoConfiguration();
+        CondensedDistributionConfiguration cc = distConfig.getCondensedDistributionConfiguration();
         cc.showAreaOfScopeLabel = true;
         config.setWithSpecimens(false);
         config.setTaxonUuid(taxonUuid);
+        config.setFeatureTree(featureTreeUuid);
+
+        config.setUseDtoLoading(true);
+        testDistributionWithFilterDo(config);
+        config.setUseDtoLoading(false);
+        testDistributionWithFilterDo(config);
+    }
+
+    private void testDistributionWithFilterDo(TaxonPageDtoConfiguration config) {
+        DistributionInfoConfiguration distConfig = config.getDistributionInfoConfiguration();
+
+        //with area filter
+        distConfig.setAreaTree(areaTreeUuid);
+        distConfig.setStatusTree(null);
+        testDistributionWithAreaFilter(config);
+
+        //with status filter
+        distConfig.setAreaTree(null);
+        distConfig.setStatusTree(statusTreeUuid);
+        testDistributionWithStatusFilter(config);
+    }
+
+    private void testDistributionWithAreaFilter(TaxonPageDtoConfiguration config) {
+
+        TaxonPageDto dto = portalService.taxonPageDto(config);
+        Assert.assertTrue("There should be no warnings", CdmUtils.isNullSafeEmpty(dto.getMessages()));
+
+        //facts
+        ContainerDto<FeatureDto> features = dto.getTaxonFacts();
+        Assert.assertEquals("There should be 1 feature (distribution)",
+                1, features.getCount());
+
+        //... distribution
+        FeatureDto distributionDto = features.getItems().get(0);
+        Assert.assertEquals("Distribution", distributionDto.getLabel());
+
+        ContainerDto<IFactDto> distributions = distributionDto.getFacts();
+        Assert.assertEquals(1, distributions.getCount());
+        IFactDto distribution = distributions.getItems().get(0);
+        Assert.assertEquals(DistributionInfoDto.class.getSimpleName(), distribution.getClazz());
+        DistributionInfoDto distributionInfo = (DistributionInfoDto)distribution;
+
+        //... condensed distribution
+        Assert.assertEquals("DEU", distributionInfo.getCondensedDistribution().getHtmlString());
+        Assert.assertEquals("as=a:,,0.1,&ad=country_earth%3Agmi_cntry:a:DEU&title=a:present", distributionInfo.getMapUriParams());
+
+        //...tree
+        DistributionTreeDto tree = (DistributionTreeDto)distributionInfo.getTree();
+        Assert.assertEquals("Tree:1<Germany:present{Second ref article. – The journal. p 22}:0>", new DistributionInfoBuilderTest().tree2String(tree));
+        Assert.assertEquals("Should be Germany only", 1, tree.getRootElement().children.size());
+        TreeNode<Set<DistributionDto>, NamedAreaDto> germanyNode = tree.getRootElement().getChildren().get(0);
+        Assert.assertEquals("Germany", germanyNode.getNodeId().getLabel());
+        DistributionDto germanyDistribution = germanyNode.getData().iterator().next();
+        Assert.assertEquals(1, germanyDistribution.getAnnotations().getCount());
+        Assert.assertEquals("There should be 1 source (even if it has no name used in source)", 1, germanyDistribution.getSources().getCount());
+    }
+
+    private void testDistributionWithStatusFilter(TaxonPageDtoConfiguration config) {
         TaxonPageDto dto = portalService.taxonPageDto(config);
+        Assert.assertTrue("There should be no warnings", CdmUtils.isNullSafeEmpty(dto.getMessages()));
+
+        //facts
+        ContainerDto<FeatureDto> features = dto.getTaxonFacts();
+        Assert.assertEquals("There should be 1 feature (distribution)", 1, features.getCount());
+
+        //... distribution
+        FeatureDto distributionDto = features.getItems().get(0);
+        Assert.assertEquals("Distribution", distributionDto.getLabel());
+
+        ContainerDto<IFactDto> distributions = distributionDto.getFacts();
+        Assert.assertEquals(1, distributions.getCount());
+        IFactDto distribution = distributions.getItems().get(0);
+        Assert.assertEquals(DistributionInfoDto.class.getSimpleName(), distribution.getClazz());
+        DistributionInfoDto distributionInfo = (DistributionInfoDto)distribution;
+
+        //... condensed distribution
+        Assert.assertEquals("DEU", distributionInfo.getCondensedDistribution().getHtmlString());
+        Assert.assertEquals("as=a:,,0.1,&ad=country_earth%3Agmi_cntry:a:DEU&title=a:present", distributionInfo.getMapUriParams());
+
+        //...tree
+        DistributionTreeDto tree = (DistributionTreeDto)distributionInfo.getTree();
+        Assert.assertEquals("Tree:1<Germany:present{Second ref article. – The journal. p 22}:0>", new DistributionInfoBuilderTest().tree2String(tree));
+        Assert.assertEquals("Should be Germany only", 1, tree.getRootElement().children.size());
+        TreeNode<Set<DistributionDto>, NamedAreaDto> germanyNode = tree.getRootElement().getChildren().get(0);
+        Assert.assertEquals("Germany", germanyNode.getNodeId().getLabel());
+        DistributionDto germanyDistribution = germanyNode.getData().iterator().next();
+        Assert.assertEquals(1, germanyDistribution.getAnnotations().getCount());
+        Assert.assertEquals("There should be 1 source (even if it has no name used in source)", 1, germanyDistribution.getSources().getCount());
+    }
+
+    /**
+     * Tests all facts without feature tree filter, distribution area filter
+     * and distributions status filter
+     */
+    private void testAllFacts() {
+        TaxonPageDtoConfiguration config = new TaxonPageDtoConfiguration();
+        DistributionInfoConfiguration distConfig = config.getDistributionInfoConfiguration();
+
+        CondensedDistributionConfiguration cc = distConfig.getCondensedDistributionConfiguration();
+        cc.showAreaOfScopeLabel = true;
+        config.setWithSpecimens(false);
+        config.setTaxonUuid(taxonUuid);
+        config.addAnnotationType(AnnotationType.uuidUntyped);
+
+        config.setUseDtoLoading(false);
+        testAllFactsDo(config); //with model instance loading
+        config.setUseDtoLoading(true);
+        testAllFactsDo(config); //with dto loading
+    }
+
+    private void testAllFactsDo(TaxonPageDtoConfiguration config) {
+        TaxonPageDto dto = portalService.taxonPageDto(config);
+        Assert.assertTrue("There should be no warnings", CdmUtils.isNullSafeEmpty(dto.getMessages()));
 
         Assert.assertNotNull(dto);
         List<TaggedText> list = dto.getTaggedName();
@@ -178,51 +323,58 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
 
         //facts
         ContainerDto<FeatureDto> features = dto.getTaxonFacts();
-        Assert.assertEquals("There should be 8 features, distribution and description and common names",
-                8, features.getCount());
+        Assert.assertEquals("There should be 9 features, distribution and description and common names",
+                9, features.getCount());
 
         //... common taxon name
-        FeatureDto commonNameDto = features.getItems().get(0);
+        int i = 0;
+        FeatureDto commonNameDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically. Also as >1 common name exists it should use plural.",
                 "Common Names", commonNameDto.getLabel());
         testCommonNames(commonNameDto);
 
         //... textData ("description")
-        FeatureDto descriptionDto = features.getItems().get(1);
+        FeatureDto descriptionDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically. Also as >1 'Description' exists it should use plural.",
                 "Descriptions", descriptionDto.getLabel());
         testTextDataAndMedia(descriptionDto);
 
+        //... discussion
+        FeatureDto discussionDto = features.getItems().get(i++);
+        Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically",
+                "Discussion", discussionDto.getLabel());
+        testTextDataEmpty(discussionDto);
+
         //... distribution
-        FeatureDto distributionDto = features.getItems().get(2);
+        FeatureDto distributionDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically",
                 "Distribution", distributionDto.getLabel());
         testDistributions(distributionDto);
 
         //termporal data
-        FeatureDto floweringDto = features.getItems().get(3);
+        FeatureDto floweringDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically",
                 "Flowering Season", floweringDto.getLabel());
         testTemporalData(floweringDto);
 
         //taxon interaction
-        FeatureDto hostPlantDto = features.getItems().get(4);
+        FeatureDto hostPlantDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically",
                 "Host Plant", hostPlantDto.getLabel());
         testTaxonInteraction(hostPlantDto);
 
         //quantitative data("introduction")
-        FeatureDto introductionDto = features.getItems().get(5);
+        FeatureDto introductionDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically.",
                 "Introduction", introductionDto.getLabel());
 
         //...categorical data ("Life-form")
-        FeatureDto statusDto = features.getItems().get(6);
+        FeatureDto statusDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically.",
                 "Life-form", statusDto.getLabel());
 
         //individuals association
-        FeatureDto materialExaminedDto = features.getItems().get(7);
+        FeatureDto materialExaminedDto = features.getItems().get(i++);
         Assert.assertEquals("As no feature tree is defined features should be sorted alphabetically",
                 "Materials Examined", materialExaminedDto.getLabel());
         testIndividualsAssociation(materialExaminedDto);
@@ -267,11 +419,14 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         FactDto flowering2 = (FactDto)floweringDto.getFacts().getItems().get(1);
         Assert.assertTrue("Currently we only compare by id. This may change in future",
                 flowering1.getId()<flowering2.getId());
-        String label1 = flowering1.getTypedLabel().get(0).getLabel();
+        TypedLabel typedLabel1 = flowering1.getTypedLabel().get(0);
+        String label1 = typedLabel1.getLabel();
         String label2 = flowering2.getTypedLabel().get(0).getLabel();
         String expectedLabel = "(10 Mar–)15 Apr–30 Jun(–20 Jul)";
         Assert.assertTrue(expectedLabel + "should be label of either flowering1 or flowering2",
                 label1.equals(expectedLabel) || label2.equals(expectedLabel));
+        Assert.assertEquals("TemporalData", typedLabel1.getCdmClass());
+        Assert.assertNotNull(typedLabel1.getUuid());
     }
 
     private void testCommonNames(FeatureDto commonNameDto) {
@@ -285,13 +440,27 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
                 "My flower", cn2.getName());
     }
 
+    private void testTextDataEmpty(FeatureDto discussionDto) {
+        ContainerDto<IFactDto> discussions = discussionDto.getFacts();
+        Assert.assertEquals(1, discussions.getCount());
+        FactDto discussion1 = (FactDto)discussions.getItems().get(0);
+        Assert.assertEquals(1, discussion1.getTypedLabel().size());
+        TypedLabel typedLabel = discussion1.getTypedLabel().get(0);
+        Assert.assertEquals("", typedLabel.getLabel());
+        Assert.assertEquals("TextData", typedLabel.getCdmClass());
+        Assert.assertNotNull(typedLabel.getUuid());
+    }
+
     private void testTextDataAndMedia(FeatureDto descriptionDto) {
+
         ContainerDto<IFactDto> descriptions = descriptionDto.getFacts();
         Assert.assertEquals(4, descriptions.getCount());
         FactDto description1 = (FactDto)descriptions.getItems().get(0);
         FactDto description2 = (FactDto)descriptions.getItems().get(1);
         FactDto description3 = (FactDto)descriptions.getItems().get(2);
         FactDto description4 = (FactDto)descriptions.getItems().get(3);
+
+        //test sorting
         Assert.assertNull("Current sorting should sort null to the top. This may change in future.",
                 description1.getSortIndex());
         Assert.assertNull("Current sorting should sort null to the top. This may change in future.",
@@ -300,9 +469,23 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
                 description1.getId() < description2.getId());
         //TODO use typed label formatter (once implemented)
         Assert.assertEquals("If sortindex is given it should be used for sorting.",
-                "My fourth description", description3.getTypedLabel().get(0).getLabel().toString());
+                "My fourth description", description3.getTypedLabel().get(0).getLabel());
         Assert.assertEquals("If sortindex is given it should be used for sorting.",
-                "My third description", description4.getTypedLabel().get(0).getLabel().toString());
+                "My third description", description4.getTypedLabel().get(0).getLabel());
+
+        FactDto td4Fact = description3; //renaming to original name td4 for better understanding
+        Assert.assertEquals(td4Uuid, td4Fact.getUuid());
+        TypedLabel td4TypedLabel = td4Fact.getTypedLabel().get(0);
+        Assert.assertEquals(td4Uuid, td4TypedLabel.getUuid());
+        Assert.assertEquals("TextData", td4TypedLabel.getCdmClass());
+
+        FactDto td3Fact = description4; //renaming to original name td3 for better understanding
+        Assert.assertEquals(1, td3Fact.getSources().getCount());
+        SourceDto source = td3Fact.getSources().getItems().get(0);
+        Assert.assertEquals(1, source.getLabel().size());
+        TypedLabel sourceTypedLabel = source.getLabel().get(0);
+        Assert.assertEquals("DescriptionElementSource", sourceTypedLabel.getCdmClass());
+
 
         //media
         ContainerDto<MediaDto2> factMedia = description4.getMedia();
@@ -338,11 +521,31 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         Assert.assertTrue("End does not match, but is: " + mapUriParamsEnd, mapUriParamsEnd.matches("a:(FRA|DEU)\\|b:(FRA|DEU)&title=[ab]:present\\|[ab]:native%3A\\+doubtfully\\+native"));
         //...tree
         DistributionTreeDto tree = (DistributionTreeDto)distributionInfo.getTree();
-        Assert.assertEquals("Tree:2<FRA:native: doubtfully native{Miller, M.M. 1978: My French distribution. p 44}:0><Germany:present{}:0>", new DistributionInfoBuilderTest().tree2String(tree));
+        Assert.assertEquals("Tree:2<FRA:native: doubtfully native{Miller, M.M. 1978: My French distribution. p 44}:0><Germany:present{Second ref article. – The journal. p 22}:0>", new DistributionInfoBuilderTest().tree2String(tree));
         Assert.assertEquals("Should be France and Germany", 2, tree.getRootElement().children.size());
         TreeNode<Set<DistributionDto>, NamedAreaDto> germanyNode = tree.getRootElement().getChildren().get(1);
         Assert.assertEquals("Germany", germanyNode.getNodeId().getLabel());
-        Assert.assertEquals(1, germanyNode.getData().iterator().next().getAnnotations().getCount());
+        DistributionDto germanyDistribution = germanyNode.getData().iterator().next();
+        Assert.assertEquals(2, germanyDistribution.getAnnotations().getCount());
+        Assert.assertEquals("Missing type annotation should exist",
+                + 1, germanyDistribution.getAnnotations().getItems().stream()
+                     .filter(a->"Missing Type Annotation".equals(a.getText())).count());
+        Assert.assertEquals("There should be 1 source (even if it has no name used in source)", 1, germanyDistribution.getSources().getCount());
+
+        //france
+        TreeNode<Set<DistributionDto>, NamedAreaDto> franceNode = tree.getRootElement().getChildren().get(0);
+        Assert.assertEquals("FRA", franceNode.getNodeId().getLabel());
+        Assert.assertTrue("Size was not 1, but " + franceNode.getData().size(), franceNode.getData().size() == 1);
+        DistributionDto franceDistributionDto = franceNode.getData().iterator().next();
+        //...source
+        Assert.assertEquals(1, franceDistributionDto.getSources().getCount());
+        SourceDto source = franceDistributionDto.getSources().getItems().get(0);
+        Assert.assertEquals("PrimaryTaxonomicSource", source.getType());
+        Assert.assertEquals("44", source.getCitationDetail());
+        Assert.assertEquals("1792", source.getAccessed());
+        Assert.assertEquals("https://doi.org/10.10.0123/suf-456", source.getDoi());
+        Assert.assertEquals("https://uri.org", source.getUri().toString());
+        Assert.assertEquals("Genus insourcus", TaggedTextFormatter.createString(source.getNameInSource()));
     }
 
     private void createTestData() {
@@ -380,31 +583,76 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         rel.setCodeEdition(NomenclaturalCodeEdition.ICN_2017_SHENZHEN);
         earlierHomonym.addAnnotation(Annotation.NewEditorialDefaultLanguageInstance("Homonym annotation"));
         nameService.save(earlierHomonym);
+
         return taxon;
     }
 
     private void createFactualData(Taxon taxon) {
+
         //distributions
         TaxonDescription taxDesc = TaxonDescription.NewInstance(taxon);
         Country.GERMANY().setSymbol("De");
         PresenceAbsenceTerm.PRESENT().setSymbol("");
         Distribution germany = Distribution.NewInstance(Country.GERMANY(), PresenceAbsenceTerm.PRESENT());
-        germany.addAnnotation(Annotation.NewEditorialDefaultLanguageInstance("Abc Annotation"));
+        germany.addAnnotation(Annotation.NewEditorialDefaultLanguageInstance("Editorial Annotation"));
         germany.addAnnotation(Annotation.NewInstance("Technical Annotation", AnnotationType.TECHNICAL(), Language.DEFAULT()));
+        germany.addAnnotation(Annotation.NewInstance("Missing Type Annotation", null, Language.DEFAULT()));
+        //.... germany source
+        Reference germanRef = ReferenceFactory.newArticle();
+        germanRef.setInJournal(ReferenceFactory.newJournal());
+        germanRef.setTitle("Second ref article");
+        germanRef.getInJournal().setTitle("The journal");
+        germany.addPrimaryTaxonomicSource(germanRef, "22");
 
         taxDesc.addElement(germany);
+
         Country.FRANCE().setSymbol("Fr");
 //        PresenceAbsenceTerm.INTRODUCED().setSymbol("i");
         Distribution franceDist = Distribution.NewInstance(Country.FRANCE(), PresenceAbsenceTerm.NATIVE_DOUBTFULLY_NATIVE());
         taxDesc.addElement(franceDist);
 
+        //area tree
+        TermTree<NamedArea> areaTree = termTreeService.find(areaTreeUuid);
+        if (areaTree == null) {
+            areaTree = TermTree.NewInstance(TermType.NamedArea, NamedArea.class);
+            areaTree.setUuid(areaTreeUuid);
+            areaTree.getRoot().addChild(Country.GERMANY());
+            termTreeService.save(areaTree);
+        }
+
+        //status tree
+        TermTree<PresenceAbsenceTerm> statusTree = termTreeService.find(statusTreeUuid);
+        if (statusTree == null) {
+            statusTree = TermTree.NewInstance(TermType.PresenceAbsenceTerm, PresenceAbsenceTerm.class);
+            statusTree.setUuid(statusTreeUuid);;
+            statusTree.getRoot().addChild(PresenceAbsenceTerm.PRESENT());
+            termTreeService.save(statusTree);
+        }
+
+        //feature tree
+        TermTree<Feature> featureTree = termTreeService.find(featureTreeUuid);
+        if (featureTree == null) {
+            featureTree = TermTree.NewInstance(TermType.Feature, Feature.class);
+            featureTree.setUuid(featureTreeUuid);
+            featureTree.getRoot().addChild(Feature.DISTRIBUTION());
+            termTreeService.save(featureTree);
+        }
+
         //... sources
         //... ... primary
         Reference franceRef = ReferenceFactory.newBook();
         franceRef.setAuthorship(taxon.getName().getCombinationAuthorship());
         franceRef.setTitle("My French distribution");
         franceRef.setDatePublished(TimePeriodParser.parseStringVerbatim("1978"));
-        franceDist.addPrimaryTaxonomicSource(franceRef, "44");
+        franceRef.setDoi(DOI.fromRegistrantCodeAndSuffix("10.0123", "suf-456"));
+        franceRef.setUri(URI.create("https://uri.org"));;
+        DescriptionElementSource source = franceDist.addPrimaryTaxonomicSource(franceRef, "44");
+        source.setAccessed(TimePeriod.NewInstance(1792));
+        TaxonName nameInSource = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
+        nameInSource.setGenusOrUninomial("Genus");
+        nameInSource.setSpecificEpithet("insourcus");
+        source.setNameUsedInSource(nameInSource);
+
         //... ... import
         Reference importRef = ReferenceFactory.newDatabase();
         importRef.setTitle("French distribution import");  //should not be shown in output
@@ -418,6 +666,7 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         td3.addPrimaryTaxonomicSource(franceRef, "63");
         TextData td4 = TextData.NewInstance(Feature.DESCRIPTION(), "My fourth description", Language.DEFAULT(), null);
         td4.setSortIndex(1);
+        td4.setUuid(td4Uuid);
         taxDesc.addElements(td1, td2, td3, td4);
         //... with media
         Media media1 = Media.NewInstance(URI.create("http://media.de/file.jpg"), 2, "JPG", "jpg");
@@ -431,6 +680,13 @@ public class TaxonPageDtoLoaderTest extends CdmTransactionalIntegrationTest {
         td3.addMedia(media1);
         td3.addMedia(media2);
 
+        //empty text
+        TextData emptyTd = TextData.NewInstance(Feature.DISCUSSION(), "", Language.DEFAULT(), null);
+        taxDesc.addElements(emptyTd);
+        //annotation
+        taxDesc.addAnnotation(Annotation.NewInstance("Missing Type Annotation for empty", null, Language.DEFAULT()));
+        taxDesc.addMarker(MarkerType.IS_DOUBTFUL(), true);
+
         //common names
         CommonTaxonName cn1 = CommonTaxonName.NewInstance("My flower", Language.ENGLISH(), Country.UNITEDKINGDOMOFGREATBRITAINANDNORTHERNIRELAND());
         CommonTaxonName cn2 = CommonTaxonName.NewInstance("Meine Blume", Language.GERMAN(), Country.GERMANY());