From d8997f3be99fcedd44811558e284ea4e70eeb729 Mon Sep 17 00:00:00 2001
From: "Stefan E. Funk" <funk@sub.uni-goettingen.de>
Date: Tue, 13 Sep 2022 18:48:58 +0200
Subject: [PATCH] fix: refactor tests a lot :-)

---
 .../test/online/OAIPMHUtilitiesOnline.java    | 541 ++++++-------
 .../test/online/dh/TestDHOAIPMHOnline.java    | 714 ++++++++++++++++++
 .../test/online/tg/OAIPMHUtilitiesOnline.java | 703 +++++++++++++++++
 .../tg/TGOAIPMHResumptionTokenThread.java     |  60 ++
 .../online/{ => tg}/TestDHOAIPMHOnline.java   |   0
 .../TestTGBasicsOnline.java}                  |   0
 .../TestTGGetRecordOnline.java}               |   0
 .../tg/TestTGListIdentifiersOnline.java       | 450 +++++++++++
 .../TestTGListRecordsOnline.java}             |   0
 9 files changed, 2144 insertions(+), 324 deletions(-)
 create mode 100644 oaipmh-core/src/test/java/info/textgrid/middleware/test/online/dh/TestDHOAIPMHOnline.java
 create mode 100644 oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/OAIPMHUtilitiesOnline.java
 create mode 100644 oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TGOAIPMHResumptionTokenThread.java
 rename oaipmh-core/src/test/java/info/textgrid/middleware/test/online/{ => tg}/TestDHOAIPMHOnline.java (100%)
 rename oaipmh-core/src/test/java/info/textgrid/middleware/test/online/{TestTGOAIPMHOnline.java => tg/TestTGBasicsOnline.java} (100%)
 rename oaipmh-core/src/test/java/info/textgrid/middleware/test/online/{TestGetRecordOnline.java => tg/TestTGGetRecordOnline.java} (100%)
 create mode 100644 oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListIdentifiersOnline.java
 rename oaipmh-core/src/test/java/info/textgrid/middleware/test/online/{TestListRecordsOnline.java => tg/TestTGListRecordsOnline.java} (100%)

diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/OAIPMHUtilitiesOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/OAIPMHUtilitiesOnline.java
index bf1aaa4e..8fe72c04 100644
--- a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/OAIPMHUtilitiesOnline.java
+++ b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/OAIPMHUtilitiesOnline.java
@@ -9,6 +9,7 @@ import java.io.InputStream;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
 import javax.ws.rs.core.Response;
