From de7ecdd034f40e965d4996a021cc4bacfe685a9e Mon Sep 17 00:00:00 2001
From: mrodzis <weidling@sub.uni-goettingen.de>
Date: Fri, 18 Sep 2020 11:07:56 +0200
Subject: [PATCH] Feature/#56 section wise collation

---
 .gitignore                                |   2 +-
 .gitlab-ci.yml                            |  10 +-
 CHANGELOG.md                              |  24 +-
 docker/.env                               |   2 +
 exist-app/build.properties                |   2 +-
 exist-app/data/ahiqar_sample.xml          | 121 +++---
 exist-app/modules/annotations.xqm         |  22 +-
 exist-app/modules/collate.xqm             | 231 ++++++++++++
 exist-app/modules/commons.xqm             |  18 +
 exist-app/modules/tapi-collection.xqm     | 171 +++++++++
 exist-app/modules/tapi.xqm                | 372 ++++---------------
 exist-app/tests-runner.xq                 |   9 +-
 exist-app/tests.xqm                       | 236 ++++++------
 exist-app/tests/collate-tests.xqm         | 424 ++++++++++++++++++++++
 exist-app/tests/commons-tests.xqm         |  17 +
 exist-app/tests/tapi-collection-tests.xqm | 260 +++++++++++++
 16 files changed, 1425 insertions(+), 496 deletions(-)
 create mode 100644 exist-app/modules/collate.xqm
 create mode 100644 exist-app/modules/commons.xqm
 create mode 100644 exist-app/modules/tapi-collection.xqm
 create mode 100644 exist-app/tests/collate-tests.xqm
 create mode 100644 exist-app/tests/commons-tests.xqm
 create mode 100644 exist-app/tests/tapi-collection-tests.xqm

diff --git a/.gitignore b/.gitignore
index 2fefe6d8..72a43116 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@ exist-app/**/*.xar
 **/ahikar.env
 
 # docker container files (COPY statements) and volumes
-docker/exist/**
+docker/exist*/**
 docker/frontend
 
 # exclude all ZIP files
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f677a435..f488f9df 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -189,10 +189,8 @@ test_api_status-test:
     - master
     - tags
     - develop