@@ -34,59 +35,89 @@ public class OAIPMHUtilitiesOnline {
   // FINALS
   // **
 
-  protected static final String OAI_DC_PREFIX = "oai_dc";
-  protected static final String OAI_DATACITE_PREFIX = "oai_datacite";
-  protected static final String OAI_IDIOMMETS_PREFIX = "oai_idiom_mets";
-
-  protected static final String EXPECTED_OAIDC_FORMAT_CONTENT = "<oai_dc:dc>";
-  protected static final String EXPECTED_IDIOMMETS_FORMAT_CONTENT = "<mets ";
-  protected static final String EXPECTED_DATACITE_FORMAT_CONTENT = "<datacite:resource>";
-
-  protected static final String NO_SET = null;
-  protected static final String ERROR = " >>> ERROR";
-  protected static final String OK = " >>> OKIDOKI";
-  protected static final String TESTING = "\n >>> TESTING ";
-  protected static final String NO_THREAD_NAME = "";
-  protected static final String NOT_TESTED = " >>> NOT_TESTED";
-  protected static final String NO_TOKEN = "-1";
-
-  protected static final String VERB_LIST_IDENTIFIERS = "ListIdentifiers";
-  protected static final String VERB_LIST_RECORDS = "ListRecords";
-  protected static final String VERB_GET_RECORD = "GetRecord";
-  protected static final String VERB_IDENTIFY = "Identify";
-  protected static final String VERB_LIST_METADATA_FORMATS = "ListMetadataFormats";
-  protected static final String VERB_LIST_SETS = "ListSets";
-  protected static final boolean NO_METADATA_FORMAT_WITH_RESTOK = false;
-  protected static final boolean METADATA_FORMAT_WITH_RESTOK = true;
-  protected static final String NO_FROM = null;
-  protected static final String NO_UNTIL = null;
+  public static final String OAI_DC_PREFIX = "oai_dc";
+  public static final String OAI_DATACITE_PREFIX = "oai_datacite";
+  public static final String OAI_IDIOMMETS_PREFIX = "oai_idiom_mets";
+
+  public static final String VERB_LIST_IDENTIFIERS = "ListIdentifiers";
+  public static final String VERB_LIST_RECORDS = "ListRecords";
+  public static final String VERB_GET_RECORD = "GetRecord";
+  public static final String VERB_IDENTIFY = "Identify";
+  public static final String VERB_LIST_METADATA_FORMATS = "ListMetadataFormats";
+  public static final String VERB_LIST_SETS = "ListSets";
+
+  public static final String HEADER_ID_START_TAG = "<identifier>";
+  public static final String HEADER_ID_END_TAG = "</identifier>";
+
+  public static final String EXPECTED_OAIDC_FORMAT_CONTENT = "<oai_dc:dc>";
+  public static final String EXPECTED_IDIOMMETS_FORMAT_CONTENT = "<mets ";
+  public static final String EXPECTED_DATACITE_FORMAT_CONTENT = "<datacite:resource>";
+
+  public static final String OK = ">>> OKIDOKI";
+  public static final String TESTING = "\n >>> TESTING ";
+  public static final String NOT_TESTED = " >>> NOT_TESTED";
+  public static final String NO_TOKEN = "-1";
+
+  public static final String NO_SET = null;
+  public static final String NO_THREAD_NAME = "";
+  public static final boolean NO_METADATA_FORMAT_WITH_RESTOK = false;
+  public static final boolean METADATA_FORMAT_WITH_RESTOK = true;
+  public static final String NO_FROM = null;
+  public static final String NO_UNTIL = null;
+  public static final String NO_QUERY = "";
+
   // Time to wait between the single queries using resumption tokens in milliseconds.
-  protected static final Long TIME = 275l;
+  public static final Long TIME = 275l;
 
-  protected static final String HDL_PREFIX = "hdl:";
-  protected static final String STAR_PREFIX = "***";
+  public static final String HDL_PREFIX = "hdl:";
+  public static final String STAR_PREFIX = "***";
+  public static final String OAI_PATH = "oai";
+  public static final String VERSION_PATH = "oai/version";
 
-  protected static final long OAIPMH_CLIENT_TIMEOUT = 120000;
+  public static final long OAIPMH_CLIENT_TIMEOUT = 120000;
 
   /**
    * @param theClient
-   * @param thePath
+   * @param theQuery
    * @return
    * @throws IOException
    */
-  protected static Response getHttpResponse(Client theClient, String thePath) throws IOException {
-    return getHttpResponse(theClient, thePath, "");
+  public static Response getOAIHttpResponse(Client theClient, String theQuery)
+      throws IOException {
+    return getHttpResponse(theClient, NO_THREAD_NAME, OAI_PATH, theQuery);
   }
 
   /**
    * @param theClient
-   * @param thePath
    * @param theThreadName
+   * @param theQuery
    * @return
    * @throws IOException
    */
-  protected static Response getHttpResponse(Client theClient, String thePath, String theThreadName)
-      throws IOException {
+  public static Response getOAIHttpResponse(Client theClient, String theThreadName,
+      String theQuery) throws IOException {
+    return getHttpResponse(theClient, theThreadName, OAI_PATH, theQuery);
+  }
+
+  /**
+   * @param theClient
+   * @return
+   * @throws IOException
+   */
+  public static Response getVersionHttpResponse(Client theClient) throws IOException {
+    return getHttpResponse(theClient, NO_THREAD_NAME, VERSION_PATH, NO_QUERY);
+  }
+
+  /**
+   * @param theClient
+   * @param theThreadName
+   * @param thePath
+   * @param theQuery
+   * @return
+   * @throws IOException
+   */
+  protected static Response getHttpResponse(Client theClient, String theThreadName, String thePath,
+      String theQuery) throws IOException {
 
     Response result;
 
@@ -96,7 +127,7 @@ public class OAIPMHUtilitiesOnline {
       // Nothing interrupts here!
     }
 
-    WebClient w = WebClient.fromClient(theClient).replaceQuery(thePath);
+    WebClient w = WebClient.fromClient(theClient).path(thePath).replaceQuery(theQuery);
 
     System.out.println("\t" + theThreadName + "HTTP GET " + w.getCurrentURI());
 
@@ -106,9 +137,8 @@ public class OAIPMHUtilitiesOnline {
     int status = result.getStatus();
 
     if (status != HttpStatus.SC_OK) {
-      System.err.println("\t" + theThreadName + "status: " + status);
-      System.err.println(ERROR);
-      assertTrue(false);
+      String message = theThreadName + "status: " + status;
+      assertTrue(message, false);
     } else {
       System.out.println("\t" + theThreadName + "status: " + status);
     }
@@ -140,7 +170,7 @@ public class OAIPMHUtilitiesOnline {
    * @return {@link File} The resource.
    * @throws IOException
    */
-  protected static File getResource(String resPart) throws IOException {
+  public static File getResource(String resPart) throws IOException {
 
     File res;
 
@@ -166,7 +196,7 @@ public class OAIPMHUtilitiesOnline {
    * @param theProperty
    * @return
    */
-  protected static List<String> getListFromProperties(String theProperty) {
+  public static List<String> getListFromProperties(String theProperty) {
 
     List<String> result = new ArrayList<String>();
 
@@ -183,7 +213,7 @@ public class OAIPMHUtilitiesOnline {
    * @throws IOException
    * @throws FileNotFoundException
    */
-  protected static Properties getPropertiesFromFile(String theConfigFile)
+  public static Properties getPropertiesFromFile(String theConfigFile)
       throws FileNotFoundException, IOException {
 
     Properties result = null;
@@ -202,7 +232,7 @@ public class OAIPMHUtilitiesOnline {
    * @param theEndpoint
    * @return
    */
-  protected static Client getOAIPMHWEebClient(String theEndpoint) {
+  public static Client getOAIPMHWEebClient(String theEndpoint) {
 
     Client result = null;
 
@@ -223,12 +253,10 @@ public class OAIPMHUtilitiesOnline {
   }
 
   // **
-  // PRIVATE METHODS
+  // TG used methods
   // **
 
   /**
-   * TODO Generalise in OaiPmhTestUtilities!
-   * 
    * @param theClient
    * @param theVerb
    * @param theSet
@@ -241,7 +269,7 @@ public class OAIPMHUtilitiesOnline {
    * @param resumptionTokenANDMetadataPrefix
    * @throws IOException
    */
-  protected static void testList(Client theClient, final String theVerb, final String theSet,
+  public static void examineTGList(Client theClient, final String theVerb, final String theSet,
       final String theMetadataPrefix, final int maxNumberOfPagesToTest,
       final int recordsExpectedPerRequest, final String theThreadName, final String from,
       final String until, final boolean resumptionTokenANDMetadataPrefix) throws IOException {
@@ -251,113 +279,115 @@ public class OAIPMHUtilitiesOnline {
     long startTime = System.currentTimeMillis();
 
     String testOccurance = "header";
-    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+    if (theVerb.equals(VERB_LIST_RECORDS)) {
       testOccurance = "record";
     }
 
-    String path = "verb=" + theVerb + "&metadataPrefix=" + theMetadataPrefix;
+    String query = "verb=" + theVerb + "&metadataPrefix=" + theMetadataPrefix;
 
     if (theSet != null) {
-      path += "&set=" + theSet;
+      query += "&set=" + theSet;
     }
 
     if (from != null && until != null) {
-      path += "&from=" + from + "&until=" + until;
+      query += "&from=" + from + "&until=" + until;
     }
 
-    Response httpResponse = OAIPMHUtilitiesOnline.getHttpResponse(theClient, path, threadName);
+    Response httpResponse = getOAIHttpResponse(theClient, threadName, query);
     int status = httpResponse.getStatus();
 
     int loopCount = 1;
     long timeRunning = System.currentTimeMillis() - startTime;
-    System.out.println("\t" + threadName + "time: "
-        + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning) + " (loop " + loopCount
-        + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
+    System.out.println("\t" + threadName + "time: " + getDurationInSecs(timeRunning) + " (loop "
+        + loopCount + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
 
     String responseString = IOUtils.readStringFromStream((InputStream) httpResponse.getEntity());
 
     // Test resumption token tags.
-    String restok =
-        examineResumptionTokenTag(responseString, testOccurance, OAIPMHUtilitiesOnline.NO_TOKEN,
-            recordsExpectedPerRequest, loopCount, threadName, theMetadataPrefix);
+    String restok = examineTGResumptionTokenTag(responseString, testOccurance, NO_TOKEN,
+        recordsExpectedPerRequest, loopCount, threadName);
+
+    // Test if the header identifiers are sound.
+    if (theVerb.equals(VERB_LIST_RECORDS)) {
+      examineTGHeader(responseString, recordsExpectedPerRequest);
+    }
 
     // Test general metadata content (if verb is listRecords!), must go conform with the metadata
     // prefix setting.
-    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
-      examineContent(responseString, theMetadataPrefix);
+    if (theVerb.equals(VERB_LIST_RECORDS)) {
+      examineTGContent(responseString, theMetadataPrefix);
     }
 
-    while (status == HttpStatus.SC_OK && !restok.equals(OAIPMHUtilitiesOnline.NO_TOKEN)) {
+    while (status == HttpStatus.SC_OK && !restok.equals(NO_TOKEN)) {
       loopCount += 1;
       synchronized (threadName) {
         if (maxNumberOfPagesToTest > 0 && loopCount > maxNumberOfPagesToTest) {
-          System.out
-              .println("\t" + threadName + "TESTING ONLY " + maxNumberOfPagesToTest + " pages!");
+          System.out.println(
+              "\t" + threadName + ">>> TESTING ONLY " + maxNumberOfPagesToTest + " pages!");
           break;
         }
-        path = "verb=" + theVerb + "&resumptionToken=" + restok;
+        query = "verb=" + theVerb + "&resumptionToken=" + restok;
         if (resumptionTokenANDMetadataPrefix) {
-          path += "&metadataPrefix=" + theMetadataPrefix;
+          query += "&metadataPrefix=" + theMetadataPrefix;
         }
-        httpResponse = OAIPMHUtilitiesOnline.getHttpResponse(theClient, path, threadName);
+        httpResponse = getOAIHttpResponse(theClient, threadName, query);
         timeRunning = System.currentTimeMillis() - startTime;
-        System.out.println("\t" + threadName + "time: "
-            + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning) + " (loop " + loopCount
-            + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
+        System.out.println("\t" + threadName + "time: " + getDurationInSecs(timeRunning) + " (loop "
+            + loopCount + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
 
         // Test resumption token tags.
         responseString = IOUtils.readStringFromStream((InputStream) httpResponse.getEntity());
-        restok = examineResumptionTokenTag(responseString, testOccurance, restok,
-            recordsExpectedPerRequest, loopCount, threadName, theMetadataPrefix);
+        restok = examineTGResumptionTokenTag(responseString, testOccurance, restok,
+            recordsExpectedPerRequest, loopCount, threadName);
+
+        // Test if the header identifiers are sound.
+        if (theVerb.equals(VERB_LIST_RECORDS)) {
+          examineTGHeader(responseString, recordsExpectedPerRequest);
+        }
 
         // Test general metadata content (if verb is listRecords!), must go conform with the
-        // metadata // prefix setting.
-        if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
-          examineContent(responseString, theMetadataPrefix);
+        // metadata prefix setting.
+        if (theVerb.equals(VERB_LIST_RECORDS)) {
+          examineTGContent(responseString, theMetadataPrefix);
         }
       }
     }
 
     // Only check for max loops, if we do have less loops and no resumption token, it will be fine,
     // too!
-    if (!restok.equals(OAIPMHUtilitiesOnline.NO_TOKEN) && loopCount < maxNumberOfPagesToTest) {
-      System.out.println(threadName + OAIPMHUtilitiesOnline.ERROR + ": Must have done "
-          + maxNumberOfPagesToTest + " loops, but did only " + loopCount);
-      assertTrue(false);
+    if (!restok.equals(NO_TOKEN) && loopCount < maxNumberOfPagesToTest) {
+      String message = threadName + ": Must have done " + maxNumberOfPagesToTest
+          + " loops, but did only " + loopCount;
+      assertTrue(message, false);
     }
 
     // Check for single loop, we need MORE!
     if (loopCount == 1) {
-      System.out
-          .println(threadName + OAIPMHUtilitiesOnline.ERROR + ": Must have more than one loop!");
-      assertTrue(false);
+      String message = threadName + ": Must have more than one loop!";
+      assertTrue(message, false);
     }
 
-    System.out.println("\t" + threadName + OAIPMHUtilitiesOnline.OK);
+    System.out.println("\t" + threadName + OK);
   }
 
   /**
-   * @param theResponseString
+   * @param theResponse
    * @param recordOrHeader
    * @param oldtok
    * @param recordsExpectedPerRequest
    * @param loopCount
    * @param theThreadName
-   * @param theMetadataPrefix
    * @return Resumption token string, if existing, -1 if either no <resumptionToken> tag is
    *         existing, tag is existing and has no token value.
    * @throws IOException
    */
-  protected static String examineResumptionTokenTag(final String theResponseString,
+  private static String examineTGResumptionTokenTag(final String theResponseString,
       final String recordOrHeader, final String oldtok, final int recordsExpectedPerRequest,
-      final int loopCount, final String theThreadName, final String theMetadataPrefix)
-      throws IOException {
+      final int loopCount, final String theThreadName) throws IOException {
 
     // Test for OAIPMH errors.
     if (theResponseString.contains("<error code=\"badArgument\">")) {
-      String message =
-          theThreadName + OAIPMHUtilitiesOnline.ERROR + " IN OAIPMH RESPONSE: "
-              + theResponseString;
+      String message = theThreadName + ": ERROR IN OAIPMH RESPONSE: " + theResponseString;
       System.out.println(message);
       throw new IOException(message);
     }
@@ -380,7 +410,7 @@ public class OAIPMHUtilitiesOnline {
     if (tokStart == -1 && tokEnd == -1) {
       System.out.println("\t" + theThreadName + "token: no token");
 
-      return OAIPMHUtilitiesOnline.NO_TOKEN;
+      return NO_TOKEN;
     }
 
     String restokTmp = theResponseString.substring(tokStart, tokEnd);
@@ -405,24 +435,21 @@ public class OAIPMHUtilitiesOnline {
 
     // If token is provided, test cursor and element count!
     if (!restok.isEmpty()) {
-      synchronized (TestGetRecordOnline.class) {
+      synchronized (OAIPMHUtilitiesOnline.class) {
         // Check <record> or <header> count, must be recordsExpectedPerRequest!
         if (recordCount != recordsExpectedPerRequest) {
-          String message =
-              OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader + " count mismatch, must be "
-                  + recordsExpectedPerRequest + " if token is provided, but is " + recordCount;
+          String message = recordOrHeader + " count mismatch, must be "
+              + recordsExpectedPerRequest + " if token is provided, but is " + recordCount;
           assertTrue(message, false);
         }
         if (size <= recordsExpectedPerRequest) {
-          String message =
-              OAIPMHUtilitiesOnline.ERROR + ": completeListSize count mismatch, must be > "
-                  + recordsExpectedPerRequest + " if token is provided, but is " + size;
+          String message = "completeListSize count mismatch, must be > "
+              + recordsExpectedPerRequest + " if token is provided, but is " + size;
           assertTrue(message, false);
         }
         if (cursor != recordsExpectedPerRequest * loopCount) {
-          String message = OAIPMHUtilitiesOnline.ERROR + ": cursor must be "
-              + (loopCount * recordsExpectedPerRequest) + " in loop " + loopCount + ", but is "
-              + cursor;
+          String message = "cursor must be " + (loopCount * recordsExpectedPerRequest)
+              + " in loop " + loopCount + ", but is " + cursor;
           assertTrue(message, false);
         }
       }
@@ -432,272 +459,138 @@ public class OAIPMHUtilitiesOnline {
     else {
       // Check <record> count, must be completeListSize % recordsExpectedPerRequest.
       if (recordCount != size % recordsExpectedPerRequest) {
-        System.err.println(theThreadName + OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader
-            + " count mismatch, should be " + size % recordsExpectedPerRequest + ", but is "
-            + recordCount);
-        assertTrue(false);
+        String message = theThreadName + ": " + recordOrHeader + " count mismatch, should be "
+            + size % recordsExpectedPerRequest + ", but is " + recordCount;
+        assertTrue(message, false);
       }
 
       // No resumption token available in response.
-      return OAIPMHUtilitiesOnline.NO_TOKEN;
+      return NO_TOKEN;
     }
 
     return restok;
   }
 
-  /**
-   * @param theThreadName
-   * @return
-   */
-  protected static String tn(String theThreadName) {
-
-    String result = theThreadName;
-
-    if (theThreadName != null && !theThreadName.equals("") && !theThreadName.endsWith(" ")) {
-      result = "[" + theThreadName + "] ";
-    }
-
-    return result;
-  }
-
   /**
    * @param theResponseString
    * @param theMetadataFormat
    */
-  protected static void examineContent(String theResponseString, String theMetadataFormat) {
+  private static void examineTGContent(String theResponseString, String theMetadataFormat) {
 
     // Check for correct metadata content according to metadata prefix.
-    if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_DC_PREFIX)
-        && !theResponseString
-            .contains("metadataPrefix=\"" + OAIPMHUtilitiesOnline.OAI_DC_PREFIX + "\"")
-        && !theResponseString.contains(OAIPMHUtilitiesOnline.EXPECTED_OAIDC_FORMAT_CONTENT)) {
-      System.out
-          .println(OAIPMHUtilitiesOnline.OAI_DC_PREFIX + " needs to deliver content with schema: "
-              + OAIPMHUtilitiesOnline.EXPECTED_OAIDC_FORMAT_CONTENT + "!");
-
-      System.out.println("ERROR:\n" + theResponseString);
-
-      assertTrue(false);
-    } else if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX)
-        && !theResponseString.contains(
-            "metadataPrefix=\"" + OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT
-                + "\"")
-        && !theResponseString
-            .contains(OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT)) {
-      System.out.println(
-          OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX + " needs to deliver content with schema: "
-              + OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT + "!");
+    if (theMetadataFormat.equals(OAI_DC_PREFIX)
+        && !theResponseString.contains("metadataPrefix=\"" + OAI_DC_PREFIX + "\"")
+        && !theResponseString.contains(EXPECTED_OAIDC_FORMAT_CONTENT)) {
 
-      System.out.println("ERROR:\n" + theResponseString);
+      String message = OAI_DC_PREFIX + " needs to deliver content with schema: "
+          + EXPECTED_OAIDC_FORMAT_CONTENT + "!";
+      assertTrue(message, false);
 
-      assertTrue(false);
-    } else if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX)
+    } else if (theMetadataFormat.equals(OAI_IDIOMMETS_PREFIX)
         && !theResponseString
-            .contains("metadataPrefix=\"" + OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX + "\"")
-        && !theResponseString.contains(OAIPMHUtilitiesOnline.EXPECTED_DATACITE_FORMAT_CONTENT)) {
-      System.out.println(
-          OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX + " needs to deliver content with schema: "
-              + OAIPMHUtilitiesOnline.EXPECTED_DATACITE_FORMAT_CONTENT + "!");
+            .contains("metadataPrefix=\"" + EXPECTED_IDIOMMETS_FORMAT_CONTENT + "\"")
+        && !theResponseString.contains(EXPECTED_IDIOMMETS_FORMAT_CONTENT)) {
+
+      String message = OAI_IDIOMMETS_PREFIX + " needs to deliver content with schema: "
+          + EXPECTED_IDIOMMETS_FORMAT_CONTENT + "!";
+      assertTrue(message, false);
+
+    } else if (theMetadataFormat.equals(OAI_DATACITE_PREFIX)
+        && !theResponseString.contains("metadataPrefix=\"" + OAI_DATACITE_PREFIX + "\"")
+        && !theResponseString.contains(EXPECTED_DATACITE_FORMAT_CONTENT)) {
 
-      System.out.println("ERROR:\n" + theResponseString);
+      String message = OAI_DATACITE_PREFIX + " needs to deliver content with schema: "
+          + EXPECTED_DATACITE_FORMAT_CONTENT + "!";
+      assertTrue(message, false);
 
-      assertTrue(false);
     } else {
-      System.out.println(theResponseString);
+      // DEBUG ONLY!
+      // System.out.println(theResponseString);
+      // DEBUG ONLY!
     }
   }
 
   /**
+   * <p>
+   * Check for correct metadata content according to metadata prefix --> We do need to check if we
+   * have different identifiers in the response.
+   * </p>
+   * 
    * @param theResponseString
-   * @param recordOrHeader
-   * @param oldtok
-   * @param theMetadataPrefix
-   * @return Resumption token string, if existing, -1 if either no <resumptionToken> tag is
-   *         existing, tag is existing and has no token value.
-   * @throws IOException
+   * @param recordsExpectedPerRequest
    */
-  protected static String examineResumptionTokenTag(String theResponseString, String recordOrHeader,
-      String oldtok, String theMetadataPrefix) throws IOException {
-
-    // Default entries per page are 100 (IDIOM: 30).
-    // momentarily.
-    int expectedCount = 100;
-    if (theMetadataPrefix.equals(OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX)) {
-      expectedCount = 30;
+  private static void examineTGHeader(String theResponseString, int recordsExpectedPerRequest) {
+    boolean IDSUnique = examineTGHeaderIDs(theResponseString);
+    if (IDSUnique) {
+      String message = "Duplicate IDs in the response!";
+      assertTrue(message, false);
     }
-
-    // Test for OAIPMH errors.
-    if (theResponseString.contains("<error code=\"badArgument\">")) {
-      System.err.println(OAIPMHUtilitiesOnline.ERROR + " IN OAIPMH RESPONSE: " + theResponseString);
-      assertTrue(false);
-    }
-
-    // Count response objects at first.
-    int recordCount = 0;
-    int i = theResponseString.indexOf("<" + recordOrHeader + ">", 0);
-    while (i != -1) {
-      recordCount++;
-      i++;
-      i = theResponseString.indexOf("<" + recordOrHeader + ">", i);
-    }
-
-    System.out.println("\t" + recordOrHeader + "s: " + recordCount);
-
-    // Check if token tag is existing.
-    int tokStart = theResponseString.indexOf("<resumptionToken");
-    int tokEnd = theResponseString.indexOf("</resumptionToken");
-
-    if (tokStart == -1 && tokEnd == -1) {
-      System.out.println("\ttoken: no token");
-
-      return "-1";
-    }
-
-    String restokTmp = theResponseString.substring(tokStart, tokEnd);
-    // Get token tag.
-    String toktag = restokTmp.substring(0, restokTmp.indexOf(">") + 1)
-        .trim();
-    System.out.println("\ttokentag: " + toktag);
-
-    // Get token.
-    String restok = restokTmp.substring(restokTmp.indexOf(">") + 1).trim();
-    System.out.println("\ttoken: " + restok);
-
-    // Check if old and new token are equal or not.
-    boolean tokchanged = !oldtok.equals(restok);
-    System.out.println("\ttokchngd: " + tokchanged);
-
-    // Get completeListSize and cursor.
-    String sizeStr = toktag
-        .substring(toktag.indexOf("completeListSize=\"") + 18);
-    int size = Integer
-        .parseInt(sizeStr.substring(0, sizeStr.indexOf("\"")));
-    String cursorStr = toktag.substring(toktag.indexOf("cursor=\"") + 8);
-    int cursor = Integer
-        .parseInt(cursorStr.substring(0, cursorStr.indexOf("\"")));
-    System.out.println("\tsize: " + size + " / " + cursor);
-
-    // If token is provided, and we have less than 100 (IDIOM: 30) elements: mekkern!
-    if (!restok.isEmpty()) {
-      synchronized (TestDHOAIPMHOnline.class) {
-        // Check <record> or <header> count, must be 100 (IDIOM: 30)!
-        if (recordCount != expectedCount) {
-          System.err.println(
-              OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader + " count mismatch, must be "
-                  + expectedCount + " if token is provided, but is " + recordCount);
-        }
-        if (size <= expectedCount) {
-          System.err
-              .println(OAIPMHUtilitiesOnline.ERROR + ": completeListSize count mismatch, must be > "
-                  + expectedCount + " if token is provided, but is " + size);
-        }
-        if (recordCount != expectedCount || size <= expectedCount) {
-          assertTrue(false);
-        }
-      }
-    }
-
-    // If no token is provided, stop querying.
-    else {
-      // Check <record> count, must be completeListSize % 100 (IDIOM: 30).
-      if (recordCount != size % expectedCount) {
-        System.err.println(OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader
-            + " count mismatch, should be " + size % expectedCount + ", but is " + recordCount);
-        assertTrue(false);
-      }
-
-      // No resumption token available in response.
-      return "-1";
-    }
-
-    return restok;
   }
 
   /**
-   * @param theWebClient
-   * @param theVerb
-   * @param theMetadataPrefix
-   * @param theSet
+   * <p>
+   * Creates a hash set containing all the ID values from a certain tag. Used to count elements such
+   * as <identifier> in OAI-PMH header list responses. We want to know here, if we have ID
+   * duplicates in one request.
+   * </p>
+   * 
+   * @param theResponseString
    * @return
-   * @throws IOException
-   */
-  protected static int testList(Client theWebClient, String theVerb, String theMetadataPrefix,
-      String theSet) throws IOException {
-    return testList(theWebClient, theVerb, theMetadataPrefix, theSet, OAIPMHUtilitiesOnline.NO_FROM,
-        OAIPMHUtilitiesOnline.NO_UNTIL);
-  }
-
-  /**
-   * @param theWebClient
-   * @param theVerb
-   * @param theMetadataPrefix
-   * @param theSet
-   * @param from
-   * @param until
-   * @return How many pages were delivered
-   * @throws IOException
    */
-  protected static int testList(Client theWebClient, String theVerb, String theMetadataPrefix,
-      String theSet, String from, String until) throws IOException {
-
-    int result;
-
-    long startTime = System.currentTimeMillis();
-
-    String testOccurance = "header";
-    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
-      testOccurance = "record";
-    }
-
-    String path = "verb=" + theVerb + "&metadataPrefix=" + theMetadataPrefix;
-
-    if (theSet != null && !theSet.isEmpty()) {
-      path += "&set=" + theSet;
+  private static boolean examineTGHeaderIDs(String theResponseString) {
+
+    boolean result = false;
+    HashSet<String> idHash = new HashSet<String>();
+
+    int startID = 0;
+    int endID = 0;
+    String id = "";
+    int count = 0;
+    // Look for all the header ID tags.
+    while (startID != -1) {
+      count++;
+      startID = theResponseString.indexOf(HEADER_ID_START_TAG, endID);
+      endID = theResponseString.indexOf(HEADER_ID_END_TAG, startID);
+      id = theResponseString.substring(startID + HEADER_ID_START_TAG.length(), endID);
+      idHash.add(id);
     }
+    // Remove "-1" ID again (and the respective counter).
+    idHash.remove(id);
+    count--;
 
-    if (from != null && until != null) {
-      path += "&from=" + from + "&until=" + until;
+    // Check for ID count: Hash size (unique IDs!) must be equal to the count of ID headers!
+    if (count != idHash.size()) {
+      String message =
+          "We must have " + count + " unique IDs in the headers, but we got only " + idHash.size();
+      assertTrue(message, false);
     }
 
-    Response response = OAIPMHUtilitiesOnline.getHttpResponse(theWebClient, path);
-    String responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
-    int status = response.getStatus();
+    return result;
+  }
 
-    long timeRunning = System.currentTimeMillis() - startTime;
-    System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
-
-    String restok = examineResumptionTokenTag(responseString, testOccurance, "", theMetadataPrefix);
-    examineIdentifiers(responseString);
-
-    result = 1;
-    while (status == HttpStatus.SC_OK && !restok.equals("-1")) {
-      path = "verb=" + theVerb + "&resumptionToken=" + restok;
-      response = OAIPMHUtilitiesOnline.getHttpResponse(theWebClient, path);
-      responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
-      timeRunning = System.currentTimeMillis() - startTime;
-      System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
-      restok = examineResumptionTokenTag(responseString, testOccurance, restok, theMetadataPrefix);
-      examineIdentifiers(responseString);
-      result++;
-    }
+  // **
+  // DH used methods
+  // **
 
-    System.out.println("\tpage amount: " + result);
-    System.out.println(OAIPMHUtilitiesOnline.OK);
+  // FIXME
 
-    return result;
-  }
+  // **
+  // Commonly used (TG & DH) methods
+  // **
 
   /**
-   * @param theResponseString
+   * @param theThreadName
    * @return
-   * @throws IOException
    */
-  protected static void examineIdentifiers(String theResponseString) throws IOException {
-    if (theResponseString.contains("<identifier>hdl:21</identifier>")) {
-      System.err.println(OAIPMHUtilitiesOnline.ERROR
-          + " IN OAIPMH RESPONSE: identifier tag is corrupt!" + theResponseString);
-      assertTrue(false);
+  private static String tn(String theThreadName) {
+
+    String result = theThreadName;
+
+    if (theThreadName != null && !theThreadName.equals("") && !theThreadName.endsWith(" ")) {
+      result = "[" + theThreadName + "] ";
     }
+
+    return result;
   }
 
 }
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/dh/TestDHOAIPMHOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/dh/TestDHOAIPMHOnline.java
new file mode 100644
index 00000000..96708e13
--- /dev/null
+++ b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/dh/TestDHOAIPMHOnline.java
@@ -0,0 +1,714 @@
+package info.textgrid.middleware.test.online.dh;
+
+import static org.junit.Assert.assertTrue;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.http.HttpStatus;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import info.textgrid.middleware.test.online.OAIPMHUtilitiesOnline;
+
+/**
+ * <p>
+ * Some online tests for the DARIAH-DE Repository OAIMPH service.
+ * </p>
+ * 
+ * @author Stefan E. Funk, SUB Göttingen
+ */
+@Ignore
+public class TestDHOAIPMHOnline {
+
+  // **
+  // FINALS
+  // **
+
+  private static final String PROPERTIES_FILE = "oaipmh.test.repository-de-dariah-eu.properties";
+  // private static final String PROPERTIES_FILE = "oaipmh.test.trep-de-dariah-eu.properties";
+
+  // **
+  // STATICS
+  // **
+
+  // Some JAXRS things.
+  private static String oaipmhEndpoint;
+  private static Client oaipmhWebClient;
+
+  // Properties
+  private static String expectedListSets;
+  private static String checkGetRecordDC;
+  private static String expectedGetRecordDC;
+  private static String checkGetRecordDATACITE;
+  private static String expectedGetRecordDATACITE;
+  private static String checkListRecordsDC;
+  private static String checkListRecordsDCFrom;
+  private static String checkListRecordsDCUntil;
+  private static String checkListRecordsDATACITEFrom;
+  private static String checkListRecordsDATACITEUntil;
+  private static String checkListRecordsSet;
+  private static Integer checkListRecordsSetExpectedPages;
+  private static String checkListIdentifiersSet;
+  private static Integer checkListIdentifiersSetExpectedPages;
+  private static List<String> checkGetRecordIDList = new ArrayList<String>();
+
+  // **
+  // PREPARATIONS
+  // **
+
+  /**
+   * @throws Exception
+   */
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+
+    // Load properties file.
+    Properties p = new Properties();
+    p.load(new FileInputStream(OAIPMHUtilitiesOnline.getResource(PROPERTIES_FILE)));
+
+    System.out.println("Properties file: " + PROPERTIES_FILE);
+    p.list(System.out);
+
+    // Get other needed properties.
+    oaipmhEndpoint = p.getProperty("oaipmhEndpoint");
+    expectedListSets = p.getProperty("expectedListSets");
+    checkGetRecordDC = p.getProperty("checkGetRecordDC");
+    expectedGetRecordDC = p.getProperty("expectedGetRecordDC");
+    checkGetRecordDATACITE = p.getProperty("checkGetRecordDATACITE");
+    expectedGetRecordDATACITE = p.getProperty("expectedGetRecordDATACITE");
+    checkListRecordsDC = p.getProperty("checkListRecordsDC");
+    checkListRecordsDCFrom = p.getProperty("checkListRecordsDCFrom");
+    checkListRecordsDCUntil = p.getProperty("checkListRecordsDCUntil");
+    checkListRecordsDATACITEFrom = p
+        .getProperty("checkListRecordsDATACITEFrom");
+    checkListRecordsDATACITEUntil = p
+        .getProperty("checkListRecordsDATACITEUntil");
+    checkListRecordsSet = p.getProperty("checkListRecordsSet");
+    checkListRecordsSetExpectedPages =
+        Integer.parseInt(p.getProperty("checkListRecordsSetExpectedPages").trim());
+    checkListIdentifiersSet = p.getProperty("checkListIdentifiersSet");
+    checkListIdentifiersSetExpectedPages =
+        Integer.parseInt(p.getProperty("checkListIdentifiersSetExpectedPages").trim());
+    checkGetRecordIDList =
+        OAIPMHUtilitiesOnline.getListFromProperties((String) p.get("checkGetRecordIDList"));
+
+    // Get web client from endpoint.
+    oaipmhWebClient = OAIPMHUtilitiesOnline.getOAIPMHWEebClient(oaipmhEndpoint);
+  }
+
+  /**
+   * @throws Exception
+   */
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    //
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+    //
+  }
+
+  /**
+   * @throws Exception
+   */
+  @After
+  public void tearDown() throws Exception {
+    //
+  }
+
+  // **
+  // TESTS
+  // **
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testGetVersion() throws IOException {
+
+    String shouldStartWith = "oaipmh-core";
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#GETVERSION for '"
+        + shouldStartWith + "'");
+
+    Response response = OAIPMHUtilitiesOnline.getVersionHttpResponse(oaipmhWebClient);
+    int status = response.getStatus();
+    String versionString = IOUtils.readStringFromStream((InputStream) response.getEntity());
+
+    if (status != HttpStatus.SC_OK && !versionString.startsWith(shouldStartWith)) {
+      String message = "[" + status + "]: response should start with '" + shouldStartWith + "'";
+      assertTrue(message, false);
+    }
+
+    System.out.println("\tresponse: " + versionString);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testRootWithSlash() throws IOException {
+
+    String url = "oai/";
+    String shouldContain = "DARIAH-DE Repository";
+
+    testRootURL(url, shouldContain);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testRootWithoutSlash() throws IOException {
+
+    String url = "oai";
+    String shouldContain = "DARIAH-DE Repository";
+
+    testRootURL(url, shouldContain);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testBlubbVerb() throws IOException {
+
+    String url = "verb=BLUBB";
+    String shouldContain = "Illegal OAI verb";
+
+    testRootURL(url, shouldContain);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testIdentify() throws IOException {
+
+    String verb = "Identify";
+    String shouldContain = "DARIAH-DE Repository";
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#IDENTIFY");
+
+    Response response = OAIPMHUtilitiesOnline.getOAIHttpResponse(oaipmhWebClient, "verb=" + verb);
+    int status = response.getStatus();
+
+    String responseString = IOUtils
+        .readStringFromStream((InputStream) response.getEntity());
+
+    if (status != HttpStatus.SC_OK || !responseString.contains(shouldContain)) {
+      String message = "[" + status + "]: response should contain '" + shouldContain + "'";
+      assertTrue(message, false);
+    }
+
+    System.out.println("\tresponse: " + responseString);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListSets() throws IOException {
+    testListSet(expectedListSets, expectedListSets);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsDC() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS");
+
+    testList("ListRecords", "oai_dc", OAIPMHUtilitiesOnline.NO_SET);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsSetDC() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS");
+
+    int pages = testList("ListRecords", "oai_dc", checkListRecordsSet);
+
+    if (pages != checkListRecordsSetExpectedPages) {
+      assertTrue(pages + " != " + checkListRecordsSetExpectedPages, false);
+    }
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsSetDCDifferentPrefixes() throws IOException {
+
+    String uri = checkListRecordsSet;
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS - HDL PREFIX");
+
+    if (!uri.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      assertTrue("missing '" + OAIPMHUtilitiesOnline.HDL_PREFIX + "' prefix!", false);
+    }
+    int pages1 = testList("ListRecords", "oai_dc", uri);
+    if (pages1 != checkListRecordsSetExpectedPages) {
+      System.out.println(pages1 + " != " + checkListRecordsSetExpectedPages);
+    }
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS - NO PREFIX");
+
+    if (checkListRecordsSet.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      uri = checkListRecordsSet.substring(4);
+    }
+    int pages2 = testList("ListRecords", "oai_dc", uri);
+    if (pages2 != checkListRecordsSetExpectedPages) {
+      System.out.println(pages2 + " != " + checkListRecordsSetExpectedPages);
+    }
+
+    // Check pages count.
+    if (pages1 != checkListRecordsSetExpectedPages || pages2 != checkListRecordsSetExpectedPages) {
+      assertTrue("page count does not match", false);
+    }
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  @Ignore
+  public void testListRecordsSetDCStarPrefix() throws IOException {
+
+    // TODO Un-ignore if identifier check is implemented!
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS - STAR PREFIX");
+
+    if (checkListRecordsSet.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      checkListRecordsSet = OAIPMHUtilitiesOnline.STAR_PREFIX + checkListRecordsSet.substring(4);
+    }
+
+    int pages3 = testList("ListRecords", "oai_dc", checkListRecordsSet);
+    if (pages3 != 1) {
+      System.out.println(pages3 + " != " + 1);
+    }
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsDCFromUntil() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS");
+
+    testList("ListRecords", "oai_dc", OAIPMHUtilitiesOnline.NO_SET,
+        checkListRecordsDCFrom, checkListRecordsDCUntil);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsDATASITE() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS");
+
+    testList("ListRecords", "oai_datacite", OAIPMHUtilitiesOnline.NO_SET);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListRecordsDATASITEFromUntil() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTRECORDS");
+
+    testList("ListRecords", "oai_datacite", OAIPMHUtilitiesOnline.NO_SET,
+        checkListRecordsDATACITEFrom, checkListRecordsDATACITEUntil);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  @Ignore
+  public void testGetRecordDC() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#GETRECORD");
+
+    String identifier = "";
+    String shouldContain = "";
+
+    String path = "verb=GetRecord&identifier=" + identifier
+        + "&metadataPrefix=oai_dc";
+
+    // FIXME Complete test!
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  @Ignore
+  public void testGetRecordDATASITE() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#GETRECORD");
+
+    String identifier = "";
+    String shouldContain = "";
+
+    String path = "verb=GetRecord&identifier=" + identifier
+        + "&metadataPrefix=oai_datacite";
+
+    // FIXME Complete test!
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersDC() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS");
+
+    testList("ListIdentifiers", "oai_dc", OAIPMHUtilitiesOnline.NO_SET);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersSetDC() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS");
+
+    int pages = testList("ListIdentifiers", "oai_dc", checkListIdentifiersSet);
+
+    if (pages != checkListIdentifiersSetExpectedPages) {
+      assertTrue(pages + " != " + checkListIdentifiersSetExpectedPages, false);
+    }
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersSetDCDifferentPrefixes() throws IOException {
+
+    String uri = checkListIdentifiersSet;
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS - HDL PREFIX");
+
+    if (!uri.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      assertTrue("missing '" + OAIPMHUtilitiesOnline.HDL_PREFIX + "' prefix!", false);
+    }
+    System.out.println("uri: " + uri);
+    int pages1 = testList("ListIdentifiers", "oai_dc", uri);
+    if (pages1 != checkListIdentifiersSetExpectedPages) {
+      System.out.println(pages1 + " != " + checkListIdentifiersSetExpectedPages);
+    }
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS - NO PREFIX");
+
+    if (checkListIdentifiersSet.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      uri = checkListIdentifiersSet.substring(4);
+    }
+    System.out.println("uri: " + uri);
+    int pages2 = testList("ListIdentifiers", "oai_dc", uri);
+    if (pages2 != checkListIdentifiersSetExpectedPages) {
+      System.out.println(pages2 + " != " + checkListIdentifiersSetExpectedPages);
+    }
+
+    // Check pages count.
+    if (pages1 != checkListRecordsSetExpectedPages || pages2 != checkListRecordsSetExpectedPages) {
+      assertTrue("pages count does not match", false);
+    }
+  }
+
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  @Ignore
+  public void testListIdentifiersSetDCStarPrefixe() throws IOException {
+
+    // TODO Un-ignore if identifier check is implemented!
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS - STAR PREFIX");
+
+    if (checkListIdentifiersSet.startsWith(OAIPMHUtilitiesOnline.HDL_PREFIX)) {
+      checkListIdentifiersSet =
+          OAIPMHUtilitiesOnline.STAR_PREFIX + checkListIdentifiersSet.substring(4);
+    }
+    System.out.println("uri: " + checkListIdentifiersSet);
+    int pages3 = testList("ListIdentifiers", "oai_dc", checkListIdentifiersSet);
+    if (pages3 != checkListIdentifiersSetExpectedPages) {
+      System.out.println(pages3 + " != " + checkListIdentifiersSetExpectedPages);
+    }
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersDATASITE() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTIDENTIFIERS");
+
+    testList("ListIdentifiers", "oai_datacite", OAIPMHUtilitiesOnline.NO_SET);
+  }
+
+  // **
+  // PRIVATE METHODS
+  // **
+
+  /**
+   * TODO Generalise in OAIPMHUtilitiesOnline!
+   * 
+   * @param theResponse
+   * @return Resumption token string, if existing, -1 if either no <resumptionToken> tag is
+   *         existing, tag is existing and has no token value.
+   * @throws IOException
+   */
+  private static String examineResumptionTokenTag(String theResponseString,
+      String recordOrHeader, String oldtok) throws IOException {
+
+    // Test for OAIPMH errors.
+    if (theResponseString.contains("<error code=\"badArgument\">")) {
+      assertTrue("ERROR IN OAIPMH RESPONSE: " + theResponseString, false);
+    }
+
+    // Count response objects at first.
+    int recordCount = 0;
+    int i = theResponseString.indexOf("<" + recordOrHeader + ">", 0);
+    while (i != -1) {
+      recordCount++;
+      i++;
+      i = theResponseString.indexOf("<" + recordOrHeader + ">", i);
+    }
+
+    System.out.println("\t" + recordOrHeader + "s: " + recordCount);
+
+    // Check if token tag is existing.
+    int tokStart = theResponseString.indexOf("<resumptionToken");
+    int tokEnd = theResponseString.indexOf("</resumptionToken");
+
+    if (tokStart == -1 && tokEnd == -1) {
+      System.out.println("\ttoken: no token");
+
+      return "-1";
+    }
+
+    String restokTmp = theResponseString.substring(tokStart, tokEnd);
+    // Get token tag.
+    String toktag = restokTmp.substring(0, restokTmp.indexOf(">") + 1)
+        .trim();
+    System.out.println("\ttokentag: " + toktag);
+
+    // Get token.
+    String restok = restokTmp.substring(restokTmp.indexOf(">") + 1).trim();
+    System.out.println("\ttoken: " + restok);
+
+    // Check if old and new token are equal or not.
+    boolean tokchanged = !oldtok.equals(restok);
+    System.out.println("\ttokchngd: " + tokchanged);
+
+    // Get completeListSize and cursor.
+    String sizeStr = toktag
+        .substring(toktag.indexOf("completeListSize=\"") + 18);
+    int size = Integer
+        .parseInt(sizeStr.substring(0, sizeStr.indexOf("\"")));
+    String cursorStr = toktag.substring(toktag.indexOf("cursor=\"") + 8);
+    int cursor = Integer
+        .parseInt(cursorStr.substring(0, cursorStr.indexOf("\"")));
+    System.out.println("\tsize: " + size + " / " + cursor);
+
+    // If token is provided, and we have less than 100 elements: mekkern!
+    if (!restok.isEmpty()) {
+      synchronized (TestDHOAIPMHOnline.class) {
+        // Check <record> or <header> count, must be 100!
+        String message = "";
+        if (recordCount > 100) {
+          message = recordOrHeader + " count mismatch, must be 100 if token is provided, but is "
+              + recordCount;
+        } else {
+          message =
+              "completeListSize count mismatch, must be > 100 if token is provided, but is " + size;
+        }
+        if (recordCount != 100 || size <= 100) {
+          assertTrue(message, false);
+        }
+      }
+    }
+
+    // If no token is provided, stop querying.
+    else {
+      // Check <record> count, must be completeListSize % 100.
+      if (recordCount != size % 100) {
+        String message =
+            recordOrHeader + " count mismatch, should be " + size % 100 + ", but is " + recordCount;
+        assertTrue(message, false);
+      }
+
+      // No resumption token available in response.
+      return "-1";
+    }
+
+    return restok;
+  }
+
+  /**
+   * @param theVerb
+   * @param theMetadataPrefix
+   * @param theSet
+   * @return
+   * @throws IOException
+   */
+  private static int testList(String theVerb, String theMetadataPrefix,
+      String theSet) throws IOException {
+    return testList(theVerb, theMetadataPrefix, theSet,
+        OAIPMHUtilitiesOnline.NO_FROM, OAIPMHUtilitiesOnline.NO_UNTIL);
+  }
+
+  /**
+   * TODO Generalise in OaiPmhTestUtilities!
+   * 
+   * @param theVerb
+   * @param theMetadataPrefix
+   * @param theSet
+   * @param from
+   * @param until
+   * @return How many pages were delivered
+   * @throws IOException
+   */
+  private static int testList(String theVerb, String theMetadataPrefix,
+      String theSet, String from, String until) throws IOException {
+
+    int result;
+
+    long startTime = System.currentTimeMillis();
+
+    String testOccurance = "header";
+    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+      testOccurance = "record";
+    }
+
+    String query = "verb=" + theVerb + "&metadataPrefix="
+        + theMetadataPrefix;
+
+    if (theSet != null && !theSet.isEmpty()) {
+      query += "&set=" + theSet;
+    }
+
+    if (from != null && until != null) {
+      query += "&from=" + from + "&until=" + until;
+    }
+
+    Response response = OAIPMHUtilitiesOnline.getOAIHttpResponse(oaipmhWebClient, query);
+    String responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
+    int status = response.getStatus();
+
+    long timeRunning = System.currentTimeMillis() - startTime;
+    System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
+
+    String restok = examineResumptionTokenTag(responseString, testOccurance, "");
+    examineIdentifiers(responseString);
+
+    result = 1;
+    while (status == HttpStatus.SC_OK && !restok.equals("-1")) {
+      query = "verb=" + theVerb + "&resumptionToken=" + restok;
+      response = OAIPMHUtilitiesOnline.getOAIHttpResponse(oaipmhWebClient, query);
+      responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
+      timeRunning = System.currentTimeMillis() - startTime;
+      System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
+      restok = examineResumptionTokenTag(responseString, testOccurance, restok);
+      examineIdentifiers(responseString);
+      result++;
+    }
+
+    System.out.println("\tpage amount: " + result);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+
+    return result;
+  }
+
+  /**
+   * @param theSet
+   * @throws IOException
+   */
+  private static void testListSet(String theSet, String theExpectedResponse)
+      throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#LISTSETS");
+
+    Response response = OAIPMHUtilitiesOnline.getOAIHttpResponse(oaipmhWebClient,
+        "verb=" + OAIPMHUtilitiesOnline.VERB_LIST_SETS);
+    int status = response.getStatus();
+
+    String responseString = IOUtils
+        .readStringFromStream((InputStream) response.getEntity());
+
+    if (status != HttpStatus.SC_OK || !responseString.contains(theExpectedResponse)) {
+      String message = "[" + status + "] response should contain '" + theExpectedResponse + "'";
+      assertTrue(message, false);
+    }
+
+    System.out.println("\tresponse: " + responseString);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @param theURL
+   * @param shouldContain
+   * @throws IOException
+   */
+  private static void testRootURL(String theURL, String shouldContain)
+      throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "#ROOTURL");
+
+    Response response = OAIPMHUtilitiesOnline.getOAIHttpResponse(oaipmhWebClient, theURL);
+    int status = response.getStatus();
+
+    String responseString = IOUtils
+        .readStringFromStream((InputStream) response.getEntity());
+
+    if (status != HttpStatus.SC_OK || !responseString.contains(shouldContain)) {
+      String message = "[" + status + "] response should contain '" + shouldContain + "'";
+      assertTrue(message, false);
+    }
+
+    System.out.println("\tresponse: " + responseString);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @param theResponseString
+   * @return
+   * @throws IOException
+   */
+  private static void examineIdentifiers(String theResponseString) throws IOException {
+    if (theResponseString.contains("<identifier>hdl:21</identifier>")) {
+      String message = "ERROR IN OAIPMH RESPONSE: identifier tag is corrupt!" + theResponseString;
+      assertTrue(message, false);
+    }
+  }
+
+}
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/OAIPMHUtilitiesOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/OAIPMHUtilitiesOnline.java
new file mode 100644
index 00000000..bf1aaa4e
--- /dev/null
+++ b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/OAIPMHUtilitiesOnline.java
@@ -0,0 +1,703 @@
+package info.textgrid.middleware.test.online;
+
+import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.http.HttpStatus;
+import info.textgrid.middleware.OAIPMHProducer;
+
+/**
+ * <p>
+ * Some online tests for the TextGrid OAIMPH service.
+ * </p>
+ * 
+ * @author Stefan E. Funk, SUB Göttingen
+ */
+public class OAIPMHUtilitiesOnline {
+
+  // **
+  // FINALS
+  // **
+
+  protected static final String OAI_DC_PREFIX = "oai_dc";
+  protected static final String OAI_DATACITE_PREFIX = "oai_datacite";
+  protected static final String OAI_IDIOMMETS_PREFIX = "oai_idiom_mets";
+
+  protected static final String EXPECTED_OAIDC_FORMAT_CONTENT = "<oai_dc:dc>";
+  protected static final String EXPECTED_IDIOMMETS_FORMAT_CONTENT = "<mets ";
+  protected static final String EXPECTED_DATACITE_FORMAT_CONTENT = "<datacite:resource>";
+
+  protected static final String NO_SET = null;
+  protected static final String ERROR = " >>> ERROR";
+  protected static final String OK = " >>> OKIDOKI";
+  protected static final String TESTING = "\n >>> TESTING ";
+  protected static final String NO_THREAD_NAME = "";
+  protected static final String NOT_TESTED = " >>> NOT_TESTED";
+  protected static final String NO_TOKEN = "-1";
+
+  protected static final String VERB_LIST_IDENTIFIERS = "ListIdentifiers";
+  protected static final String VERB_LIST_RECORDS = "ListRecords";
+  protected static final String VERB_GET_RECORD = "GetRecord";
+  protected static final String VERB_IDENTIFY = "Identify";
+  protected static final String VERB_LIST_METADATA_FORMATS = "ListMetadataFormats";
+  protected static final String VERB_LIST_SETS = "ListSets";
+  protected static final boolean NO_METADATA_FORMAT_WITH_RESTOK = false;
+  protected static final boolean METADATA_FORMAT_WITH_RESTOK = true;
+  protected static final String NO_FROM = null;
+  protected static final String NO_UNTIL = null;
+  // Time to wait between the single queries using resumption tokens in milliseconds.
+  protected static final Long TIME = 275l;
+
+  protected static final String HDL_PREFIX = "hdl:";
+  protected static final String STAR_PREFIX = "***";
+
+  protected static final long OAIPMH_CLIENT_TIMEOUT = 120000;
+
+  /**
+   * @param theClient
+   * @param thePath
+   * @return
+   * @throws IOException
+   */
+  protected static Response getHttpResponse(Client theClient, String thePath) throws IOException {
+    return getHttpResponse(theClient, thePath, "");
+  }
+
+  /**
+   * @param theClient
+   * @param thePath
+   * @param theThreadName
+   * @return
+   * @throws IOException
+   */
+  protected static Response getHttpResponse(Client theClient, String thePath, String theThreadName)
+      throws IOException {
+
+    Response result;
+
+    try {
+      Thread.sleep(TIME);
+    } catch (InterruptedException e) {
+      // Nothing interrupts here!
+    }
+
+    WebClient w = WebClient.fromClient(theClient).replaceQuery(thePath);
+
+    System.out.println("\t" + theThreadName + "HTTP GET " + w.getCurrentURI());
+
+    result = w.get();
+
+    // Check HTTP status.
+    int status = result.getStatus();
+
+    if (status != HttpStatus.SC_OK) {
+      System.err.println("\t" + theThreadName + "status: " + status);
+      System.err.println(ERROR);
+      assertTrue(false);
+    } else {
+      System.out.println("\t" + theThreadName + "status: " + status);
+    }
+
+    return result;
+  }
+
+  /**
+   * @param theMillis
+   * @return
+   */
+  public static String getDurationInSecs(long theMillis) {
+
+    int SECS_IN_MILLIS = 1000;
+    int secs = SECS_IN_MILLIS;
+
+    return theMillis / secs + " second" + ((theMillis / secs) != 1 ? "s" : "");
+  }
+
+  /**
+   * <p>
+   * Loads a resource.
+   * </p>
+   * 
+   * TODO Put together with the method in TGCrudServiceUtilities! Maybe create a first build maven
+   * module for utility things.
+   * 
+   * @param {@link String} The resource to search for.
+   * @return {@link File} The resource.
+   * @throws IOException
+   */
+  protected static File getResource(String resPart) throws IOException {
+
+    File res;
+
+    // If we have an absolute resPart, just return the file.
+    if (resPart.startsWith(File.separator)) {
+      return new File(resPart);
+    }
+
+    URL url = ClassLoader.getSystemClassLoader().getResource(resPart);
+    if (url == null) {
+      throw new IOException("Resource '" + resPart + "' not found");
+    }
+    try {
+      res = new File(url.toURI());
+    } catch (URISyntaxException ue) {
+      res = new File(url.getPath());
+    }
+
+    return res;
+  }
+
+  /**
+   * @param theProperty
+   * @return
+   */
+  protected static List<String> getListFromProperties(String theProperty) {
+
+    List<String> result = new ArrayList<String>();
+
+    String parts[] = theProperty.split(",");
+    for (String part : parts) {
+      result.add(part.trim());
+    }
+
+    return result;
+  }
+
+  /**
+   * @return
+   * @throws IOException
+   * @throws FileNotFoundException
+   */
+  protected static Properties getPropertiesFromFile(String theConfigFile)
+      throws FileNotFoundException, IOException {
+
+    Properties result = null;
+
+    // Load properties file.
+    result = new Properties();
+    result.load(new FileInputStream(getResource(theConfigFile)));
+
+    System.out.println("Properties file: " + theConfigFile);
+    result.list(System.out);
+
+    return result;
+  }
+
+  /**
+   * @param theEndpoint
+   * @return
+   */
+  protected static Client getOAIPMHWEebClient(String theEndpoint) {
+
+    Client result = null;
+
+    // Get OAI-PMH REST endpoint and HTTP client.
+    System.out.println("Getting OAI-PMH HTTP client --> " + theEndpoint + " <--");
+
+    // Get proxy first, set policy.
+    OAIPMHProducer JAXRSClient = JAXRSClientFactory.create(theEndpoint, OAIPMHProducer.class);
+    HTTPConduit conduit = WebClient.getConfig(JAXRSClient).getHttpConduit();
+    HTTPClientPolicy policy = new HTTPClientPolicy();
+    policy.setReceiveTimeout(OAIPMHUtilitiesOnline.OAIPMH_CLIENT_TIMEOUT);
+    conduit.setClient(policy);
+
+    // Create Web Client from Web Proxy.
+    result = WebClient.client(JAXRSClient);
+
+    return result;
+  }
+
+  // **
+  // PRIVATE METHODS
+  // **
+
+  /**
+   * TODO Generalise in OaiPmhTestUtilities!
+   * 
+   * @param theClient
+   * @param theVerb
+   * @param theSet
+   * @param theMetadataPrefix
+   * @param maxNumberOfPagesToTest
+   * @param recordsExpectedPerRequest
+   * @param theThreadName
+   * @param from
+   * @param until
+   * @param resumptionTokenANDMetadataPrefix
+   * @throws IOException
+   */
+  protected static void testList(Client theClient, final String theVerb, final String theSet,
+      final String theMetadataPrefix, final int maxNumberOfPagesToTest,
+      final int recordsExpectedPerRequest, final String theThreadName, final String from,
+      final String until, final boolean resumptionTokenANDMetadataPrefix) throws IOException {
+
+    String threadName = tn(theThreadName);
+
+    long startTime = System.currentTimeMillis();
+
+    String testOccurance = "header";
+    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+      testOccurance = "record";
+    }
+
+    String path = "verb=" + theVerb + "&metadataPrefix=" + theMetadataPrefix;
+
+    if (theSet != null) {
+      path += "&set=" + theSet;
+    }
+
+    if (from != null && until != null) {
+      path += "&from=" + from + "&until=" + until;
+    }
+
+    Response httpResponse = OAIPMHUtilitiesOnline.getHttpResponse(theClient, path, threadName);
+    int status = httpResponse.getStatus();
+
+    int loopCount = 1;
+    long timeRunning = System.currentTimeMillis() - startTime;
+    System.out.println("\t" + threadName + "time: "
+        + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning) + " (loop " + loopCount
+        + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
+
+    String responseString = IOUtils.readStringFromStream((InputStream) httpResponse.getEntity());
+
+    // Test resumption token tags.
+    String restok =
+        examineResumptionTokenTag(responseString, testOccurance, OAIPMHUtilitiesOnline.NO_TOKEN,
+            recordsExpectedPerRequest, loopCount, threadName, theMetadataPrefix);
+
+    // Test general metadata content (if verb is listRecords!), must go conform with the metadata
+    // prefix setting.
+    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+      examineContent(responseString, theMetadataPrefix);
+    }
+
+    while (status == HttpStatus.SC_OK && !restok.equals(OAIPMHUtilitiesOnline.NO_TOKEN)) {
+      loopCount += 1;
+      synchronized (threadName) {
+        if (maxNumberOfPagesToTest > 0 && loopCount > maxNumberOfPagesToTest) {
+          System.out
+              .println("\t" + threadName + "TESTING ONLY " + maxNumberOfPagesToTest + " pages!");
+          break;
+        }
+        path = "verb=" + theVerb + "&resumptionToken=" + restok;
+        if (resumptionTokenANDMetadataPrefix) {
+          path += "&metadataPrefix=" + theMetadataPrefix;
+        }
+        httpResponse = OAIPMHUtilitiesOnline.getHttpResponse(theClient, path, threadName);
+        timeRunning = System.currentTimeMillis() - startTime;
+        System.out.println("\t" + threadName + "time: "
+            + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning) + " (loop " + loopCount
+            + (maxNumberOfPagesToTest != 0 ? "/" + maxNumberOfPagesToTest : "") + ")");
+
+        // Test resumption token tags.
+        responseString = IOUtils.readStringFromStream((InputStream) httpResponse.getEntity());
+        restok = examineResumptionTokenTag(responseString, testOccurance, restok,
+            recordsExpectedPerRequest, loopCount, threadName, theMetadataPrefix);
+
+        // Test general metadata content (if verb is listRecords!), must go conform with the
+        // metadata // prefix setting.
+        if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+          examineContent(responseString, theMetadataPrefix);
+        }
+      }
+    }
+
+    // Only check for max loops, if we do have less loops and no resumption token, it will be fine,
+    // too!
+    if (!restok.equals(OAIPMHUtilitiesOnline.NO_TOKEN) && loopCount < maxNumberOfPagesToTest) {
+      System.out.println(threadName + OAIPMHUtilitiesOnline.ERROR + ": Must have done "
+          + maxNumberOfPagesToTest + " loops, but did only " + loopCount);
+      assertTrue(false);
+    }
+
+    // Check for single loop, we need MORE!
+    if (loopCount == 1) {
+      System.out
+          .println(threadName + OAIPMHUtilitiesOnline.ERROR + ": Must have more than one loop!");
+      assertTrue(false);
+    }
+
+    System.out.println("\t" + threadName + OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @param theResponseString
+   * @param recordOrHeader
+   * @param oldtok
+   * @param recordsExpectedPerRequest
+   * @param loopCount
+   * @param theThreadName
+   * @param theMetadataPrefix
+   * @return Resumption token string, if existing, -1 if either no <resumptionToken> tag is
+   *         existing, tag is existing and has no token value.
+   * @throws IOException
+   */
+  protected static String examineResumptionTokenTag(final String theResponseString,
+      final String recordOrHeader, final String oldtok, final int recordsExpectedPerRequest,
+      final int loopCount, final String theThreadName, final String theMetadataPrefix)
+      throws IOException {
+
+    // Test for OAIPMH errors.
+    if (theResponseString.contains("<error code=\"badArgument\">")) {
+      String message =
+          theThreadName + OAIPMHUtilitiesOnline.ERROR + " IN OAIPMH RESPONSE: "
+              + theResponseString;
+      System.out.println(message);
+      throw new IOException(message);
+    }
+
+    // Count response objects at first.
+    int recordCount = 0;
+    int i = theResponseString.indexOf("<" + recordOrHeader + ">", 0);
+    while (i != -1) {
+      recordCount++;
+      i++;
+      i = theResponseString.indexOf("<" + recordOrHeader + ">", i);
+    }
+
+    System.out.println("\t" + theThreadName + recordOrHeader + "s: " + recordCount);
+
+    // Check if token tag is existing.
+    int tokStart = theResponseString.indexOf("<resumptionToken");
+    int tokEnd = theResponseString.indexOf("</resumptionToken");
+
+    if (tokStart == -1 && tokEnd == -1) {
+      System.out.println("\t" + theThreadName + "token: no token");
+
+      return OAIPMHUtilitiesOnline.NO_TOKEN;
+    }
+
+    String restokTmp = theResponseString.substring(tokStart, tokEnd);
+    // Get token tag.
+    String toktag = restokTmp.substring(0, restokTmp.indexOf(">") + 1).trim();
+    System.out.println("\t" + theThreadName + "tokentag: " + toktag);
+
+    // Get token.
+    String restok = restokTmp.substring(restokTmp.indexOf(">") + 1).trim();
+    System.out.println("\t" + theThreadName + "token: " + restok);
+
+    // Check if old and new token are equal or not.
+    boolean tokchanged = !oldtok.equals(restok);
+    System.out.println("\t" + theThreadName + "tokchngd: " + tokchanged);
+
+    // Get completeListSize and cursor.
+    String sizeStr = toktag.substring(toktag.indexOf("completeListSize=\"") + 18);
+    int size = Integer.parseInt(sizeStr.substring(0, sizeStr.indexOf("\"")));
+    String cursorStr = toktag.substring(toktag.indexOf("cursor=\"") + 8);
+    int cursor = Integer.parseInt(cursorStr.substring(0, cursorStr.indexOf("\"")));
+    System.out.println("\t" + theThreadName + "size: " + size + " / " + cursor);
+
+    // If token is provided, test cursor and element count!
+    if (!restok.isEmpty()) {
+      synchronized (TestGetRecordOnline.class) {
+        // Check <record> or <header> count, must be recordsExpectedPerRequest!
+        if (recordCount != recordsExpectedPerRequest) {
+          String message =
+              OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader + " count mismatch, must be "
+                  + recordsExpectedPerRequest + " if token is provided, but is " + recordCount;
+          assertTrue(message, false);
+        }
+        if (size <= recordsExpectedPerRequest) {
+          String message =
+              OAIPMHUtilitiesOnline.ERROR + ": completeListSize count mismatch, must be > "
+                  + recordsExpectedPerRequest + " if token is provided, but is " + size;
+          assertTrue(message, false);
+        }
+        if (cursor != recordsExpectedPerRequest * loopCount) {
+          String message = OAIPMHUtilitiesOnline.ERROR + ": cursor must be "
+              + (loopCount * recordsExpectedPerRequest) + " in loop " + loopCount + ", but is "
+              + cursor;
+          assertTrue(message, false);
+        }
+      }
+    }
+
+    // If no token is provided, stop querying.
+    else {
+      // Check <record> count, must be completeListSize % recordsExpectedPerRequest.
+      if (recordCount != size % recordsExpectedPerRequest) {
+        System.err.println(theThreadName + OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader
+            + " count mismatch, should be " + size % recordsExpectedPerRequest + ", but is "
+            + recordCount);
+        assertTrue(false);
+      }
+
+      // No resumption token available in response.
+      return OAIPMHUtilitiesOnline.NO_TOKEN;
+    }
+
+    return restok;
+  }
+
+  /**
+   * @param theThreadName
+   * @return
+   */
+  protected static String tn(String theThreadName) {
+
+    String result = theThreadName;
+
+    if (theThreadName != null && !theThreadName.equals("") && !theThreadName.endsWith(" ")) {
+      result = "[" + theThreadName + "] ";
+    }
+
+    return result;
+  }
+
+  /**
+   * @param theResponseString
+   * @param theMetadataFormat
+   */
+  protected static void examineContent(String theResponseString, String theMetadataFormat) {
+
+    // Check for correct metadata content according to metadata prefix.
+    if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_DC_PREFIX)
+        && !theResponseString
+            .contains("metadataPrefix=\"" + OAIPMHUtilitiesOnline.OAI_DC_PREFIX + "\"")
+        && !theResponseString.contains(OAIPMHUtilitiesOnline.EXPECTED_OAIDC_FORMAT_CONTENT)) {
+      System.out
+          .println(OAIPMHUtilitiesOnline.OAI_DC_PREFIX + " needs to deliver content with schema: "
+              + OAIPMHUtilitiesOnline.EXPECTED_OAIDC_FORMAT_CONTENT + "!");
+
+      System.out.println("ERROR:\n" + theResponseString);
+
+      assertTrue(false);
+    } else if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX)
+        && !theResponseString.contains(
+            "metadataPrefix=\"" + OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT
+                + "\"")
+        && !theResponseString
+            .contains(OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT)) {
+      System.out.println(
+          OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX + " needs to deliver content with schema: "
+              + OAIPMHUtilitiesOnline.EXPECTED_IDIOMMETS_FORMAT_CONTENT + "!");
+
+      System.out.println("ERROR:\n" + theResponseString);
+
+      assertTrue(false);
+    } else if (theMetadataFormat.equals(OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX)
+        && !theResponseString
+            .contains("metadataPrefix=\"" + OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX + "\"")
+        && !theResponseString.contains(OAIPMHUtilitiesOnline.EXPECTED_DATACITE_FORMAT_CONTENT)) {
+      System.out.println(
+          OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX + " needs to deliver content with schema: "
+              + OAIPMHUtilitiesOnline.EXPECTED_DATACITE_FORMAT_CONTENT + "!");
+
+      System.out.println("ERROR:\n" + theResponseString);
+
+      assertTrue(false);
+    } else {
+      System.out.println(theResponseString);
+    }
+  }
+
+  /**
+   * @param theResponseString
+   * @param recordOrHeader
+   * @param oldtok
+   * @param theMetadataPrefix
+   * @return Resumption token string, if existing, -1 if either no <resumptionToken> tag is
+   *         existing, tag is existing and has no token value.
+   * @throws IOException
+   */
+  protected static String examineResumptionTokenTag(String theResponseString, String recordOrHeader,
+      String oldtok, String theMetadataPrefix) throws IOException {
+
+    // Default entries per page are 100 (IDIOM: 30).
+    // momentarily.
+    int expectedCount = 100;
+    if (theMetadataPrefix.equals(OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX)) {
+      expectedCount = 30;
+    }
+
+    // Test for OAIPMH errors.
+    if (theResponseString.contains("<error code=\"badArgument\">")) {
+      System.err.println(OAIPMHUtilitiesOnline.ERROR + " IN OAIPMH RESPONSE: " + theResponseString);
+      assertTrue(false);
+    }
+
+    // Count response objects at first.
+    int recordCount = 0;
+    int i = theResponseString.indexOf("<" + recordOrHeader + ">", 0);
+    while (i != -1) {
+      recordCount++;
+      i++;
+      i = theResponseString.indexOf("<" + recordOrHeader + ">", i);
+    }
+
+    System.out.println("\t" + recordOrHeader + "s: " + recordCount);
+
+    // Check if token tag is existing.
+    int tokStart = theResponseString.indexOf("<resumptionToken");
+    int tokEnd = theResponseString.indexOf("</resumptionToken");
+
+    if (tokStart == -1 && tokEnd == -1) {
+      System.out.println("\ttoken: no token");
+
+      return "-1";
+    }
+
+    String restokTmp = theResponseString.substring(tokStart, tokEnd);
+    // Get token tag.
+    String toktag = restokTmp.substring(0, restokTmp.indexOf(">") + 1)
+        .trim();
+    System.out.println("\ttokentag: " + toktag);
+
+    // Get token.
+    String restok = restokTmp.substring(restokTmp.indexOf(">") + 1).trim();
+    System.out.println("\ttoken: " + restok);
+
+    // Check if old and new token are equal or not.
+    boolean tokchanged = !oldtok.equals(restok);
+    System.out.println("\ttokchngd: " + tokchanged);
+
+    // Get completeListSize and cursor.
+    String sizeStr = toktag
+        .substring(toktag.indexOf("completeListSize=\"") + 18);
+    int size = Integer
+        .parseInt(sizeStr.substring(0, sizeStr.indexOf("\"")));
+    String cursorStr = toktag.substring(toktag.indexOf("cursor=\"") + 8);
+    int cursor = Integer
+        .parseInt(cursorStr.substring(0, cursorStr.indexOf("\"")));
+    System.out.println("\tsize: " + size + " / " + cursor);
+
+    // If token is provided, and we have less than 100 (IDIOM: 30) elements: mekkern!
+    if (!restok.isEmpty()) {
+      synchronized (TestDHOAIPMHOnline.class) {
+        // Check <record> or <header> count, must be 100 (IDIOM: 30)!
+        if (recordCount != expectedCount) {
+          System.err.println(
+              OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader + " count mismatch, must be "
+                  + expectedCount + " if token is provided, but is " + recordCount);
+        }
+        if (size <= expectedCount) {
+          System.err
+              .println(OAIPMHUtilitiesOnline.ERROR + ": completeListSize count mismatch, must be > "
+                  + expectedCount + " if token is provided, but is " + size);
+        }
+        if (recordCount != expectedCount || size <= expectedCount) {
+          assertTrue(false);
+        }
+      }
+    }
+
+    // If no token is provided, stop querying.
+    else {
+      // Check <record> count, must be completeListSize % 100 (IDIOM: 30).
+      if (recordCount != size % expectedCount) {
+        System.err.println(OAIPMHUtilitiesOnline.ERROR + ": " + recordOrHeader
+            + " count mismatch, should be " + size % expectedCount + ", but is " + recordCount);
+        assertTrue(false);
+      }
+
+      // No resumption token available in response.
+      return "-1";
+    }
+
+    return restok;
+  }
+
+  /**
+   * @param theWebClient
+   * @param theVerb
+   * @param theMetadataPrefix
+   * @param theSet
+   * @return
+   * @throws IOException
+   */
+  protected static int testList(Client theWebClient, String theVerb, String theMetadataPrefix,
+      String theSet) throws IOException {
+    return testList(theWebClient, theVerb, theMetadataPrefix, theSet, OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL);
+  }
+
+  /**
+   * @param theWebClient
+   * @param theVerb
+   * @param theMetadataPrefix
+   * @param theSet
+   * @param from
+   * @param until
+   * @return How many pages were delivered
+   * @throws IOException
+   */
+  protected static int testList(Client theWebClient, String theVerb, String theMetadataPrefix,
+      String theSet, String from, String until) throws IOException {
+
+    int result;
+
+    long startTime = System.currentTimeMillis();
+
+    String testOccurance = "header";
+    if (theVerb.equals(OAIPMHUtilitiesOnline.VERB_LIST_RECORDS)) {
+      testOccurance = "record";
+    }
+
+    String path = "verb=" + theVerb + "&metadataPrefix=" + theMetadataPrefix;
+
+    if (theSet != null && !theSet.isEmpty()) {
+      path += "&set=" + theSet;
+    }
+
+    if (from != null && until != null) {
+      path += "&from=" + from + "&until=" + until;
+    }
+
+    Response response = OAIPMHUtilitiesOnline.getHttpResponse(theWebClient, path);
+    String responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
+    int status = response.getStatus();
+
+    long timeRunning = System.currentTimeMillis() - startTime;
+    System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
+
+    String restok = examineResumptionTokenTag(responseString, testOccurance, "", theMetadataPrefix);
+    examineIdentifiers(responseString);
+
+    result = 1;
+    while (status == HttpStatus.SC_OK && !restok.equals("-1")) {
+      path = "verb=" + theVerb + "&resumptionToken=" + restok;
+      response = OAIPMHUtilitiesOnline.getHttpResponse(theWebClient, path);
+      responseString = IOUtils.readStringFromStream((InputStream) response.getEntity());
+      timeRunning = System.currentTimeMillis() - startTime;
+      System.out.println("\ttime: " + OAIPMHUtilitiesOnline.getDurationInSecs(timeRunning));
+      restok = examineResumptionTokenTag(responseString, testOccurance, restok, theMetadataPrefix);
+      examineIdentifiers(responseString);
+      result++;
+    }
+
+    System.out.println("\tpage amount: " + result);
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+
+    return result;
+  }
+
+  /**
+   * @param theResponseString
+   * @return
+   * @throws IOException
+   */
+  protected static void examineIdentifiers(String theResponseString) throws IOException {
+    if (theResponseString.contains("<identifier>hdl:21</identifier>")) {
+      System.err.println(OAIPMHUtilitiesOnline.ERROR
+          + " IN OAIPMH RESPONSE: identifier tag is corrupt!" + theResponseString);
+      assertTrue(false);
+    }
+  }
+
+}
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TGOAIPMHResumptionTokenThread.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TGOAIPMHResumptionTokenThread.java
new file mode 100644
index 00000000..701a9700
--- /dev/null
+++ b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TGOAIPMHResumptionTokenThread.java
@@ -0,0 +1,60 @@
+package info.textgrid.middleware.test.online;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import org.apache.cxf.jaxrs.client.Client;
+
+
+
+/**
+ * <p>
+ * Starts a new OAI-PMH thread.
+ * </p>
+ */
+
+public class TGOAIPMHResumptionTokenThread implements Callable<Boolean> {
+
+  protected Client client;
+  protected String verb;
+  protected String set;
+  protected String metadataPrefix;
+  protected int numberOfPagesToTest;
+  protected int recordsExpectedPerRequest;
+  protected String threadName;
+
+  /**
+   * @param theClient
+   * @param theVerb
+   * @param theSet
+   * @param theMetadataPrefix
+   * @param numberOfPagesToTest
+   * @param recordsExpectedPerRequest
+   * @param theThreadName
+   */
+  public TGOAIPMHResumptionTokenThread(Client theClient, String theVerb, String theSet,
+      String theMetadataPrefix, int numberOfPagesToTest, int recordsExpectedPerRequest,
+      String theThreadName) {
+    this.client = theClient;
+    this.verb = theVerb;
+    this.set = theSet;
+    this.metadataPrefix = theMetadataPrefix;
+    this.numberOfPagesToTest = numberOfPagesToTest;
+    this.recordsExpectedPerRequest = recordsExpectedPerRequest;
+    this.threadName = theThreadName;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see java.lang.Thread#run()
+   */
+  @Override
+  public Boolean call() throws IOException {
+    OAIPMHUtilitiesOnline.examineTGList(this.client, this.verb, this.set, this.metadataPrefix,
+        this.numberOfPagesToTest, this.recordsExpectedPerRequest, this.threadName,
+        OAIPMHUtilitiesOnline.NO_FROM, OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.NO_METADATA_FORMAT_WITH_RESTOK);
+    return true;
+  }
+
+}
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestDHOAIPMHOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestDHOAIPMHOnline.java
similarity index 100%
rename from oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestDHOAIPMHOnline.java
rename to oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestDHOAIPMHOnline.java
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestTGOAIPMHOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGBasicsOnline.java
similarity index 100%
rename from oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestTGOAIPMHOnline.java
rename to oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGBasicsOnline.java
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestGetRecordOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGGetRecordOnline.java
similarity index 100%
rename from oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestGetRecordOnline.java
rename to oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGGetRecordOnline.java
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListIdentifiersOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListIdentifiersOnline.java
new file mode 100644
index 00000000..cae83921
--- /dev/null
+++ b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListIdentifiersOnline.java
@@ -0,0 +1,450 @@
+package info.textgrid.middleware.test.online;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import org.apache.cxf.jaxrs.client.Client;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * <p>
+ * Some online tests for the TextGrid OAIMPH service --> verb=ListIdentifiers <--
+ * </p>
+ * 
+ * @author Stefan E. Funk, SUB Göttingen
+ * @version 2022-09-13
+ * @since 2022-09-12
+ */
+@Ignore
+public class TestListIdentifiersOnline {
+
+  // **
+  // FINALS
+  // **
+
+  // private static final String PROPERTIES_FILE = "oaipmh.test.textgridlab-org.properties";
+  // TODO There is NO IDIOM test data for testing on dev.textgridlab.org! So eventually all
+  // IDIOM tests will fail!
+  protected static final String PROPERTIES_FILE = "oaipmh.test.dev-textgridlab-org.properties";
+
+  // **
+  // STATICS
+  // **
+
+  // Some JAXRS things.
+  private static String oaipmhEndpoint;
+  static Client oaipmhWebClient;
+
+  // Properties
+
+  private static String checkListIdentifiersSet;
+  private static int checkListIdentifiersPagesToTestIDIOM;
+  private static int checkListIdentifiersRecordsPerPageIDIOM;
+
+  // **
+  // PREPARATIONS
+  // **
+
+  /**
+   * @throws Exception
+   */
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+
+    // Get properties.
+    Properties p = OAIPMHUtilitiesOnline.getPropertiesFromFile(PROPERTIES_FILE);
+
+    // Set properties.
+    oaipmhEndpoint = p.getProperty("oaipmhEndpoint");
+
+    checkListIdentifiersSet = p.getProperty("checkListIdentifiersSet");
+    checkListIdentifiersPagesToTestIDIOM =
+        Integer.parseInt(p.getProperty("checkListIdentifiersPagesToTestIDIOM"));
+    checkListIdentifiersRecordsPerPageIDIOM =
+        Integer.parseInt(p.getProperty("checkListIdentifiersRecordsPerPageIDIOM"));
+
+    // Get web client from endpoint.
+    oaipmhWebClient = OAIPMHUtilitiesOnline.getOAIPMHWEebClient(oaipmhEndpoint);
+  }
+
+  /**
+   * @throws Exception
+   */
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    //
+  }
+
+  /**
+   * @throws Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+    //
+  }
+
+  /**
+   * @throws Exception
+   */
+  @After
+  public void tearDown() throws Exception {
+    //
+  }
+
+  // **
+  // TESTS
+  // **
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersOAIDCMorePages() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "testListIdentifiersOAIDCMorePages()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        checkListIdentifiersSet,
+        OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+        30, 100,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.NO_METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test(expected = IOException.class)
+  public void testListIdentifiersOAIDCMorePagesAndMetadataFormatWithRestok() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING
+        + "testListIdentifiersOAIDCMorePagesAndMetadataFormatWithRestok()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        checkListIdentifiersSet,
+        OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+        30, 100,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  @Ignore
+  public void testListIdentifiersIDIOMMETSAllPages() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "testListIdentifiersIDIOMMETSAllPages()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        OAIPMHUtilitiesOnline.NO_SET,
+        OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+        0, 30,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.NO_METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersIDIOMMETSMorePages() throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING + "testListIdentifiersIDIOMMETSMorePages()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        OAIPMHUtilitiesOnline.NO_SET,
+        OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+        6, 30,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.NO_METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test(expected = IOException.class)
+  public void testListIdentifiersIDIOMMETSSomePagesAndMetadataFormatWithRestok()
+      throws IOException {
+
+    System.out.println(OAIPMHUtilitiesOnline.TESTING
+        + "testListIdentifiersIDIOMMETSSomePagesAndMetadataFormatWithRestok()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        OAIPMHUtilitiesOnline.NO_SET,
+        OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+        3, 30,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   */
+  @Test
+  public void testListIdentifiersDATACITEMorePages() throws IOException {
+
+    System.out
+        .println(OAIPMHUtilitiesOnline.TESTING + "testListIdentifiersDATACITEMorePages()");
+
+    OAIPMHUtilitiesOnline.examineTGList(oaipmhWebClient,
+        OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+        OAIPMHUtilitiesOnline.NO_SET,
+        OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX,
+        22, 100,
+        OAIPMHUtilitiesOnline.NO_THREAD_NAME,
+        OAIPMHUtilitiesOnline.NO_FROM,
+        OAIPMHUtilitiesOnline.NO_UNTIL,
+        OAIPMHUtilitiesOnline.NO_METADATA_FORMAT_WITH_RESTOK);
+
+    System.out.println(OAIPMHUtilitiesOnline.OK);
+  }
+
+  /**
+   * @throws IOException
+   * @throws ExecutionException
+   * @throws InterruptedException
+   */
+  @Test
+  public void testListIdentifiersConcurrentlyIDIOMMETSMorePages()
+      throws IOException, InterruptedException, ExecutionException {
+
+    System.out.println(
+        OAIPMHUtilitiesOnline.TESTING + "testListIdentifiersConcurrentlyIDIOMMETSMorePages()");
+
+    ExecutorService executor = Executors.newFixedThreadPool(3);
+
+    Future<Boolean> f1 = executor
+        .submit(new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            3, 30,
+            "C1"));
+    Future<Boolean> f2 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            5, 30,
+            "C2"));
+    Future<Boolean> f3 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            8, 30,
+            "C2"));
+
+    executor.shutdown();
+
+    System.out.println(OAIPMHUtilitiesOnline.OK + ": [C1]=" + f1.get() + ", [C2]=" + f2.get()
+        + ", [C3]=" + f3.get());
+  }
+
+  /**
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws ExecutionException
+   */
+  @Test
+  public void testRestokConcurrentlyListIdentifiersDC()
+      throws InterruptedException, ExecutionException {
+
+    System.out
+        .println(OAIPMHUtilitiesOnline.TESTING + "testRestokConcurrentlyListIdentifiersDC()");
+
+    ExecutorService executor = Executors.newFixedThreadPool(3);
+
+    Future<Boolean> f1 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+            36, 100,
+            "A1"));
+    Future<Boolean> f2 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+            27, 100,
+            "A2"));
+    Future<Boolean> f3 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+            19, 100,
+            "A3"));
+
+    executor.shutdown();
+
+    System.out.println(OAIPMHUtilitiesOnline.OK + ": [A1]=" + f1.get() + ", [A2]=" + f2.get()
+        + ", [A3]=" + f3.get());
+  }
+
+  /**
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws ExecutionException
+   */
+  @Test
+  public void testRestokConcurrentlyListIdentifiersIDIOMMETS()
+      throws InterruptedException, ExecutionException {
+
+    System.out
+        .println(
+            OAIPMHUtilitiesOnline.TESTING + "testRestokConcurrentlyListIdentifiersIDIOM()");
+
+    ExecutorService executor = Executors.newFixedThreadPool(3);
+
+    Future<Boolean> f1 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            13, 30,
+            "A1"));
+    Future<Boolean> f2 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            35, 30,
+            "A2"));
+    Future<Boolean> f3 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            11, 30,
+            "A3"));
+
+    executor.shutdown();
+
+    System.out.println(OAIPMHUtilitiesOnline.OK + ": [A1]=" + f1.get() + ", [A2]=" + f2.get()
+        + ", [A3]=" + f3.get());
+  }
+
+  /**
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws ExecutionException
+   */
+  @Test
+  public void testRestokConcurrentlyListIdentifiersDATACITE()
+      throws InterruptedException, ExecutionException {
+
+    System.out
+        .println(
+            OAIPMHUtilitiesOnline.TESTING + "testRestokConcurrentlyListIdentifiersDATACITE()");
+
+    ExecutorService executor = Executors.newFixedThreadPool(3);
+
+    Future<Boolean> f1 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX,
+            13, 100,
+            "A1"));
+    Future<Boolean> f2 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX,
+            20, 100,
+            "A2"));
+    Future<Boolean> f3 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DATACITE_PREFIX,
+            8, 100,
+            "A3"));
+
+    executor.shutdown();
+
+    System.out.println(OAIPMHUtilitiesOnline.OK + ": [A1]=" + f1.get() + ", [A2]=" + f2.get()
+        + ", [A3]=" + f3.get());
+  }
+
+  /**
+   * @throws InterruptedException
+   * @throws ExecutionException
+   */
+  @Test
+  public void testRestokConcurrentlyListIdentifiersDCAndIDIOMMETS()
+      throws InterruptedException, ExecutionException {
+
+    System.out.println(
+        OAIPMHUtilitiesOnline.TESTING
+            + "testRestokConcurrentlyListIdentifiersDCAndIDIOMMets()");
+
+    ExecutorService executor = Executors.newFixedThreadPool(3);
+
+    Future<Boolean> f1 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+            53, 100,
+            "DC1"));
+    Future<Boolean> f2 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_DC_PREFIX,
+            28, 100,
+            "DC2"));
+    Future<Boolean> f3 = executor.submit(
+        new OAIPMHResumptionTokenThread(oaipmhWebClient,
+            OAIPMHUtilitiesOnline.VERB_LIST_IDENTIFIERS,
+            OAIPMHUtilitiesOnline.NO_SET,
+            OAIPMHUtilitiesOnline.OAI_IDIOMMETS_PREFIX,
+            checkListIdentifiersPagesToTestIDIOM,
+            checkListIdentifiersRecordsPerPageIDIOM,
+            "IDIOM"));
+
+    executor.shutdown();
+
+    System.out.println(OAIPMHUtilitiesOnline.OK + ": [DC1]=" + f1.get() + ", [DC2]=" + f2.get()
+        + ", [IDIOM]=" + f3.get());
+  }
+
+}
diff --git a/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestListRecordsOnline.java b/oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListRecordsOnline.java
similarity index 100%
rename from oaipmh-core/src/test/java/info/textgrid/middleware/test/online/TestListRecordsOnline.java
rename to oaipmh-core/src/test/java/info/textgrid/middleware/test/online/tg/TestTGListRecordsOnline.java
-- 
GitLab