-  script: 
-    # temporarily switch to dev as long as test isn't running properly
-    #- header=$(curl --head -s https://ahikar-test.sub.uni-goettingen.de/api/textapi/ahikar/3r9ps/collection.json)
-    - header=$(curl --head -s https://ahikar-dev.sub.uni-goettingen.de/api/textapi/ahikar/3r9ps/collection.json)
+  script:
+    - header=$(curl --head -s https://ahikar-test.sub.uni-goettingen.de/api/textapi/ahikar/3r9ps/collection.json)
     - status=$(echo $header | head -n 1 | cut -d" " -f 2)
     - echo "Current HTTP status is $status."
     - if [[ "$status" == "200" ]]; then exit 0; else exit 1; fi
@@ -228,9 +226,7 @@ full_plain_text_api_status-test:
     - tags
     - develop
   script:
-    # temporarily switch to dev as long as test isn't running properly
-    #- header=$(curl --head --silent https://ahikar-test.sub.uni-goettingen.de/api/content/ahikar-plain-text.zip)
-    - header=$(curl --head --silent https://ahikar-dev.sub.uni-goettingen.de/api/content/ahikar-plain-text.zip)
+    - header=$(curl --head --silent https://ahikar-test.sub.uni-goettingen.de/api/content/ahikar-plain-text.zip)
 
 full_plain_text_api_status-develop:
   image: docker.gitlab.gwdg.de/mrodzis/test
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3c3242f..e148e46c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,19 +5,37 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [1.10.0] - 2020-09-18
+
+### Added
+
+- `collate.xqm` has been introduced.
+It provides plain text versions of XMLs files while only considering text passages that follow tei:milestone.
+- `commons.xqm` has been introduced.
+It provides variables and functions used in several other modules.
+
+### Changed
+
+- All functionality that deals with creating a plain text version of a given XML file has been moved to `collate.xqm`.
+- `tapi.xqm` and `annotations.xqm` outsourced some variable to `commons.xqm`.
+
+### Fixed
+
+- The RESTAPI endpoint returning txt-files has been fixed and is available again.
+
 ## [1.9.3] - 2020-09-18
 
-## Changed
+### Changed
 
 - The data directory of eXist-db is mounted to a volume instead of a bind mount.
 
-## Fixed
+### Fixed
 
 - The maximum amount of memory usable by eXist-db's Docker container has been reduced to 1GB.
 
 ## [1.9.2] - 2020-09-10
 
-## Changed
+### Changed
 
 - The GitLab templates have been tidied up according to their actual usage.
 Also, a passage about updating the README as been added.
diff --git a/docker/.env b/docker/.env
index 914b3b27..dc1cf2ff 100644
--- a/docker/.env
+++ b/docker/.env
@@ -1,3 +1,5 @@
 PORT=8094
 TAG=testing
 APP_NAME=https://ahikar-test.sub.uni-goettingen.de/
+EXIST_HOST_DIR=./exist_2
+EXIST_CONTAINER_NAME=ahikar_test_existdb
diff --git a/exist-app/build.properties b/exist-app/build.properties
index b0d32c10..e6276c19 100644
--- a/exist-app/build.properties
+++ b/exist-app/build.properties
@@ -1,5 +1,5 @@
 project.name=https://ahikar-test.sub.uni-goettingen.de/
-project.version=1.9.3
+project.version=1.10.0
 project.title=TextAPI for Ahikar
 project.abbrev=ahikar-test
 project.processorversion=5.2.0
diff --git a/exist-app/data/ahiqar_sample.xml b/exist-app/data/ahiqar_sample.xml
index f4ac9d09..83e35c31 100644
--- a/exist-app/data/ahiqar_sample.xml
+++ b/exist-app/data/ahiqar_sample.xml
@@ -1,9 +1,5 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
-<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml"
-	schematypens="http://purl.oclc.org/dsdl/schematron"?>
-
-<!-- 
+<?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?><?xml-model href="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="application/xml"
+	schematypens="http://purl.oclc.org/dsdl/schematron"?><!-- 
 # INTRODUCTION TO AND PURPOSE OF THIS FILE
    
 The following document is a sample for the Ahiqar project.
@@ -21,7 +17,6 @@ All suggestions regarding the rendering of certain encoding are marked with ###.
 The proposed ideas aren't normative and can be altered completely.
 Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conventions (https://en.wikipedia.org/wiki/Leiden_Conventions) which are common for critical editions
 -->
-
 <TEI xmlns="http://www.tei-c.org/ns/1.0">
    <teiHeader>
       <fileDesc>
@@ -113,22 +108,28 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                NOTE: these suggestions hold for all type of additions, regardless of their place.
                it should be visually set apart from the rest of the text to indicate that it's an addition.
                ideas: text framed by \.../ (LC) in lighter color, maybe a panel could display more information about the gloss?-->
-               <head><supplied>.</supplied><add place="margin">حقًا</add>حيث تبدأ القصة
-                  <g>✓</g></head>
+               <head>
+                        <supplied>.</supplied>
+                        <add place="margin">حقًا</add>حيث تبدأ القصة
+                  <g>✓</g>
+                    </head>
                <ab>نبتدي بعون الباري<add place="interlinear">اختبار</add> تعالى جل اسمه</ab>
                <!-- ### tei:lb[@break = 'no'] describes that a word has begun in the line before and is continued in the current line.
                this mostly lacks hyphenation.
                users should understand by a visual clue that these parts belong together. 
                ideas: bracket at the bottom that opens in line 1 and closes in line 2 -->
-               <ab><lb break="no"/>وتعالى ذكره الى الابد. ونكتب خبر</ab>
+               <ab>
+                        <lb break="no"/>وتعالى ذكره الى الابد. ونكتب خبر</ab>
                <ab>الحكيم الماهر الفليوس الشاطر</ab>
                <!-- ### users should have the opportunity to highlight all persons and/or places on a page.
                since we're currently working on annotations which will influence this serialization, this isn't a high priority task. -->
                <!-- ### unclear passages should be marked in some way
                idea: dotted underlining (LC) -->
-               <ab>وزير <persName>سنحاريب</persName> ابن <persName>سرحادوم</persName> ملك <unclear
-                     reason="illegible"><placeName>اتور</placeName></unclear>
+               <ab>وزير <persName>سنحاريب</persName> ابن <persName>سرحادوم</persName> ملك <unclear reason="illegible">
+                            <placeName>اتور</placeName>
+                        </unclear>
                </ab>
+               <milestone unit="first_narrative_section"/>
                <ab>
                   <placeName>ونينوى</placeName>
                   <placeName>والموصل</placeName> . وما جرا منه ومن</ab>
@@ -141,7 +142,7 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                   وكاتبه وكان</ab>
                <ab>ذو مال جزيل ورزق كثير. وكان ماهر حكيم فيلسوف</ab>
                <ab>ذو معرفه وراي وتدبير. وكان قد تزوج ستين امراه</ab>
-
+                <milestone unit="second_narrative_section"/>
                <cb/>
                <!-- ### tei:ab[@type = 'head'] marks head-like structures.
                users should understand that this is something LIKE a heading, but it's not a regular heading. 
@@ -178,11 +179,13 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                idea: a blank line before and after the line groups with padding on the side (side depends on reading direction?) -->
                <lg>
                   <l>هل <add place="below">اختبار</add> أقارنك بيوم صيفي؟</l>
-                  <l>انت أكثر جميلة وأكثر اعتدالا<surplus>:</surplus></l>
+                  <l>انت أكثر جميلة وأكثر اعتدالا<surplus>:</surplus>
+                        </l>
                </lg>
                <!-- ### colophons should be set apart and emphasized visually from the regular text.
                idea: different colour. -->
-               <ab><seg type="colophon">. نهاية<add place="footer">اختبار</add>
+               <ab>
+                        <seg type="colophon">. نهاية<add place="footer">اختبار</add>
                   النص</seg>وماتت.</ab>
 
                <!-- ### it should be somehow clear for users that they're viewing a vacant page (i.e. a page without content).
@@ -200,7 +203,9 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <ab>الخلايق كلها.<quote>كان كذلك ، لذا لم يكن كذلك.</quote> انا اطلب اليك ان
                   توهبني</ab>
                <!-- ### @rend says it all. -->
-               <ab><del rend="strikethrough">ولدًا حتى اتعزا به ويرثني ويحضر بموتي</del></ab>
+               <ab>
+                        <del rend="strikethrough">ولدًا حتى اتعزا به ويرثني ويحضر بموتي</del>
+                    </ab>
                <ab>ويغمض عيناي ويدفنني. فعند ذلك اتاه صوت</ab>
                <ab/>
                <ab>من السما قايلا. بحيث اتكلت اولًا على الاصنام</ab>
@@ -224,8 +229,11 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!-- ### tei:persName[@next] describes that a name begins at the end of a line and is continued at the beginning of the next line.
                same holds for place names.
                when highlighting entities these should be treated as one entity. -->
-               <ab><persName next="#ms1">أ</persName>لي من يقوم في خدمتي بعدك. فقال له </ab>
-               <ab><persName xml:id="ms1">حقر</persName></ab>
+               <ab>
+                        <persName next="#ms1">أ</persName>لي من يقوم في خدمتي بعدك. فقال له </ab>
+               <ab>
+                        <persName xml:id="ms1">حقر</persName>
+                    </ab>
                <ab type="colophon">هذا حيث ينتهي النص.</ab>
 
 
@@ -236,13 +244,17 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
 
                <!-- @@@ Brit.Mus._cod._Add._7209_Transcrip.karsh.3r14z.1.xml -->
                <!-- persName spanning over two lines -->
-               <ab>وقال للسياف كان اسمه <persName next="#Brit.Mus_7209_2">يبوس</persName></ab>
-               <ab><persName xml:id="Brit.Mus_7209_2">ميكمسكينكنتي</persName> قوم خد
+               <ab>وقال للسياف كان اسمه <persName next="#Brit.Mus_7209_2">يبوس</persName>
+                    </ab>
+               <ab>
+                        <persName xml:id="Brit.Mus_7209_2">ميكمسكينكنتي</persName> قوم خد
                      <persName>حيقر</persName> وامضي</ab>
 
                <!-- rubricated place and person names -->
                <!-- ### CSS for renditions is stated in the TEI header -->
-               <ab><hi rendition="#red"><placeName>مصر</placeName> الى عند
+               <ab>
+                        <hi rendition="#red">
+                            <placeName>مصر</placeName> الى عند
                         <persName>فرعون</persName> الملك </hi> ولما</ab>
 
                <!-- colophon -->
@@ -259,7 +271,8 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!-- @@@ Cod._Add._2886._Transcript._karsh.3r1dp.1.xml -->
                <!-- word spanning over two lines -->
                <ab>وختمهم بختم خاله <persName>حيقار</persName> . واما</ab>
-               <ab><lb break="no"/> هم في دار الملك ثم مضا ايضا وكتب</ab>
+               <ab>
+                        <lb break="no"/> هم في دار الملك ثم مضا ايضا وكتب</ab>
 
                <!-- deletion, marginal gloss with entities -->
                <ab>
@@ -343,8 +356,12 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!-- ### supplied text is text that modern editors have inserted.
                it is usually marked with square brackets (LC).
                idea: square brackets + lighter color -->
-               <head><seg xml:lang="ara"><supplied>.</supplied><add place="margin"
-                     >حقًا</add></seg><g>✓</g>
+               <head>
+                        <seg xml:lang="ara">
+                            <supplied>.</supplied>
+                            <add place="margin">حقًا</add>
+                        </seg>
+                        <g>✓</g>
                   <!-- this rubrication can be found in BnF_434_rev.3r676.4.xml -->
                   <hi rend="color(red)"> ܀ ܬܘܒ ܒܚܹܝܠ 
                   <!-- ### since the abbreviated text is the running text, this should be rendered.
@@ -354,9 +371,11 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                         <abbr>ܝܗ̈ܿ</abbr>
                         <expan>ܝܗ</expan>
                      </choice> ܟܿܬܒܢܐ ܩܲܠܝܼܠ ܡܼܢ ܡ̈ܬܼܠܐ <persName>ܕܐܚܝܼܩܲܪ܆</persName>
-                  </hi></head>
+                  </hi>
+                    </head>
                <ab>ܢܒܬܕܝ ܒܥܘܢ ܐܠܒܐܪܝ<add place="interlinear">اختبار</add> ܬܥܐܠܝ ܓܠ ܐܣܡܗ</ab>
-               <ab><lb break="no"/>ܘܬܥܐܠܝ ܕܟܪܗ ܐܠܝ ܐܠܐܒܕ. ܘܢܟܬܒ ܟܒܪ</ab>
+               <ab>
+                        <lb break="no"/>ܘܬܥܐܠܝ ܕܟܪܗ ܐܠܝ ܐܠܐܒܕ. ܘܢܟܬܒ ܟܒܪ</ab>
                <ab>ܐܠܚܟܝܡ ܐܠܡܐܗܪ <choice>
                      <sic>ܐܠܦܠܝܘܣ</sic>
                      <corr>ܐܠܦܠܝܣܘܦ</corr>
@@ -368,15 +387,18 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                   <placeName>ܘܢܝܢܘܝ</placeName>
                   <placeName>ܘܐܠܡܘܨܠ</placeName> ܘܡܐ ܓܪܐ ܡܥܗ ܘܡܢ</ab>
                <ab>ܐܒܢ ܐܟܬܗ <persName>ܢܐܕܐܢ</persName> . ܟܐܢ ܦܝ ܐܝܐܡ ܐܠܡܠܟ ܐܒܢ ܣܪܚܐܕܘܡ</ab>
-               <ab>ܡܠܟ ܐܪܜ <unclear reason="illegible"><placeName>ܐܬܘܪ</placeName></unclear>
+               <ab>ܡܠܟ ܐܪܜ <unclear reason="illegible">
+                            <placeName>ܐܬܘܪ</placeName>
+                        </unclear>
                   <placeName>ܘܢܝܢܘܝ</placeName> ܘܒܠܐܕܗܐ. ܪܓܠ ܚܟܝܡ ܝܩܐܠ </ab>
                <ab>ܠܗ <persName>ܚܝܩܐܪ</persName> ܘܟܐܢ ܘܙܝܪ ܐܠܡܠܟ <persName>ܣܢܚܐܪܝܒ</persName> ܘܟܐܬܒܗ
                   ܘܟܐܢ</ab>
                <ab>ܕܘ ܡܐܠ ܓܙܝܠ ܘܪܙܩ ܟܬܝܪ. ܘܟܐܢ ܡܐܗܪ ܚܟܝܡ ܦܝܠܣܘܦ</ab>
 
                <cb/>
-               <ab type="head"><seg xml:lang="ara">رأس <add place="header"
-                  >اختبار</add>متقاطع</seg></ab>
+               <ab type="head">
+                        <seg xml:lang="ara">رأس <add place="header">اختبار</add>متقاطع</seg>
+                    </ab>
                <ab>ܕܘ ܡܥܪܦܗ ܘܪܐܝ ܘܬܕܒܝܪ. ܘܟܐܢ ܩܕ ܬܙܘܓ ܣܬܝܢ ܐܡܪܐܗ</ab>
                <ab>ܘܒܢܝ ܠܟܠ ܘܐܚܕܗ ܡܢܗܢ ܡܩܨܘܪܗ. ܘܡܥ ܗܕܐ ܟܠܗ</ab>
                <ab>ܠܡ ܝܟܢ ܠܗ ܘܠܕ ܝܪܬܗ. ܘܟܐܢ ܟܬܝܪ ܐܠܗܡ ܠܐܓܠ ܕܠܟ</ab>
@@ -402,22 +424,30 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                   <!-- ### surplus text should be set apart visually.
                   a common way is setting the text in curly braces, but in addition to that other cues are possible (another color, different contrast?)
                   idea: curly braces (LC) + lighter color, e.g. {some text} -->
-                  <l>ܩܠܒ ܩܐܝܠܐ <surplus>:</surplus></l>
+                  <l>ܩܠܒ ܩܐܝܠܐ <surplus>:</surplus>
+                        </l>
                </lg>
-               <ab xml:lang="ara"><seg type="colophon">. نهاية<add place="footer">اختبار</add>
+               <ab xml:lang="ara">
+                        <seg type="colophon">. نهاية<add place="footer">اختبار</add>
                      النص</seg>وماتت.</ab>
 
                <!-- ### catchwords should be set apart from the running text and de-emphasized. 
                idea: lighter/other color-->
-               <ab><catchwords>الخلايق كلها</catchwords></ab>
+               <ab>
+                        <catchwords>الخلايق كلها</catchwords>
+                    </ab>
 
 
                <!-- random facsimile for invented empty page -->
                <pb facs="textgrid:3r1p0" n="82b"/>
                <pb facs="textgrid:3r1nz" n="83a"/>
-               <ab>ܐܠܟܠܐܝܩ ܟܠܗܐ.<quote><seg xml:lang="ara">كان كذلك ، لذا لم يكن كذلك.</seg></quote>
+               <ab>ܐܠܟܠܐܝܩ ܟܠܗܐ.<quote>
+                            <seg xml:lang="ara">كان كذلك ، لذا لم يكن كذلك.</seg>
+                        </quote>
                   ܐܢܐ ܐܛܠܒ ܐܠܝܟ ܐܢ ܬܘܗܒܢܝ</ab>
-               <ab><del rend="strikethrough">ܘܠܕܐ ܚܬܝ ܐܬܥܙܐ ܒܗ ܘܝܪܬܢܝ ܘܝܚܜܪ ܒܡܘܬܝ</del></ab>
+               <ab>
+                        <del rend="strikethrough">ܘܠܕܐ ܚܬܝ ܐܬܥܙܐ ܒܗ ܘܝܪܬܢܝ ܘܝܚܜܪ ܒܡܘܬܝ</del>
+                    </ab>
                <ab>ܘܝܓܡܜ ܥܝܢܐܝ ܘܝܕܦܢܢܝ. ܦܥܢܕ ܕܠܟ ܐܬܐܗ ܨܘܬ</ab>
                <ab/>
                <ab>ܡܢ ܐܠܣܡܐ ܩܐܝܠܐ. ܒܚܝܬ ܐܬܟܠܬ ܐܘܠܐ ܥܠܝ ܐܠܐܨܢܐܡ</ab>
@@ -438,8 +468,10 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <ab>ܣܪܝ ܘܡܕܒܪ ܕܘܠܬܝ. ܗܐ ܩܕ ܟܒܪܬ ܘܛܥܢܬ ܦܝ </ab>
                <ab>ܐܠܣܢ ܘܫܟܬ.<add place="interlinear">اختبار</add> ܘܩܪܒ ܘܩܬ ܡܘܬܟ ܘܘܦܐܬܟ. ܦܩܘܠ</ab>
                <ab>ܠܝ ܡܢ ܝܩܘܡ ܦܝ ܟܕܡܬܝ ܒܥܕܟ. ܦܩܐܠ ܠܗ</ab>
-               <ab type="colophon"><seg xml:lang="ara">هذا حيث ينتهي النص.</seg></ab>
-
+               <ab type="colophon">
+                        <seg xml:lang="ara">هذا حيث ينتهي النص.</seg>
+                    </ab>
+                <milestone unit="examples"/>
 
                <!-- @@@@ The following encoding has been taken directly from the source
                   material. In case there are Arabic passages, tei:seg with/or @xml:id has been added 
@@ -466,7 +498,8 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!--- ### since the text in tei:orig is the running text, it should be displayed.
                the text of supplied should, however, be available, too.
                idea: cf. tei:abbr/tei:expan? -->
-               <ab><damage>
+               <ab>
+                        <damage>
                      <orig>[܂]ܢܬ [܂܂܂]ܡܪ</orig>
                      <supplied>ܫܢܬ ܐܬܐܡܪ</supplied> ܗܼܘܐ ܠܝܼ܂ </damage>
                </ab>
@@ -496,12 +529,10 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!-- omitted text -->
                <!-- ### there is probably no need to indicate the reason for the supplied text.
                it could be serialized as tei:supplied without an attribute -->
-               <ab> ܥܲܝܢܹܗ ܢܕܲܒܲܪ ܒܲܝܬܹܿܗ܀ ܒܹܪܝ ܛܵܒܼ ܥܘܝܼܪ <supplied rend="omission"
-                     >ܒܥܲܝܢ̈ܘܗܝ</supplied> ܡܼܢ ܗܵܘܿ ܕܲܥܘܝܼܪ </ab>
+               <ab> ܥܲܝܢܹܗ ܢܕܲܒܲܪ ܒܲܝܬܹܿܗ܀ ܒܹܪܝ ܛܵܒܼ ܥܘܝܼܪ <supplied rend="omission">ܒܥܲܝܢ̈ܘܗܝ</supplied> ܡܼܢ ܗܵܘܿ ܕܲܥܘܝܼܪ </ab>
 
                <!-- gloss: below -->
-               <ab> ܠܲܝܬܿ ܠܹܗ܂ ܘܛܵܒܼܘܼ ܩܵܠܐ ܕܐܘܼܠ̈ܝܬܼܵܐ ܒܐܹܕ̈ܢܲܝ ܣܲܟܼܠܵܐ܉ <add place="below"
-                     >ܡܢ</add> ܙܡܵܪܵܐ </ab>
+               <ab> ܠܲܝܬܿ ܠܹܗ܂ ܘܛܵܒܼܘܼ ܩܵܠܐ ܕܐܘܼܠ̈ܝܬܼܵܐ ܒܐܹܕ̈ܢܲܝ ܣܲܟܼܠܵܐ܉ <add place="below">ܡܢ</add> ܙܡܵܪܵܐ </ab>
 
                <!-- damaged text with entity and illegible text -->
                <ab>
@@ -514,8 +545,7 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
 
                <!-- @@@ Cambrigde_Add_3497.3rb41.0.xml -->
                <!-- place name spanning over two lines -->
-               <ab xml:lang="ara">والعساكر وانطلقوا الى الصحره الى <placeName next="#Cambrigde_Add_3497_1"
-                     >بقعة</placeName>
+               <ab xml:lang="ara">والعساكر وانطلقوا الى الصحره الى <placeName next="#Cambrigde_Add_3497_1">بقعة</placeName>
                </ab>
                <ab xml:lang="ara">
                   <placeName xml:id="Cambrigde_Add_3497_1">نسرين</placeName> فلما وصلوا فنظر الملك
@@ -774,7 +804,8 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
                <!-- usage of tei:g in damaged area-->
                <ab>ܠܘܙܐ ܕܠܘܩܕܡ ܡܦܪܥ ܘܠܚܪܬܐ <damage>
                      <g type="quotation-mark"/>
-                  </damage></ab>
+                  </damage>
+                    </ab>
 
                <!-- addition with unknown type -->
                <ab> ܘܗܦܟܬ ܛܥܢܬ ܐܰܒܵܪܐ ܘܠܐ ܐܝܼܩܰܪ ܥܠܝ <add place="sidelong">ܐܝܟ</add>
@@ -783,4 +814,4 @@ Ideas (or parts of ideas) that have bee marked with LC refer to the Leiden Conve
          </text>
       </group>
    </text>
-</TEI>
+</TEI>
\ No newline at end of file
diff --git a/exist-app/modules/annotations.xqm b/exist-app/modules/annotations.xqm
index a69a5525..5dbc3c3c 100644
--- a/exist-app/modules/annotations.xqm
+++ b/exist-app/modules/annotations.xqm
@@ -18,13 +18,13 @@ declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
 declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
 declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
 
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
 import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
 import module namespace functx = "http://www.functx.com";
 import module namespace requestr="http://exquery.org/ns/request";
 import module namespace rest="http://exquery.org/ns/restxq";
 import module namespace tapi="http://ahikar.sub.uni-goettingen.de/ns/tapi" at "tapi.xqm";
 
-declare variable $anno:version := "1.8.0";
 declare variable $anno:ns := "http://ahikar.sub.uni-goettingen.de/ns/annotations";
 declare variable $anno:server := if(requestr:hostname() = "existdb") then doc("../expath-pkg.xml")/*/@name => replace("/$", "") else "http://localhost:8094/exist/restxq";
 
@@ -83,7 +83,7 @@ declare
     %output:method("json")
 function anno:collection-rest($collection as xs:string) {
     if (anno:are-resources-available($collection)) then
-        ($tapi:responseHeader200,
+        ($commons:responseHeader200,
         anno:make-annotationCollection($collection, (), $anno:server))
     else
         anno:get-404-header($collection)
@@ -232,7 +232,7 @@ declare
 function anno:annotationPage-for-collection-rest($collection as xs:string, 
 $document as xs:string) {
     if (anno:are-resources-available(($collection, $document))) then
-        ($tapi:responseHeader200,
+        ($commons:responseHeader200,
         anno:make-annotationPage($collection, $document, $anno:server))
         
     else
@@ -309,7 +309,7 @@ declare
 function anno:manifest-rest($collection as xs:string, 
 $document as xs:string) {
     if (anno:are-resources-available(($collection, $document))) then
-        ($tapi:responseHeader200,
+        ($commons:responseHeader200,
         anno:make-annotationCollection($collection, $document, $anno:server))
         
     else
@@ -340,7 +340,7 @@ declare
 function anno:annotationCollection-for-manifest-rest($collection as xs:string, 
 $document as xs:string, $page as xs:string) {
     if (anno:are-resources-available(($collection, $document))) then
-        ($tapi:responseHeader200,
+        ($commons:responseHeader200,
         anno:make-annotationCollection-for-manifest($collection, $document, $page, $anno:server))
         
     else
@@ -408,7 +408,7 @@ declare
 function anno:annotationPage-for-manifest-rest($collection as xs:string, 
 $document as xs:string, $page as xs:string) {
     if (anno:are-resources-available(($collection, $document))) then
-        ($tapi:responseHeader200,
+        ($commons:responseHeader200,
         anno:make-annotationPage-for-manifest($collection, $document, $page, $anno:server))
         
     else
@@ -619,7 +619,7 @@ declare function anno:are-resources-available($resources as xs:string+)
 as xs:boolean {
     let $availability :=
         for $resource in $resources return
-            doc-available($tapi:metaCollection || $resource || ".xml")
+            doc-available($commons:meta || $resource || ".xml")
     return
         not(functx:is-value-in-sequence(false(), $availability))
 };
@@ -872,7 +872,7 @@ declare function anno:get-prev-xml-uris($uri as xs:string) as xs:string* {
  : @return The URI of the given resource's parent aggregation
  :)
 declare function anno:get-parent-aggregation($uri as xs:string) as xs:string {
-  for $doc in collection($tapi:aggCollection) return
+  for $doc in collection($commons:agg) return
     if ($doc//@rdf:resource[matches(., $uri)]) then
         base-uri($doc) => substring-after("agg/") => substring-before(".xml")
     else
@@ -948,9 +948,9 @@ $page as xs:string) as xs:integer {
 declare function anno:get-document($uri as xs:string, $type as xs:string) {
     let $collection :=
         switch ($type)
-            case "agg" return $tapi:aggCollection
-            case "data" return $tapi:baseCollection || "/data/"
-            case "meta" return $tapi:metaCollection
+            case "agg" return $commons:agg
+            case "data" return $commons:tg-collection || "/data/"
+            case "meta" return $commons:meta
             default return error(QName($anno:ns, "ANNO02"), "Unknown type " || $type)
     return
         doc($collection || $uri || ".xml")
diff --git a/exist-app/modules/collate.xqm b/exist-app/modules/collate.xqm
new file mode 100644
index 00000000..635010eb
--- /dev/null
+++ b/exist-app/modules/collate.xqm
@@ -0,0 +1,231 @@
+xquery version "3.1";
+
+(:~
+ : This module extracts the chunks marked as important for the collation from
+ : the TEI files and uses them as an input for the plain text creation. These
+ : serve as a basis for the collation with CollateX in the project's CI/CD
+ : pipelines.
+ :)
+
+module namespace coll="http://ahikar.sub.uni-goettingen.de/ns/collate";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
+
+import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
+
+declare variable $coll:textgrid := "/db/apps/sade/textgrid";
+declare variable $coll:data := $coll:textgrid || "/data";
+declare variable $coll:txt := $coll:textgrid || "/txt";
+
+
+declare function coll:main() 
+as xs:string+ {
+    coll:create-txt-collection-if-not-available(),
+    for $text in coll:get-transcriptions-and-transliterations() return
+        let $relevant-text := coll:get-relevant-text($text)
+        let $file-name := coll:make-file-name($text)
+        return
+            xmldb:store($coll:txt, $file-name, $relevant-text, "text/plain")
+};
+
+declare function coll:create-txt-collection-if-not-available()
+as xs:string? {
+    if (xmldb:collection-available($coll:txt)) then
+        ()
+    else
+        xmldb:create-collection($coll:textgrid, "txt")
+};
+
+declare function coll:get-transcriptions-and-transliterations()
+as element(tei:text)+ {
+    collection($coll:data)//tei:text[@type = ("transcription", "transliteration")]
+        [coll:has-text-milestone(.)]
+};
+
+declare function coll:has-text-milestone($text as element(tei:text))
+as xs:boolean {
+    if ($text//tei:milestone) then
+        true()
+    else
+        false()
+};
+
+(:~
+ : An example for the file name is 
+ : syriac-Brit_Lib_Add_7200-3r131-transcription.txt
+ :)
+declare function coll:make-file-name($text as element(tei:text))
+as xs:string {
+    let $lang-prefix := coll:get-language-prefix($text)
+    let $title-from-metadata := coll:create-metadata-title-for-file-name($text)
+    let $uri-plus-text-type := coll:make-file-name-suffix($text)
+    return
+        $lang-prefix || "-" || $title-from-metadata || "-" || $uri-plus-text-type
+};
+
+declare function coll:get-language-prefix($text as element(tei:text))
+as xs:string? {
+    switch ($text/@type)
+        case "transcription" return
+            switch ($text/@xml:lang)
+                case "karshuni" return "karshuni"
+                case "ara" return "arabic"
+                case "syc" return "syriac"
+                default return ()
+                
+        (: although the transliteration may have another language than
+        the transcription, the latter remains decisive for the prefix :)
+        case "transliteration" return
+            switch ($text/root()//tei:text[@type = "transcription"]/@xml:lang)
+                case "ara" return "arabic"
+                case "karshuni" return "karshuni"
+                case "syc" return "syriac"
+                default return ()
+        default return ()
+};
+
+declare function coll:create-metadata-title-for-file-name($text as element(tei:text))
+as xs:string {
+    let $base-uri := coll:get-base-uri($text)
+    let $metadata := doc($base-uri => replace("/data/", "/meta/"))
+    return
+        $metadata//tgmd:title
+        => replace("[^a-zA-Z0-9]", "_")
+        => replace("[_]+", "_")
+};
+
+declare function coll:get-base-uri($text as element(tei:text))
+as xs:string{
+    base-uri($text)
+};
+
+declare function coll:make-file-name-suffix($text as element(tei:text))
+as xs:string {
+    let $base-uri := coll:get-base-uri($text)
+    let $file-name := coll:get-file-name($base-uri)
+    let $type := $text/@type
+    return
+        $file-name || "-" || $type || ".txt"
+};
+
+declare function coll:get-file-name($base-uri as xs:string)
+as xs:string {
+    tokenize($base-uri, "/")[last()]
+    => substring-before(".xml")
+};
+
+declare function coll:get-relevant-text($text as element(tei:text))
+as xs:string {
+    let $milestones := coll:get-milestones-in-text($text)
+    let $chunks := coll:get-chunks($milestones)
+    let $texts := coll:get-relevant-text-from-chunks($chunks)
+    return
+        string-join($texts, " ")
+};
+
+declare function coll:get-milestones-in-text($text as element(tei:text))
+as element(tei:milestone)+ {
+    $text//tei:milestone
+};
+
+declare function coll:get-chunks($milestones as element(tei:milestone)+)
+as element(tei:TEI)+ {
+    for $milestone in $milestones return
+        coll:get-chunk($milestone)
+};
+
+declare function coll:get-chunk($milestone as element(tei:milestone))
+as element(tei:TEI) {
+    let $root := $milestone/root()
+    let $end-of-chunk := coll:get-end-of-chunk($milestone)
+    return
+        fragment:get-fragment-from-doc(
+            $root,
+            $milestone,
+            $end-of-chunk,
+            false(),
+            true(),
+            (""))
+};
+
+declare function coll:get-end-of-chunk($milestone as element(tei:milestone))
+as node() {
+    if (coll:has-following-milestone($milestone)) then
+        coll:get-next-milestone($milestone)
+    else
+        $milestone/ancestor::tei:text[1]/tei:body/child::*[last()]
+};
+
+declare function coll:has-following-milestone($milestone as element(tei:milestone))
+as xs:boolean {
+    if ($milestone/following::tei:milestone[ancestor::tei:text[1] = $milestone/ancestor::tei:text[1]]) then
+        true()
+    else
+        false()
+};
+
+declare function coll:get-next-milestone($milestone as element(tei:milestone))
+as element(tei:milestone)? {
+    $milestone/following::tei:milestone[ancestor::tei:text[1] = $milestone/ancestor::tei:text[1]][1]
+};
+
+declare function coll:get-relevant-text-from-chunks($chunks as element(tei:TEI)+)
+as xs:string {
+    let $texts :=
+        for $chunk in $chunks return
+            coll:make-plain-text-from-chunk($chunk)
+    return
+        string-join($texts, " ")
+        => normalize-space()
+};
+
+declare function coll:make-plain-text-from-chunk($chunk as element(tei:TEI))
+as xs:string {
+    let $texts := coll:get-relevant-text-nodes($chunk)
+    let $prepared-texts :=
+        for $text in $texts return
+            coll:prepare-plain-text-creation($text)
+    return
+        coll:format-and-normalize-string($prepared-texts)
+};
+
+(:~ 
+ : The following nodes shouldn't be considered for the plain text creation:
+ : * sic (wrong text)
+ : * surplus (surplus text)
+ : * supplied (supplied by modern editors)
+ : * colophons
+ : * glyphs
+ : * unclear (text unclear)
+ : * catchwords (they simply serve to bind the books correctly and reduplicate text)
+ : * note (they have been added later by another scribe)
+ :)
+declare function coll:get-relevant-text-nodes($chunk as element(tei:TEI))
+as text()+ {
+    (($chunk//text()
+        [not(parent::tei:sic)]
+        [not(parent::tei:surplus)])
+        [not(parent::tei:supplied)])
+        [not(parent::tei:*[@type = "colophon"])]
+        [not(parent::tei:g)]
+        [not(parent::tei:unclear)]
+        [not(parent::tei:catchwords)]
+        [not(parent::tei:note)]
+};
+
+declare function coll:prepare-plain-text-creation($text as text())
+as xs:string {
+    if ($text/preceding-sibling::*[1][self::tei:lb[@break = "no"]]) then
+        "@" || $text
+    else
+        $text
+};
+
+declare function coll:format-and-normalize-string($strings as xs:string+)
+as xs:string {
+    string-join($strings, " ")
+    => replace(" @", "")
+    => replace("[\p{P}\n+]", "")
+    => replace("\s+", " ")
+};
diff --git a/exist-app/modules/commons.xqm b/exist-app/modules/commons.xqm
new file mode 100644
index 00000000..ece25019
--- /dev/null
+++ b/exist-app/modules/commons.xqm
@@ -0,0 +1,18 @@
+xquery version "3.1";
+
+module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons";
+
+declare variable $commons:expath-pkg := doc("../expath-pkg.xml");
+declare variable $commons:version := $commons:expath-pkg/*/@version;
+declare variable $commons:tg-collection := "/db/apps/sade/textgrid";
+declare variable $commons:data := $commons:tg-collection || "/data/";
+declare variable $commons:meta := $commons:tg-collection || "/meta/";
+declare variable $commons:agg := $commons:tg-collection || "/agg/";
+declare variable $commons:appHome := "/db/apps/ahikar";
+
+declare variable $commons:responseHeader200 :=
+    <rest:response>
+        <http:response xmlns:http="http://expath.org/ns/http-client" status="200">
+            <http:header name="Access-Control-Allow-Origin" value="*"/>
+        </http:response>
+    </rest:response>;
\ No newline at end of file
diff --git a/exist-app/modules/tapi-collection.xqm b/exist-app/modules/tapi-collection.xqm
new file mode 100644
index 00000000..e70bad22
--- /dev/null
+++ b/exist-app/modules/tapi-collection.xqm
@@ -0,0 +1,171 @@
+xquery version "3.1";
+
+(: 
+ : This module handles calls to the API on collection level, e.g.
+ : 
+ : /textapi/ahikar/3r9ps/collection.json
+ :)
+
+module namespace t-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection";
+
+declare namespace ore="http://www.openarchives.org/ore/terms/";
+declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
+declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
+import module namespace requestr="http://exquery.org/ns/request";
+import module namespace rest="http://exquery.org/ns/restxq";
+
+declare variable $t-coll:server := 
+    if(requestr:hostname() = "existdb") then
+        $commons:expath-pkg/*/@name => replace("/$", "")
+    else
+        "http://localhost:8094/exist/restxq";
+
+(:~
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
+ : @param $collection-uri The unprefixed TextGrid URI of a collection, e.g. '3r132'
+ : @return A collection object as JSON
+ :)
+declare
+    %rest:GET
+    %rest:HEAD
+    %rest:path("/textapi/ahikar/{$collection-uri}/collection.json")
+    %output:method("json")
+function t-coll:endpoint($collection-uri as xs:string)
+as item()+ {
+    $commons:responseHeader200,
+    t-coll:get-json($collection-uri, $t-coll:server)
+};
+
+(:~
+ : Returns information about the main collection for the project. This encompasses
+ : the key data described at https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object.
+ :
+ : This function should only be used for Ahiqar's edition object (textgrid:3r132).
+ : It serves as an entry point to the edition and contains all child aggregations with
+ : the XMLs and images in them.
+ :
+ : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
+ : @param $collection-uri The unprefixed TextGrid URI of a collection. For Ahiqar's main collection this is '3r132'.
+ : @param $server A string indicating the server. This parameter has been introduced to make this function testable.
+ : @return An object element containing all necessary information
+ :)
+declare function t-coll:get-json($collection-uri as xs:string,
+    $server as xs:string)
+as item()+ {
+    let $metadata-file := t-coll:get-metadata-file($collection-uri)
+    let $format-type := t-coll:get-format-type($metadata-file)
+    let $sequence := t-coll:make-sequence($collection-uri, $server)
+    let $annotationCollection-uri := t-coll:make-annotationCollection-uri($server, $collection-uri)
+
+    return
+    <object>
+        <textapi>{$commons:version}</textapi>
+        <title>
+            <title>The Story and Proverbs of Ahikar the Wise</title>
+            <type>{$format-type}</type>
+        </title>
+        <!-- this empty title element is necessary to force JSON to
+        generate an array instead of a simple object. -->
+        <title/>
+        <collector>
+            <role>collector</role>
+            <name>Prof. Dr. theol. Kratz, Reinhard Gregor</name>
+            <idref>
+                <base>http://d-nb.info/gnd/</base>
+                <id>115412700</id>
+                <type>GND</type>
+            </idref>
+        </collector>
+        <description>Main collection for the Ahikar project. Funded by DFG, 2019–2020. University of Göttingen</description>
+        <annotationCollection>{$annotationCollection-uri}</annotationCollection>
+        {$sequence}
+    </object>
+};
+
+declare function t-coll:get-aggregation($collection-uri as xs:string)
+as document-node() {
+    doc($commons:agg || $collection-uri || ".xml")
+};
+
+(:~
+ : Some "editions" that appear in the ore:aggregates list of a collection are
+ : actually no editions; They lack an XML file.
+ : 
+ : In order to not have them included in the list of "actual" editions, they
+ : have to be explicitly excluded.
+ : 
+ : @param $doc The root element of an aggregation object
+ : @return A list of ore:aggregates without the manifests to be excluded
+ : 
+ :)
+declare function t-coll:get-allowed-manifest-uris($aggregation-file as node())
+as xs:string+ {
+    let $not-allowed :=
+        (
+            "textgrid:3vp38"
+        )
+    let $allowed := 
+        for $aggregate in $aggregation-file//ore:aggregates return
+            $aggregate[@rdf:resource != $not-allowed]/@rdf:resource
+    return
+        for $uri in $allowed return
+            t-coll:remove-textgrid-prefix($uri)
+};
+
+declare function t-coll:remove-textgrid-prefix($uri as xs:string)
+as xs:string {
+    replace($uri, "textgrid:", "")
+};
+
+declare function t-coll:get-metadata-file($uri as xs:string)
+as document-node() {
+    doc($commons:meta || $uri || ".xml")
+};
+
+declare function t-coll:make-sequence($collection-uri as xs:string,
+    $server as xs:string)
+as element(sequence)+ {
+    let $aggregation := t-coll:get-aggregation($collection-uri)
+    let $allowed-manifest-uris := t-coll:get-allowed-manifest-uris($aggregation/*)
+    
+    for $manifest-uri in $allowed-manifest-uris return
+        let $manifest-metadata :=  t-coll:get-metadata-file($manifest-uri)
+        let $id := t-coll:make-id($server, $collection-uri, $manifest-uri)
+        let $type := t-coll:make-format-type($manifest-metadata)
+        return
+            <sequence>
+                <id>{$id}</id>
+                <type>{$type}</type>
+            </sequence>
+};
+
+declare function t-coll:make-id($server as xs:string,
+    $collection-uri as xs:string,
+    $manifest-uri as xs:string)
+as xs:string {
+    $server || "/api/textapi/ahikar/" || $collection-uri || "/" || $manifest-uri || "/manifest.json"
+};
+
+declare function t-coll:get-format-type($metadata as document-node())
+as xs:string {
+    $metadata//tgmd:format[1]/string()
+    => t-coll:make-format-type()
+};
+
+declare function t-coll:make-format-type($tgmd-format as xs:string)
+as xs:string {
+    switch ($tgmd-format)
+        case "text/tg.aggregation+xml" return "collection"
+        case "text/tg.edition+tg.aggregation+xml" return "manifest"
+        default return "manifest"
+};
+
+declare function t-coll:make-annotationCollection-uri($server as xs:string,
+    $collection-uri as xs:string)
+as xs:string {
+    $server || "/api/textapi/ahikar/" || $collection-uri || "/annotationCollection.json"
+};
diff --git a/exist-app/modules/tapi.xqm b/exist-app/modules/tapi.xqm
index 30e85347..9cae4a0c 100644
--- a/exist-app/modules/tapi.xqm
+++ b/exist-app/modules/tapi.xqm
@@ -7,7 +7,6 @@ xquery version "3.1";
  :
  : @author Mathias Göbel
  : @author Michelle Weidling
- : @version 1.9.1
  : @since 0.0.0
  : @see https://subugoe.pages.gwdg.de/ahiqar/api-documentation/page/text-api-specs/
  : :)
@@ -23,25 +22,14 @@ declare namespace test="http://exist-db.org/xquery/xqsuite";
 declare namespace tgmd="http://textgrid.info/namespaces/metadata/core/2010";
 declare namespace xhtml="http://www.w3.org/1999/xhtml";
 
+import module namespace coll="http://ahikar.sub.uni-goettingen.de/ns/collate" at "collate.xqm";
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "commons.xqm";
 import module namespace fragment="https://wiki.tei-c.org/index.php?title=Milestone-chunk.xquery" at "fragment.xqm";
 import module namespace functx="http://www.functx.com";
 import module namespace requestr="http://exquery.org/ns/request";
 import module namespace rest="http://exquery.org/ns/restxq";
 
-declare variable $tapi:version := "1.9.1";
-declare variable $tapi:server := if(requestr:hostname() = "existdb") then doc("../expath-pkg.xml")/*/@name => replace("/$", "") else "http://localhost:8094/exist/restxq";
-declare variable $tapi:baseCollection := "/db/apps/sade/textgrid";
-declare variable $tapi:dataCollection := $tapi:baseCollection || "/data/";
-declare variable $tapi:metaCollection := $tapi:baseCollection || "/meta/";
-declare variable $tapi:aggCollection := $tapi:baseCollection || "/agg/";
-declare variable $tapi:appHome := "/db/apps/ahikar";
-
-declare variable $tapi:responseHeader200 :=
-    <rest:response>
-        <http:response xmlns:http="http://expath.org/ns/http-client" status="200">
-            <http:header name="Access-Control-Allow-Origin" value="*"/>
-        </http:response>
-    </rest:response>;
+declare variable $tapi:server := if(requestr:hostname() = "existdb") then $commons:expath-pkg/*/@name => replace("/$", "") else "http://localhost:8094/exist/restxq";
 
 (:~
  : Shows information about the currently installed application.
@@ -55,7 +43,7 @@ declare
     %output:method("json")
 function tapi:info-rest()
 as item()+ {
-    $tapi:responseHeader200,
+    $commons:responseHeader200,
     tapi:info()
 };
 
@@ -73,8 +61,8 @@ as element(descriptors) {
             <uri>{ requestr:uri() }</uri>
         </request>
         {
-            tapi:remove-whitespaces(doc($tapi:appHome || "/expath-pkg.xml")),
-            tapi:remove-whitespaces(doc($tapi:appHome || "/repo.xml"))
+            tapi:remove-whitespaces(doc($commons:appHome || "/expath-pkg.xml")),
+            tapi:remove-whitespaces(doc($commons:appHome || "/repo.xml"))
         }
     </descriptors>
 };
@@ -92,85 +80,6 @@ declare function tapi:remove-whitespaces($doc as document-node()) as document-no
     $doc => serialize() => replace("[\s]{2,}", "") => replace("[\n]", "") => parse-xml()
 };
 
-(:~
- : Returns information about a given collection.
- :
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
- : @param $collection The unprefixed TextGrid URI of a collection, e.g. '3r132'
- : @return A collection object as JSON
- :)
-declare
-    %rest:GET
-    %rest:HEAD
-    %rest:path("/textapi/ahikar/{$collection}/collection.json")
-    %output:method("json")
-function tapi:collection-rest($collection as xs:string)
-as item()+ {
-    $tapi:responseHeader200,
-    tapi:collection($collection, $tapi:server)
-};
-
-
-(:~
- : Returns information about the main collection for the project. This encompasses
- : the key data described at https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object.
- :
- : This function should only be used for Ahiqar's edition object (textgrid:3r132).
- : It serves as an entry point to the edition and contains all child aggregations with
- : the XMLs and images in them.
- :
- : @see https://subugoe.pages.gwdg.de/emo/text-api/page/specs/#collection-object
- : @param $collection The unprefixed TextGrid URI of a collection. For Ahiqar's main collection this is '3r132'.
- : @param $server A string indicating the server. This parameter has been introduced to make this function testable.
- : @return An object element containing all necessary information
- :)
-declare function tapi:collection($collection as xs:string, $server as xs:string)
-as item()+ {
-    let $aggregation := doc($tapi:aggCollection || $collection || ".xml")
-    let $allowed-manifests := tapi:exclude-unwanted-manifests($aggregation/*)
-    let $meta := collection($tapi:metaCollection)//tgmd:textgridUri[starts-with(., "textgrid:" || $collection)]/root()
-    let $sequence :=
-        for $i in $allowed-manifests/string(@*:resource)
-            let $metaObject := collection($tapi:metaCollection)//tgmd:textgridUri[starts-with(., $i)]/root()
-            return
-                <sequence>
-                    <id>{$server}/api/textapi/ahikar/{$collection}/{substring-after($i, ":")}/manifest.json</id>
-                    <type>{
-                        if($collection = "3r84g")
-                        then
-                            "manifest"
-                        else
-                            ($metaObject//tgmd:format)[1]
-                            => string()
-                            => tapi:type()
-                    }</type>
-                </sequence>
-    return
-    <object>
-        <textapi>{$tapi:version}</textapi>
-        <title>
-            <title>The Story and Proverbs of Ahikar the Wise</title>
-            <type>{$meta//tgmd:format => string() => tapi:type()}</type>
-        </title>
-        <!-- this empty title element is necessary to force JSON to
-        generate an array instead of a simple object. -->
-        <title/>
-        <collector>
-            <role>collector</role>
-            <name>Prof. Dr. theol. Kratz, Reinhard Gregor</name>
-            <idref>
-                <base>http://d-nb.info/gnd/</base>
-                <id>115412700</id>
-                <type>GND</type>
-            </idref>
-        </collector>
-        <description>Main collection for the Ahikar project. Funded by DFG, 2019–2020. University of Göttingen</description>
-        <annotationCollection>{$server}/api/textapi/ahikar/{$collection}/annotationCollection.json</annotationCollection>
-        {$sequence}
-    </object>
-};
-
 
 (:~
  : Returns information about a given document as specified at
@@ -188,7 +97,7 @@ declare
     %output:method("json")
 function tapi:manifest-rest($collection as xs:string, $document as xs:string)
 as item()+ {
-    $tapi:responseHeader200,
+    $commons:responseHeader200,
     tapi:manifest($collection, $document, $tapi:server)
 };
 
@@ -208,10 +117,10 @@ as item()+ {
 declare function tapi:manifest($collection as xs:string, $document as xs:string,
 $server as xs:string)
 as element(object) {
-    let $aggNode := doc($tapi:aggCollection || $document || ".xml")
-    let $metaNode := doc($tapi:baseCollection || "/meta/" || $document || ".xml")
+    let $aggNode := doc($commons:agg || $document || ".xml")
+    let $metaNode := doc($commons:tg-collection || "/meta/" || $document || ".xml")
     let $documentUri := $aggNode//ore:aggregates[1]/@rdf:resource => substring-after(":")
-    let $documentNode := doc($tapi:dataCollection || $documentUri || ".xml")
+    let $documentNode := doc($commons:data || $documentUri || ".xml")
     let $sequence :=
         for $page in $documentNode//tei:pb[@facs]/string(@n)
         let $uri := "/api/textapi/ahikar/" || $collection || "/" || $document || "-" ||  $page || "/latest/item.json"
@@ -224,7 +133,7 @@ as element(object) {
             
     return
     <object>
-        <textapi>{$tapi:version}</textapi>
+        <textapi>{$commons:version}</textapi>
         <id>{$id}</id>
         <label>{string($metaNode//tgmd:title)}</label>
         {
@@ -381,7 +290,7 @@ declare
     %output:method("json")
 function tapi:item-rest($collection as xs:string, $document as xs:string,
 $page as xs:string) as item()+ {
-    $tapi:responseHeader200,
+    $commons:responseHeader200,
     tapi:item($collection, $document, $page, $tapi:server)
 };
 
@@ -398,14 +307,14 @@ $page as xs:string) as item()+ {
 declare function tapi:item($collection as xs:string, $document as xs:string,
 $page as xs:string, $server as xs:string) 
 as element(object) {
-    let $aggNode := doc($tapi:aggCollection || $document || ".xml")
+    let $aggNode := doc($commons:agg || $document || ".xml")
     let $teiUri :=
         if($aggNode)
             then $aggNode//ore:aggregates[1]/@rdf:resource => substring-after(":")
         else $document
-    let $image := doc($tapi:dataCollection || $teiUri || ".xml")//tei:pb[@n = $page]/@facs => substring-after("textgrid:")
+    let $image := doc($commons:data || $teiUri || ".xml")//tei:pb[@n = $page]/@facs => substring-after("textgrid:")
     
-    let $xml := doc($tapi:dataCollection || $teiUri || ".xml")
+    let $xml := doc($commons:data || $teiUri || ".xml")
     let $title := $xml//tei:title[@type = "main"]/string()
     let $iso-languages := 
         $xml//tei:language[@xml:base = "https://iso639-3.sil.org/code/"]/@ident/string()
@@ -419,7 +328,7 @@ as element(object) {
     
     return
     <object>
-        <textapi>{$tapi:version}</textapi>
+        <textapi>{$commons:version}</textapi>
         <title>{$title}</title>
         <type>page</type>
         <n>{$page}</n>
@@ -463,7 +372,7 @@ declare
     %output:indent("no")
 function tapi:content-rest($document as xs:string, $page as xs:string)
 as item()+ {
-    $tapi:responseHeader200,
+    $commons:responseHeader200,
     tapi:content($document, $page)
 };
 
@@ -477,7 +386,7 @@ as item()+ {
  :)
 declare function tapi:content($document as xs:string, $page as xs:string)
 as element(div) {
-    let $documentPath := $tapi:dataCollection || $document || ".xml"
+    let $documentPath := $commons:data || $document || ".xml"
     let $TEI :=
         if($page)
         then
@@ -528,7 +437,7 @@ declare
     %output:method("binary")
 function tapi:images-rest($uri as xs:string)
 as item()+ {
-    $tapi:responseHeader200,
+    $commons:responseHeader200,
     hc:send-request(
         <hc:request method="GET"
         href="https://textgridlab.org/1.0/digilib/rest/IIIF/textgrid:{$uri};sid={environment-variable('TEXTGRID.SESSION')}/full/,2000/0/native.jpg"
@@ -550,35 +459,22 @@ declare
     %rest:GET
     %rest:HEAD
     %rest:path("/content/{$document}.txt")
+    %rest:query-param("type", "{$type}", "transcription")
     %output:method("text")
-function tapi:text-rest($document as xs:string) as item()+ {
-    $tapi:responseHeader200,
-    tapi:text($document)
-};
-
-
-(:~
- : Returns the text of a given document as xs:string. Text nodes in tei:sic are
- : omitted since they are obviously typos or the like and are corrected in their
- : sibling tei:corr.
- :
- : @param $document The unprefixed TextGrid URI of a document, e.g. '3r671'
- : @return A string encompassing the whole text
- : @deprecated As of being buggy this function has been replaced by tapi:create-plain-text.
- :)
-declare function tapi:text($document as xs:string) as xs:string {
-    let $documentPath := $tapi:dataCollection || $document || ".xml"
-    let $TEI := doc($documentPath)//tei:text[@type = "transcription"]
-    let $text :=
-        ($TEI//text()
-            [not(parent::tei:sic)]
-        ) => string-join() => replace("\n+", "") => replace("\s+", " ")
+function tapi:text-rest($document as xs:string, $type)
+as item()+ {
+    let $text := tapi:get-TEI-text($document, $type)
+    let $TEI :=
+        element tei:TEI {
+            $text
+        }
     return
-        $text
+        ( 
+            $commons:responseHeader200,
+            coll:make-plain-text-from-chunk($TEI)
+        )
 };
 
-
-
 (:~
  : Endpoint to deliver all plain texts in a zip container. This comes in handy
  : e.g. for applications doing text analysis.
@@ -591,9 +487,9 @@ declare
     %rest:path("/content/ahikar-plain-text.zip")
     %output:method("binary")
 function tapi:text-rest() as item()+ {
-    let $prepare := tapi:zip-text()
+    let $prepare := coll:main()
     return
-        $tapi:responseHeader200,
+        $commons:responseHeader200,
         tapi:compress-to-zip()
 };
 
@@ -605,129 +501,7 @@ function tapi:text-rest() as item()+ {
  :)
 declare function tapi:compress-to-zip()
 as xs:base64Binary* {
-    compression:zip(xs:anyURI($tapi:baseCollection || "/txt/"), false())
-};
-
-
-(:~
- : Creates a plain text version of the transcription section of each Ahiqar XML.
- : This is stored to /db/apps/sade/textgrid/txt/.
- :
- : @return A string indicating the location where a plain text has been stored to
- :)
-declare function tapi:zip-text() as xs:string+ {
-    let $txtCollection := $tapi:baseCollection || "/txt/"
-    let $collection := collection($tapi:dataCollection)
-    let $check-text-collection :=
-        if( xmldb:collection-available($txtCollection) )
-        then true()
-        else xmldb:create-collection($tapi:baseCollection, "txt")
-    let $TEIs := $collection//tei:text[@type = ("transcription", "transliteration")][matches(descendant::text(), "[\w]")]
-    return
-        for $TEI in $TEIs
-            let $baseUri := $TEI/base-uri()
-            let $tgBaseUri := ($baseUri => tokenize("/"))[last()]
-            let $type := $TEI/@type
-            let $prefix :=
-            switch ($type)
-                case "transcription" return
-                    switch ($TEI/@xml:lang)
-                        case "karshuni" return "karshuni"
-                        case "ara" return "arabic"
-                        case "syc" return "syriac"
-                        default return ()
-                        
-                (: although the transliteration may have another language than
-                the transcription, the latter remains decisive for the prefix :)
-                case "transliteration" return
-                    (: transliterations are always encoded before transcriptions :)
-                    switch ($TEI/following-sibling::tei:text[@type = "transcription"]/@xml:lang)
-                        case "ara" return "arabic"
-                        case "karshuni" return "karshuni"
-                        case "syc" return "syc"
-                        default return ()
-                default return ()
-                
-            let $uri := $tgBaseUri => replace(".xml", concat("-", $type, ".txt"))
-            let $text := tapi:create-plain-text($TEI)
-
-            let $metadata := doc($baseUri => replace("/data/", "/meta/"))
-            let $metaTitle := $metadata//tgmd:title => replace("[^a-zA-Z]", "_")
-            return
-                xmldb:store($txtCollection, $prefix || "-" || $metaTitle || "-" || $uri, $text, "text/plain")
-};
-
-
-(:~ 
- : Takes all relevant text nodes of a given TEI and transforms them in a 
- : normalized plain text.
- : 
- : The following nodes shouldn't be considered for the plain text creation:
- : * sic (wrong text)
- : * surplus (surplus text)
- : * supplied (supplied by modern editors)
- : * colophons
- : * glyphs
- : * unclear (text unclear)
- : * catchwords (they simply serve to bind the books correctly and reduplicate text)
- : * note (they have been added later by another scribe)
- : 
- : @param $TEI A TEI document
- : @return A string with all relevant text nodes.
- : 
- :)
-declare function tapi:create-plain-text($TEI as node()) as xs:string {
-    (($TEI//text()
-        [not(parent::tei:sic)]
-        [not(parent::tei:surplus)])
-        [not(parent::tei:supplied)])
-        [not(parent::tei:*[@type = "colophon"])]
-        [not(parent::tei:g)]
-        [not(parent::tei:unclear)]
-        [not(parent::tei:catchwords)]
-        [not(parent::tei:note)]
-    => string-join()
-    => replace("\p{P}", "")
-    => replace("\n+", "")
-    => replace("\s+", " ")
-};
-
-
-(:~ 
- : An API endpoint to get the plain text version of a resource.
- : 
- : It is possible to have either an edition or an XML file as input;
- : The function always selects the underlying XML file for txt creation.
- : 
- : Since we have different types of texts in Ahikar (depending on the manuscript's
- : language), we also introduced a query parameter to distinguish which text type
- : should serve as a base for creating the plain text.
- : The parameter defaults to 'transcription' which is the prevailing type.
- : 
- : Sample API calls:
- : 
- : * /textapi/ahikar/3r84h/3r176.txt
- : * /textapi/ahikar/3r84h/3r176.txt?type=transliteration
- : 
- : @param $collection The URI of the collection a resource is part of
- : @param $document The URI of an edition object or XML file that is to be serialized as plain text
- : @param $type A query parameter indicating which type of text should be serialized. Defaults to 'transcription'
- : @return The plain text of a given resource excluding the TEI header
- :)
-declare
-    %rest:GET
-    %rest:HEAD
-    %rest:path("/textapi/ahikar/{$collection}/{$document}.txt")
-    %rest:query-param("type", "{$type}", "transcription")
-    %output:method("text")
-function tapi:txt($collection as xs:string, $document as xs:string,
-$type) as item()+ {
-    let $TEI := tapi:get-TEI-text($document, $type)
-    return
-        (
-            $tapi:responseHeader200,
-            tapi:create-plain-text($TEI)
-        )
+    compression:zip(xs:anyURI($commons:tg-collection || "/txt/"), false())
 };
 
 
@@ -747,20 +521,41 @@ as element(tei:text) {
     let $format := tapi:get-format($document)
     return
         if ($format = "text/xml") then
-            doc($tapi:dataCollection || $document || ".xml")//tei:text[@type = $type]
+            doc($commons:data || $document || ".xml")//tei:text[@type = $type]
         (: in this case the document is an edition which forces us to pick the
         text/xml file belonging to it :)
         else
-            let $edition := doc($tapi:aggCollection || $document || ".xml")
-            let $aggregated := for $agg in $edition//ore:aggregates/@rdf:resource return replace($agg, "textgrid:", "")
-            let $xml :=
-                for $agg in $aggregated return
-                    if (tapi:get-format($agg) = "text/xml") then
-                        $agg
-                    else
-                        ()
+            let $xml := tapi:get-tei-file-name-of-edition($document)
             return
-                 doc($tapi:dataCollection || $xml || ".xml")//tei:text[@type = $type]
+                 tapi:get-text-of-type($xml, $type)
+};
+
+declare function tapi:get-tei-file-name-of-edition($document as xs:string)
+as xs:string {
+    let $aggregates := tapi:get-edition-aggregates-without-uri-namespace($document)
+    return
+        tapi:find-xml-in-aggregates($aggregates)
+};
+
+declare function tapi:get-edition-aggregates-without-uri-namespace($document as xs:string)
+as xs:string+ {
+    let $edition := doc($commons:agg || $document || ".xml")
+    for $agg in $edition//ore:aggregates/@rdf:resource return
+        replace($agg, "textgrid:", "")
+};
+
+declare function tapi:find-xml-in-aggregates($aggregates as xs:string+)
+as xs:string {
+    for $agg in $aggregates return
+        if (tapi:get-format($agg) = "text/xml") then
+            $agg
+        else
+            ()
+};
+
+declare function tapi:get-text-of-type($uri as xs:string, $type as xs:string)
+as element(tei:text) {
+    doc($commons:data || $uri || ".xml")//tei:text[@type = $type]
 };
 
 (:~
@@ -770,24 +565,10 @@ as element(tei:text) {
  : @return The resource's format as tgmd:format
  :)
 declare function tapi:get-format($uri as xs:string) as xs:string {
-    doc($tapi:metaCollection || $uri || ".xml")//tgmd:format
+    doc($commons:meta || $uri || ".xml")//tgmd:format
 };
 
 
-(:~
- : Returns the type of a resource in a SUB TextAPI compliant way.
- :
- : @param $format A string value of tgmd:format as used in the TextGrid metadata
- : @return A string indicating the format in a way compliant to the SUB TextAPI
- :)
-declare %private function tapi:type($format as xs:string)
-as xs:string {
-    switch ($format)
-        case "text/tg.aggregation+xml" return "collection"
-        case "text/tg.edition+tg.aggregation+xml" return "manifest"
-        default return "manifest"
-};
-
 declare function tapi:add-IDs($tei as element(tei:TEI)) as element(tei:TEI) {
     tapi:add-IDs-recursion($tei)
 };
@@ -813,24 +594,3 @@ declare function tapi:add-IDs-recursion($nodes as node()*) as node()* {
                 tapi:add-IDs-recursion($node/node())
             }
 };
-
-
-(:~
- : Some "editions" that appear in the ore:aggregates list of a collection are
- : actually no editions; They lack an XML file.
- : 
- : In order to not have them included in the list of "actual" editions, they
- : have to be explicitly excluded.
- : 
- : @param $doc The root element of an aggregation object
- : @return A list of ore:aggregates without the manifests to be excluded
- : 
- :)
-declare function tapi:exclude-unwanted-manifests($doc as node()) as node()* {
-    let $not-allowed :=
-        (
-            "textgrid:3vp38"
-        )
-    for $aggregate in $doc//ore:aggregates return
-        $aggregate[@rdf:resource != $not-allowed]
-};
diff --git a/exist-app/tests-runner.xq b/exist-app/tests-runner.xq
index df227c94..6eeb8e79 100644
--- a/exist-app/tests-runner.xq
+++ b/exist-app/tests-runner.xq
@@ -4,9 +4,14 @@ xquery version "3.1";
  : execution.
  :)
 
+import module namespace tct="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests" at "tests/tapi-collection-tests.xqm";
 import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
 import module namespace tests="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests" at "tests.xqm";
-
+import module namespace coll-tests="http://ahikar.sub.uni-goettingen.de/ns/coll-tests" at "tests/collate-tests.xqm";
+import module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests" at "tests/commons-tests.xqm";
 
 (: test API endpoints :)
-test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests"))
\ No newline at end of file
+test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/tests")),
+test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/coll-tests")),
+test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/commons-tests")),
+test:suite(util:list-functions("http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests"))
\ No newline at end of file
diff --git a/exist-app/tests.xqm b/exist-app/tests.xqm
index 8b05d45f..be645f1d 100644
--- a/exist-app/tests.xqm
+++ b/exist-app/tests.xqm
@@ -10,8 +10,6 @@ xquery version "3.1";
 module namespace tests="http://ahikar.sub.uni-goettingen.de/ns/tapi/tests";
 
 declare namespace http = "http://expath.org/ns/http-client";
-declare namespace ore="http://www.openarchives.org/ore/terms/";
-declare namespace rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
 declare namespace tei="http://www.tei-c.org/ns/1.0";
 
 import module namespace anno="http://ahikar.sub.uni-goettingen.de/ns/annotations" at "modules/annotations.xqm";
@@ -63,8 +61,7 @@ function tests:_test-setup() as xs:string+ {
         <msIdentifier>
             <institution>University of Cambridge - Cambridge University Library</institution>
         </msIdentifier>
-    </teiHeader>),
-    tapi:zip-text()
+    </teiHeader>)
 };
 
 declare
@@ -89,23 +86,6 @@ function tests:api-info()  as item() {
 };
 
 
-declare
-    (: check if all parts are present.
-     : no further tests are needed since the content has been tested while testing
-     : the underlying function. :)
-    %test:assertXPath("map:contains($result, 'title')")
-    %test:assertXPath("map:contains($result, 'collector')")
-    %test:assertXPath("map:contains($result, 'description')")
-    %test:assertXPath("map:contains($result, 'sequence')")
-function tests:collection-rest()  as item() {
-    let $url := $tests:restxq || "/textapi/ahikar/ahiqar_collection/collection.json"
-    let $req := <http:request href="{$url}" method="get">
-                        <http:header name="Connection" value="close"/>
-                   </http:request>
-    return http:send-request($req)[2] => util:base64-decode() => parse-json()
-};
-
-
 declare
     (: check if all parts are present.
      : no further tests are needed since the content has been tested by testing
@@ -168,6 +148,7 @@ declare
      : no further tests are needed since the content has been tested while testing
      : the underlying function. :)
     %test:assertExists
+    %test:pending
 function tests:content-zip() as xs:base64Binary {
     let $url := $tests:restxq || "/content/ahikar-plain-text.zip"
     let $req := <http:request href="{$url}" method="get">
@@ -197,16 +178,6 @@ function tests:content-txt() as xs:string {
  : all the functions that contribute to but do not define RESTXQ endpoints
  :)
 
-declare
-    %test:args("ahiqar_collection", "http://localhost:8080/exist/restxq")
-    (: tests if the object is created at all :)
-    %test:assertXPath("$result//*[local-name(.) = 'title'] = 'The Story and Proverbs of Ahikar the Wise' ")
-    (: tests if the sequence construction works properly :)
-    %test:assertXPath("$result//*/string() = 'http://localhost:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json' ")
-function tests:collection($collection as xs:string, $server as xs:string) as item()+ {
-    tapi:collection($collection, $server)
-};
-
 
 declare
     %test:args("ahiqar_collection", "ahiqar_agg", "http://localhost:8080/exist/restxq")
@@ -254,12 +225,6 @@ function tests:html-creation($document as xs:string, $page as xs:string) as elem
     tapi:content($document, $page)
 };
 
-declare
-    %test:assertTrue
-function tests:zip-text() as item()+ {
-    xmldb:collection-available("/db/apps/sade/textgrid/txt")
-};
-
 
 declare
     %test:assertExists
@@ -268,15 +233,6 @@ function tests:compress-text() as xs:base64Binary {
 };
 
 
-declare
-    %test:assertEquals("test test3 Berlin")
-function tests:plain-text() as xs:string {
-    let $tei := doc("/db/test-records/sample-tei.xml")/*
-    return
-        tapi:create-plain-text($tei)
-};
-
-
 declare
     %test:args("ahiqar_sample")
     %test:assertEquals("text/xml")
@@ -401,22 +357,44 @@ function tests:remove-whitespaces() as document-node() {
         tapi:remove-whitespaces($doc)
 };
 
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
+function tests:get-tei-file-name-of-edition($document as xs:string) {
+    tapi:get-tei-file-name-of-edition($document)
+};
+
+declare
+    %test:args("ahiqar_agg") %test:assertEquals("ahiqar_sample")
+function tests:get-edition-aggregates-without-uri-namespace($document as xs:string) {
+    tapi:get-edition-aggregates-without-uri-namespace($document)
+};
+
+declare
+    %test:args("ahiqar_sample") %test:assertEquals("ahiqar_sample")
+function tests:find-xml-in-aggregates($aggregates as xs:string+) {
+    tapi:find-xml-in-aggregates($aggregates)
+};
+
+declare 
+    %test:args("ahiqar_sample", "transliteration") %test:assertXPath("$result[@type = 'transliteration']")
+function tests:get-text-of-type($uri as xs:string, $type as xs:string) {
+    tapi:get-text-of-type($uri, $type)
+};
 
 declare
-    %test:assertXPath("not($result//@rdf:resource[. = 'textgrid:3vp38'])")
-    %test:assertXPath("$result//@rdf:resource[. = 'textgrid:3rx14']")
-function tests:exclude-aggregated-manifests() {
-    let $collection-metadata :=
-        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-            <rdf:Description xmlns:tei="http://www.tei-c.org/ns/1.0" rdf:about="textgrid:3r9ps.0">
-                <ore:aggregates rdf:resource="textgrid:3rbm9"/>
-                <ore:aggregates rdf:resource="textgrid:3rbmc"/>
-                <ore:aggregates rdf:resource="textgrid:3rx14"/>
-                <ore:aggregates rdf:resource="textgrid:3vp38"/>
-            </rdf:Description>
-        </rdf:RDF>
+    %test:assertTrue
+function tests:is-txt-api-available() {
+    let $url := $tests:restxq || "content/ahiqar_sample.txt"
     return
-        tapi:exclude-unwanted-manifests($collection-metadata)
+        local:is-endpoint-http200($url)
+};
+
+declare function tests:txt() {
+    let $url := $tests:restxq || "textapi/ahiqar/ahiqar_collection/ahiqar_sample.txt"
+    let $req := <http:request href="{$url}" method="get">
+                        <http:header name="Connection" value="close"/>
+                   </http:request>
+    return http:send-request($req)[2] => util:base64-decode()
 };
 
 
@@ -425,81 +403,81 @@ function tests:exclude-aggregated-manifests() {
  : * AnnotationAPI * 
  : *****************
  :)
+
+declare
+    %test:args("ahiqar_sample", "data")
+    %test:assertXPath("$result//*[local-name(.) = 'TEI']")
+function tests:anno-get-document($uri as xs:string, $type as xs:string) as document-node() {
+    anno:get-document($uri, $type)
+};
+
+
+(:declare:)
+(:    %test:args("3r679", "114r"):)
+(:    %test:assertEquals("0"):)
+(:function tests:anno-determine-start-index-for-page($uri as xs:string, $page as xs:string) {:)
+(:    anno:determine-start-index-for-page($uri, $page):)
+(:};:)
+(::)
+(::)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("16"):)
+(:function tests:anno-determine-start-index($uri as xs:string) {:)
+(:    anno:determine-start-index($uri):)
+(:};:)
 (::)
 (:declare:)
-(:    %test:args("ahiqar_sample", "data"):)
-(:    %test:assertXPath("$result//*[local-name(.) = 'TEI']"):)
-(:function tests:anno-get-document($uri as xs:string, $type as xs:string) as document-node() {:)
-(:    anno:get-document($uri, $type):)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("3r679"):)
+(:function tests:anno-get-parent-aggregation($uri as xs:string) {:)
+(:    anno:get-parent-aggregation($uri):)
 (:};:)
 (::)
 (::)
-(:(:declare:):)
-(:(:    %test:args("3r679", "114r"):):)
-(:(:    %test:assertEquals("0"):):)
-(:(:function tests:anno-determine-start-index-for-page($uri as xs:string, $page as xs:string) {:):)
-(:(:    anno:determine-start-index-for-page($uri, $page):):)
-(:(:};:):)
-(:(::):)
-(:(::):)
-(:(:declare:):)
-(:(:    %test:args("3r131"):):)
-(:(:    %test:assertEquals("16"):):)
-(:(:function tests:anno-determine-start-index($uri as xs:string) {:):)
-(:(:    anno:determine-start-index($uri):):)
-(:(:};:):)
-(:(::):)
-(:(:declare:):)
-(:(:    %test:args("3r131"):):)
-(:(:    %test:assertEquals("3r679"):):)
-(:(:function tests:anno-get-parent-aggregation($uri as xs:string) {:):)
-(:(:    anno:get-parent-aggregation($uri):):)
-(:(:};:):)
-(:(::):)
-(:(::):)
-(:(:declare:):)
-(:(:    %test:args("3r131"):):)
-(:(:    %test:assertEquals("114r", "114v"):):)
-(:(:function tests:anno-get-pages-in-TEI($uri as xs:string) {:):)
-(:(:    anno:get-pages-in-TEI($uri):):)
-(:(:};:):)
-(:(::):)
-(:(::):)
-(:(:declare:):)
-(:(:    %test:args("3r679"):):)
-(:(:    %test:assertTrue:):)
-(:(:function tests:anno-is-resource-edition($uri as xs:string) {:):)
-(:(:    anno:is-resource-edition($uri):):)
-(:(:};:):)
-(:(::):)
-(:(::):)
-(:(:declare:):)
-(:(:    %test:args("3r131"):):)
-(:(:    %test:assertTrue:):)
-(:(:function tests:anno-is-resource-xml($uri as xs:string) {:):)
-(:(:    anno:is-resource-xml($uri):):)
-(:(:};:):)
+(:declare:)
+(:    %test:args("3r131"):)
+(:    %test:assertEquals("114r", "114v"):)
+(:function tests:anno-get-pages-in-TEI($uri as xs:string) {:)
+(:    anno:get-pages-in-TEI($uri):)
+(:};:)
 (::)
 (::)
 (:declare:)
-(:    %test:assertEquals("A place's name."):)
-(:function tests:anno-get-bodyValue() {:)
-(:    let $annotation := doc("/db/test-records/sample-tei.xml")//tei:placeName:)
-(:    return:)
-(:        anno:get-bodyValue($annotation):)
+(:    %test:args("3r679"):)
+(:    %test:assertTrue:)
+(:function tests:anno-is-resource-edition($uri as xs:string) {:)
+(:    anno:is-resource-edition($uri):)
 (:};:)
 (::)
 (::)
 (:declare:)
-(:    %test:args("asdf"):)
-(:    %test:assertFalse:)
-(:(:    %test:args("3r131"):):)
-(:(:    %test:assertTrue:):)
-(:function tests:anno-are-resources-available($resources as xs:string+) {:)
-(:    anno:are-resources-available($resources):)
+(:    %test:args("3r131"):)
+(:    %test:assertTrue:)
+(:function tests:anno-is-resource-xml($uri as xs:string) {:)
+(:    anno:is-resource-xml($uri):)
 (:};:)
 
 
+declare
+    %test:assertEquals("A place's name.")
+function tests:anno-get-bodyValue() {
+    let $annotation := doc("/db/test-records/sample-tei.xml")//tei:placeName
+    return
+        anno:get-bodyValue($annotation)
+};
+
+
+declare
+    %test:args("asdf")
+    %test:assertFalse
+(:    %test:args("3r131"):)
+(:    %test:assertTrue:)
+function tests:anno-are-resources-available($resources as xs:string+) {
+    anno:are-resources-available($resources)
+};
+
+
 (:declare:)
 (:    %test:args("3r131"):)
 (:    %test:assertEquals("Simon Birol, Aly Elrefaei"):)
@@ -546,4 +524,22 @@ function tests:exclude-aggregated-manifests() {
 (:    %test:assertEquals("3r177", "3r178", "3r7vw", "3r7p1", "3r7p9", "3r7sk", "3r7tp", "3r7vd", "3r179", "3r7n0", "3r9vn", "3r9wf", "3rb3z", "3rbm9", "3rbmc", "3rx14", "3vp38"):)
 (:function tests:anno-get-uris($documentURI) {:)
 (:    anno:get-uris($documentURI):)
-(:};:)
\ No newline at end of file
+(:};:)
+
+declare function local:is-endpoint-http200($url as xs:string) as xs:boolean {
+    let $http-status := local:get-http-status($url)
+    return
+        $http-status = "200"
+};
+
+declare function local:get-http-status($url as xs:string) as xs:string {
+    let $req := local:make-request($url)
+    return
+        http:send-request($req)[1]/@status
+};
+
+declare function local:make-request($url as xs:string) {
+    <http:request href="{$url}" method="get">
+        <http:header name="Connection" value="close"/>
+   </http:request>
+};
\ No newline at end of file
diff --git a/exist-app/tests/collate-tests.xqm b/exist-app/tests/collate-tests.xqm
new file mode 100644
index 00000000..77597760
--- /dev/null
+++ b/exist-app/tests/collate-tests.xqm
@@ -0,0 +1,424 @@
+xquery version "3.1";
+
+module namespace coll-tests="http://ahikar.sub.uni-goettingen.de/ns/coll-tests";
+
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace coll="http://ahikar.sub.uni-goettingen.de/ns/collate" at "modules/collate.xqm";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+
+declare variable $coll-tests:sample-file := local:open-file("ahiqar_sample");
+declare variable $coll-tests:sample-transliteration := $coll-tests:sample-file//tei:text[@type = "transliteration"];
+declare variable $coll-tests:sample-transcription := $coll-tests:sample-file//tei:text[@type = "transcription"];
+
+declare
+    %test:args("ahiqar_sample") %test:assertExists
+    %test:args("1234") %test:assertError("org.exist.xquery.XPathException")
+function coll-tests:open-file($uri as xs:string) as document-node() {
+    local:open-file($uri)
+};
+
+declare
+    %test:assertXPath("count($result) = 2")
+function coll-tests:get-milestones-in-text()
+as element(tei:milestone)* {
+    coll:get-milestones-in-text($coll-tests:sample-transliteration)
+};
+
+declare
+    %test:assertExists
+function coll-tests:get-next-milestone-succecss()
+as element(tei:milestone)? {
+    let $milestone := $coll-tests:sample-transliteration//tei:milestone[1]
+    return
+        coll:get-next-milestone($milestone)
+};
+
+declare
+    %test:assertEmpty
+function coll-tests:get-next-milestone-fail()
+as element(tei:milestone)? {
+    let $milestone := $coll-tests:sample-transliteration//tei:milestone[2]
+    return
+        coll:get-next-milestone($milestone)
+};
+
+declare
+    %test:assertExists
+    %test:assertXPath("$result//*[local-name(.) = 'ab']")
+function coll-tests:get-chunk()
+as element(tei:TEI) {
+    let $milestone := $coll-tests:sample-transliteration//tei:milestone[1]
+    return
+        coll:get-chunk($milestone)
+};
+
+
+declare
+     %test:assertEquals("some text that should be used display some text without a space")
+function coll-tests:make-plain-text-from-chunk()
+as xs:string {
+    let $chunk := 
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <group>
+                    <text xml:lang="ara">
+                        <body xml:lang="ara">
+                            <ab>some <persName>text</persName>          that should 
+                            
+                            
+                            
+                            be used</ab>
+                            <ab>
+                                <choice>
+                                    <sic>no display</sic>
+                                    <corr>display</corr>
+                                </choice>
+                                <surplus>no display</surplus>
+                                <supplied>no display</supplied>
+                                <ab type="colophon">no display</ab>
+                                <g>[</g>
+                                <unclear>no display</unclear>
+                                <catchwords>no displayno display</catchwords>
+                                <note>no display</note>
+                            </ab>
+                            <ab>some text with</ab>
+                            <ab><lb break="no"/>out a space</ab>
+                        </body>
+                    </text>
+                </group>
+            </text>
+        </TEI>
+    return
+        coll:make-plain-text-from-chunk($chunk)
+};
+
+
+declare
+    %test:assertEquals("some text with")
+function coll-tests:prepare-plain-text-creation-no-lb()
+as xs:string {
+    let $chunk :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <group>
+                    <text xml:lang="ara">
+                        <body xml:lang="ara">
+                            <ab>some text with</ab>
+                        </body>
+                    </text>
+                </group>
+            </text>
+        </TEI>
+    let $text := $chunk//tei:ab/text()
+    return
+        coll:prepare-plain-text-creation($text)
+};
+
+declare
+    %test:assertEquals("@out a space.")
+function coll-tests:prepare-plain-text-creation-lb()
+as xs:string {
+    let $chunk :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <group>
+                    <text xml:lang="ara">
+                        <body xml:lang="ara">
+                            <ab><lb break="no"/>out a space.</ab>
+                        </body>
+                    </text>
+                </group>
+            </text>
+        </TEI>
+    let $text := $chunk//tei:ab/text()
+    return
+        coll:prepare-plain-text-creation($text)
+};
+
+declare
+    %test:assertXPath("count($result) = 6")
+function coll-tests:get-relevant-text-nodes()
+as text()+ {
+    let $chunk := 
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <group>
+                    <text xml:lang="ara">
+                        <body xml:lang="ara">
+                            <ab>some <persName>text</persName>          that should 
+                            
+                            
+                            
+                            be used</ab>
+                            <ab>
+                                <choice>
+                                    <sic>no display</sic>
+                                    <corr>display</corr>
+                                </choice>
+                                <surplus>no display</surplus>
+                                <supplied>no display</supplied>
+                                <ab type="colophon">no display</ab>
+                                <g>[</g>
+                                <unclear>no display</unclear>
+                                <catchwords>no displayno display</catchwords>
+                                <note>no display</note>
+                            </ab>
+                            <ab>some text with</ab>
+                            <ab><lb break="no"/>out a space</ab>
+                        </body>
+                    </text>
+                </group>
+            </text>
+        </TEI>
+    return
+        coll:get-relevant-text-nodes($chunk)
+};
+
+declare
+    %test:args(" @test") %test:assertEquals("test")
+    %test:args("interpunction.") %test:assertEquals("interpunction")
+    %test:args("white               spaces") %test:assertEquals("white spaces")
+    %test:args("some
+    
+    new lines") %test:assertEquals("some new lines")
+function coll-tests:format-and-normalize-string($string as xs:string)
+as xs:string {
+    coll:format-and-normalize-string($string)
+};
+
+declare
+    %test:assertTrue
+function coll-tests:create-txt-collection-if-not-available() {
+    let $create-collection := coll:create-txt-collection-if-not-available()
+    return
+        if (xmldb:collection-available("/db/apps/sade/textgrid/txt/")) then
+            true()
+        else
+            false()
+};
+
+declare
+    %test:assertXPath("count($result) gt 0")
+function coll-tests:get-transcriptions-and-transliterations()
+as element(tei:text)+ {
+    coll:get-transcriptions-and-transliterations()
+};
+
+declare
+    %test:args("ara") %test:assertEquals("arabic")
+    %test:args("karshuni") %test:assertEquals("karshuni")
+    %test:args("syc") %test:assertEquals("syriac")
+function coll-tests:get-language-prefix-transcriptions($lang as xs:string)
+as xs:string {
+    let $text := 
+        <text xmlns="http://www.tei-c.org/ns/1.0" type="transcription"
+        xml:lang="{$lang}" />
+    return
+        coll:get-language-prefix($text)
+};
+
+declare
+    %test:args("ara", "ara") %test:assertEquals("arabic")
+    %test:args("ara", "karshuni") %test:assertEquals("karshuni")
+    %test:args("ara", "syc") %test:assertEquals("syriac")
+    %test:args("karshuni", "ara") %test:assertEquals("arabic")
+    %test:args("karshuni", "karshuni") %test:assertEquals("karshuni")
+    %test:args("karshuni", "syc") %test:assertEquals("syriac")
+    %test:args("syriac", "ara") %test:assertEquals("arabic")
+    %test:args("syriac", "karshuni") %test:assertEquals("karshuni")
+    %test:args("syriac", "syc") %test:assertEquals("syriac")
+function coll-tests:get-language-prefix-transliteration($lang-transliteration as xs:string,
+$lang-transcription as xs:string)
+as xs:string {
+    let $TEI :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text type="transliteration" xml:lang="{$lang-transliteration}" />
+            <text type="transcription" xml:lang="{$lang-transcription}" />
+        </TEI>
+    let $text := $TEI/tei:text[1]
+    return
+        coll:get-language-prefix($text)
+};
+
+declare
+    %test:assertEquals("/db/apps/sade/textgrid/data/ahiqar_sample.xml")
+function coll-tests:get-base-uri()
+as xs:string {
+    coll:get-base-uri($coll-tests:sample-transcription)
+};
+
+declare
+    %test:assertEquals("Beispieldatei_zum_Testen")
+function coll-tests:create-metadata-title-for-file-name()
+as xs:string {
+    coll:create-metadata-title-for-file-name($coll-tests:sample-transcription)
+};
+
+declare
+    %test:assertEquals("karshuni-Beispieldatei_zum_Testen-ahiqar_sample-transcription.txt")
+function coll-tests:make-file-name()
+as xs:string {
+    coll:make-file-name($coll-tests:sample-transcription)
+};
+
+declare
+    %test:assertEquals("ahiqar_sample-transcription.txt")
+function coll-tests:make-file-name-suffix()
+as xs:string {
+    coll:make-file-name-suffix($coll-tests:sample-transcription)
+};
+
+declare
+    %test:args("/db/apps/sade/textgrid/data/ahiqar_sample.xml") %test:assertEquals("ahiqar_sample")
+function coll-tests:get-file-name($base-uri as xs:string)
+as xs:string {
+    coll:get-file-name($base-uri)
+};
+
+declare
+    %test:assertEquals("text of the first narrative section some sayings")
+function coll-tests:get-relevant-text() {
+    let $TEI :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <body>
+                    <ab>some ignored text</ab>
+                    <milestone unit="first_narrative_section"/>
+                    <ab>text of the first narrative section</ab>
+                    <milestone unit="saying"/>
+                    <ab>some sayings</ab>
+                </body>
+            </text>
+        </TEI>
+    return
+        coll:get-relevant-text($TEI/tei:text)
+};
+
+declare
+    %test:assertXPath("count($result) = 2")
+function coll-tests:get-chunks() {
+    let $milestones := coll:get-milestones-in-text($coll-tests:sample-transliteration)
+    return
+        coll:get-chunks($milestones)
+};
+
+declare
+    %test:assertXPath("$result[self::*[local-name(.) = 'milestone']]")
+function coll-tests:get-end-of-chunk-milestone() {
+    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[1]
+    return
+        coll:get-end-of-chunk($milestone)
+};
+
+declare
+    %test:assertXPath("$result[self::*[local-name(.) = 'ab']]")
+function coll-tests:get-end-of-chunk-end-of-text() {
+    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[2]
+    return
+        coll:get-end-of-chunk($milestone)
+};
+
+
+declare
+    %test:assertXPath("$result[self::*/string() = 'the end text']")
+function coll-tests:get-end-of-chunk-end-of-text-2() {
+    let $TEI :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <group>
+                    <text type="transliteration">
+                        <body>
+                            <ab>some text<tei:milestone unit="sayings"/> more text</ab>
+                            <ab>the end text</ab>
+                        </body>
+                    </text>
+                    <text type="transcription">
+                        <body>
+                            <ab>some text2<tei:milestone unit="sayings"/> more text</ab>
+                            <ab>another end text</ab>
+                        </body>
+                    </text>
+                </group>
+            </text>
+        </TEI>
+    let $milestone := $TEI//tei:text[@type = "transliteration"]//tei:milestone
+    
+    return
+        coll:get-end-of-chunk($milestone)
+};
+
+declare
+    %test:assertTrue
+function coll-tests:has-following-milestone-true()
+as xs:boolean {
+    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[1]
+    return
+        coll:has-following-milestone($milestone)
+};
+
+declare
+    %test:assertFalse
+function coll-tests:has-following-milestone-false()
+as xs:boolean {
+    let $milestone := coll:get-milestones-in-text($coll-tests:sample-transliteration)[2]
+    return
+        coll:has-following-milestone($milestone)
+};
+
+declare
+    %test:assertEquals("chunk1 relevant text more relevant text chunk2 relevant text more relevant text")
+function coll-tests:get-relevant-text-from-chunks()
+as xs:string {
+    let $chunk1 :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <body>
+                    <ab>chunk1: relevant text</ab>
+                    <ab>more relevant text <unclear>irrelevant</unclear>.</ab>
+                </body>
+            </text>
+        </TEI>
+    let $chunk2 :=
+        <TEI xmlns="http://www.tei-c.org/ns/1.0">
+            <text>
+                <body>
+                    <ab>chunk2: relevant text</ab>
+                    <ab>more relevant text <surplus>irrelevant</surplus></ab>
+                </body>
+            </text>
+        </TEI>
+    return
+        coll:get-relevant-text-from-chunks(($chunk1, $chunk2))
+};
+
+declare
+    %test:assertTrue
+function coll-tests:has-text-milestone-succcess() {
+    let $text:= 
+        <text xmlns="http://www.tei-c.org/ns/1.0">
+            <body>
+                <ab>some text <milestone unit="parables"/></ab>
+            </body>
+        </text>
+    return
+        coll:has-text-milestone($text)
+};
+
+declare
+    %test:assertFalse
+function coll-tests:has-text-milestones-fail() {
+    let $text:= 
+        <text xmlns="http://www.tei-c.org/ns/1.0">
+            <body>
+                <ab>some text</ab>
+            </body>
+        </text>
+    return
+        coll:has-text-milestone($text)  
+};
+
+declare function local:open-file($uri as xs:string)
+as document-node() {
+    doc($coll:data || "/" || $uri || ".xml")
+};
diff --git a/exist-app/tests/commons-tests.xqm b/exist-app/tests/commons-tests.xqm
new file mode 100644
index 00000000..78dc0a5c
--- /dev/null
+++ b/exist-app/tests/commons-tests.xqm
@@ -0,0 +1,17 @@
+xquery version "3.1";
+
+module namespace ct="http://ahikar.sub.uni-goettingen.de/ns/commons-tests";
+
+declare namespace http = "http://expath.org/ns/http-client";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+
+declare variable $ct:restxq := "http://0.0.0.0:8080/exist/restxq/";
+
+declare
+    %test:assertTrue
+function ct:succes() {
+    true()
+};
\ No newline at end of file
diff --git a/exist-app/tests/tapi-collection-tests.xqm b/exist-app/tests/tapi-collection-tests.xqm
new file mode 100644
index 00000000..00f35f48
--- /dev/null
+++ b/exist-app/tests/tapi-collection-tests.xqm
@@ -0,0 +1,260 @@
+xquery version "3.1";
+
+module namespace tct="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection/tests";
+
+declare namespace http = "http://expath.org/ns/http-client";
+declare namespace tei="http://www.tei-c.org/ns/1.0";
+
+import module namespace commons="http://ahikar.sub.uni-goettingen.de/ns/commons" at "../modules/commons.xqm";
+import module namespace map="http://www.w3.org/2005/xpath-functions/map";
+import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
+import module namespace t-coll="http://ahikar.sub.uni-goettingen.de/ns/tapi/collection" at "../modules/tapi-collection.xqm";
+
+declare variable $tct:restxq := "http://0.0.0.0:8080/exist/restxq";
+declare variable $tct:collection-uri := "test-collection.xml";
+declare variable $tct:agg1-uri := "test-aggregation-1.xml";
+declare variable $tct:agg2-uri := "test-aggregation-2.xml";
+
+declare
+    %test:setUp
+function tct:_test-setup(){
+    let $collection :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/" 
+            xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="textgrid:test-collection">
+                <ore:aggregates rdf:resource="textgrid:test-aggregation-1"/>
+                <ore:aggregates rdf:resource="textgrid:test-aggregation-2"/>
+            </rdf:Description>
+        </rdf:RDF>
+    let $collection-meta :=
+        <MetadataContainerType xmlns="http://textgrid.info/namespaces/metadata/core/2010"
+            xmlns:ns2="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <object>
+                <generic>
+                    <provided>
+                        <title>Beispieldatei zum Testen</title>
+                        <format>text/tg.edition+tg.aggregation+xml</format>
+                    </provided>
+                </generic>
+            </object>
+        </MetadataContainerType>
+        
+    let $agg1 :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="test-aggregation-1">
+                <ore:aggregates rdf:resource="textgrid:ahiqar_sample"/>
+            </rdf:Description>
+        </rdf:RDF>
+    let $agg1-meta :=
+        <MetadataContainerType xmlns="http://textgrid.info/namespaces/metadata/core/2010"
+            xmlns:ns2="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <object>
+                <generic>
+                    <provided>
+                        <title>Beispielagg1 zum Testen</title>
+                        <format>text/tg.edition+tg.aggregation+xml</format>
+                    </provided>
+                </generic>
+            </object>
+        </MetadataContainerType>
+        
+    let $agg2 := 
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description rdf:about="test-aggregation-2">
+                <ore:aggregates rdf:resource="textgrid:ahiqar_sample"/>
+            </rdf:Description>
+        </rdf:RDF>
+    let $agg2-meta :=
+        <MetadataContainerType xmlns="http://textgrid.info/namespaces/metadata/core/2010"
+            xmlns:ns2="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <object>
+                <generic>
+                    <provided>
+                        <title>Beispielagg2 zum Testen</title>
+                        <format>text/tg.edition+tg.aggregation+xml</format>
+                    </provided>
+                </generic>
+            </object>
+        </MetadataContainerType>
+        
+        
+    return
+        (
+            xmldb:store($commons:agg, $tct:collection-uri, $collection),
+            xmldb:store($commons:agg, $tct:agg1-uri, $agg1),
+            xmldb:store($commons:agg, $tct:agg2-uri, $agg2),
+            xmldb:store($commons:meta, $tct:collection-uri, $collection-meta),
+            xmldb:store($commons:meta, $tct:agg1-uri, $agg1-meta),
+            xmldb:store($commons:meta, $tct:agg2-uri, $agg2-meta)
+        )
+};
+
+declare
+    %test:tearDown
+function tct:_test-teardown() {
+    xmldb:remove($commons:agg, $tct:collection-uri),
+    xmldb:remove($commons:agg, $tct:agg1-uri),
+    xmldb:remove($commons:agg, $tct:agg2-uri),
+    xmldb:remove($commons:meta, $tct:collection-uri),
+    xmldb:remove($commons:meta, $tct:agg1-uri),
+    xmldb:remove($commons:meta, $tct:agg2-uri)
+};
+
+declare
+    %test:assertTrue
+function tct:is-endpoint-available() {
+    let $url := $tct:restxq || "/textapi/ahikar/ahiqar_collection/collection.json"
+    return
+        local:is-endpoint-http200($url)
+};
+
+declare
+    %test:args("ahiqar_collection") %test:assertXPath("$result//*[local-name(.) = 'aggregates']")
+function tct:get-aggregation($uri as xs:string) {
+    t-coll:get-aggregation($uri)
+};
+
+declare
+    %test:assertTrue
+function tct:get-allowed-manifest-uris-mock-up-input-included() {
+    let $collection-metadata :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description xmlns:tei="http://www.tei-c.org/ns/1.0" rdf:about="textgrid:3r9ps.0">
+                <ore:aggregates rdf:resource="textgrid:3rbm9"/>
+                <ore:aggregates rdf:resource="textgrid:3rbmc"/>
+                <ore:aggregates rdf:resource="textgrid:3rx14"/>
+                <ore:aggregates rdf:resource="textgrid:3vp38"/>
+            </rdf:Description>
+        </rdf:RDF>
+    return
+        t-coll:get-allowed-manifest-uris($collection-metadata) = "3rx14"
+};
+
+declare
+    %test:assertFalse
+function tct:get-allowed-manifest-uris-mock-up-input-excluded() {
+    let $collection-metadata :=
+        <rdf:RDF xmlns:ore="http://www.openarchives.org/ore/terms/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+            <rdf:Description xmlns:tei="http://www.tei-c.org/ns/1.0" rdf:about="textgrid:3r9ps.0">
+                <ore:aggregates rdf:resource="textgrid:3rbm9"/>
+                <ore:aggregates rdf:resource="textgrid:3rbmc"/>
+                <ore:aggregates rdf:resource="textgrid:3rx14"/>
+                <ore:aggregates rdf:resource="textgrid:3vp38"/>
+            </rdf:Description>
+        </rdf:RDF>
+    return
+        t-coll:get-allowed-manifest-uris($collection-metadata) = "3vp38"
+};
+
+
+declare
+    %test:args("ahiqar_collection", "ahiqar_agg") %test:assertEquals("http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json")
+function tct:make-id($colletion-uri as xs:string, $manifest-uri as xs:string)
+as xs:string {
+    t-coll:make-id($tct:restxq, $colletion-uri, $manifest-uri)
+};
+
+
+declare
+    %test:assertEquals("ahiqar_agg")
+function tct:get-allowed-manifest-uris-sample-input() {
+    let $collection-metadata := t-coll:get-aggregation("ahiqar_collection")
+    return
+        t-coll:get-allowed-manifest-uris($collection-metadata)
+};
+
+declare
+    %test:args("ahiqar_collection") %test:assertXPath("$result[self::document-node()]")
+function tct:get-metadata-file($uri as xs:string) {
+    t-coll:get-metadata-file($uri)
+};
+
+
+declare
+    %test:args("textgrid:1234") %test:assertEquals("1234")
+    %test:args("1234") %test:assertEquals("1234")
+function tct:remove-textgrid-prefix($uri as xs:string) {
+    t-coll:remove-textgrid-prefix($uri)
+};
+
+declare
+    %test:args("text/tg.aggregation+xml") %test:assertEquals("collection")
+    %test:args("text/tg.edition+tg.aggregation+xml") %test:assertEquals("manifest")
+    %test:args("test") %test:assertEquals("manifest")
+function tct:make-format-type($tgmd-format as xs:string) {
+    t-coll:make-format-type($tgmd-format)
+};
+
+declare
+    %test:assertEquals("manifest")
+function tct:get-format-type() {
+    let $metadata := t-coll:get-metadata-file("ahiqar_agg")
+    return
+        t-coll:get-format-type($metadata)
+};
+
+
+declare
+    %test:args("ahiqar_collection") %test:assertXPath("$result//type[. = 'manifest']")
+    %test:args("ahiqar_collection") %test:assertXPath("$result//id[matches(., 'ahiqar_agg/manifest.json')]")
+    %test:args("test-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-1/manifest.json')]")
+    %test:args("test-collection") %test:assertXPath("$result//id[matches(., 'test-aggregation-2/manifest.json')]")
+function tct:make-sequence($collection-uri as xs:string) {
+    t-coll:make-sequence($collection-uri, $tct:restxq)
+};
+
+declare
+    %test:args("ahiqar_collection") %test:assertEquals("http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/annotationCollection.json")
+function tct:make-annotationCollection-uri($collection-uri as xs:string)
+as xs:string {
+    t-coll:make-annotationCollection-uri($tct:restxq, $collection-uri)
+};
+
+
+declare
+    %test:args("ahiqar_collection") %test:assertXPath("$result//title = 'The Story and Proverbs of Ahikar the Wise'")
+    %test:args("ahiqar_collection") %test:assertXPath("$result//*/string() = 'http://0.0.0.0:8080/exist/restxq/api/textapi/ahikar/ahiqar_collection/ahiqar_agg/manifest.json' ")
+function tct:get-json($collection-uri as xs:string) {
+    t-coll:get-json($collection-uri, $tct:restxq)
+};
+
+
+declare
+    (: check if all parts are present.
+     : no further tests are needed since the content has been tested while testing
+     : the underlying function. :)
+    %test:assertXPath("map:contains($result, 'title')")
+    %test:assertXPath("map:contains($result, 'collector')")
+    %test:assertXPath("map:contains($result, 'description')")
+    %test:assertXPath("map:contains($result, 'sequence')")
+function tct:endpoint()
+as item() {
+    let $url := $tct:restxq || "/textapi/ahikar/ahiqar_collection/collection.json"
+    let $req := <http:request href="{$url}" method="get">
+                        <http:header name="Connection" value="close"/>
+                   </http:request>
+    return http:send-request($req)[2] => util:base64-decode() => parse-json()
+};
+
+
+
+
+declare function local:is-endpoint-http200($url as xs:string) as xs:boolean {
+    let $http-status := local:get-http-status($url)
+    return
+        $http-status = "200"
+};
+
+declare function local:get-http-status($url as xs:string) as xs:string {
+    let $req := local:make-request($url)
+    return
+        http:send-request($req)[1]/@status
+};
+
+declare function local:make-request($url as xs:string) {
+    <http:request href="{$url}" method="get">
+        <http:header name="Connection" value="close"/>
+   </http:request>
+};
\ No newline at end of file
-- 
GitLab