diff --git a/common/persistence/src/main/java/app/coronawarn/server/services/common/persistence/service/DiagnosisKeyService.java b/common/persistence/src/main/java/app/coronawarn/server/services/common/persistence/service/DiagnosisKeyService.java
--- a/common/persistence/src/main/java/app/coronawarn/server/services/common/persistence/service/DiagnosisKeyService.java
+++ b/common/persistence/src/main/java/app/coronawarn/server/services/common/persistence/service/DiagnosisKeyService.java
@@ -13,16 +13,12 @@ public class DiagnosisKeyService {
   private DiagnosisKeyRepository keyRepository;

   /**
-   * Persists the specified collection of {@link DiagnosisKey} instances. Use the returned
-   * collection for further operations as the saveDiagnosisKeys operation might have changed the
-   * {@link DiagnosisKey} instances completely.
+   * Persists the specified collection of {@link DiagnosisKey} instances.
    *
    * @param diagnosisKeys must not contain {@literal null}.
-   * @return a collection of the saved keys; will never contain {@literal null}.
    * @throws IllegalArgumentException in case the given collection contains {@literal null}.
    */
-  public Collection<DiagnosisKey> saveDiagnosisKeys(
-      Collection<DiagnosisKey> diagnosisKeys) {
-    return keyRepository.saveAll(diagnosisKeys);
+  public void saveDiagnosisKeys(Collection<DiagnosisKey> diagnosisKeys) {
+    keyRepository.saveAll(diagnosisKeys);
   }
 }
diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java
--- a/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java
+++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/controller/SubmissionController.java
@@ -1,23 +1,22 @@
 package app.coronawarn.server.services.submission.controller;

-import java.util.Collection;
-import java.util.Collections;
-import java.util.stream.Collectors;
 import app.coronawarn.server.common.protocols.generated.ExposureKeys.TemporaryExposureKey;
 import app.coronawarn.server.services.common.persistence.domain.DiagnosisKey;
 import app.coronawarn.server.services.common.persistence.service.DiagnosisKeyService;
 import app.coronawarn.server.services.submission.verification.TanVerifier;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Collectors;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestHeader;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;

-// TODO Implement Unit Tests
 @RestController
 @RequestMapping("/version/v1")
 public class SubmissionController {
@@ -34,9 +33,8 @@ public ResponseEntity<String> hello() {
   }

   // TODO update protoSpec and endpoint to Collection<TemporaryExposureKey>
-  @PostMapping(value = "/diagnosis-keys/country/{country}")
-  public ResponseEntity<String> submitDiagnosisKey(
-      @PathVariable String country,
+  @PostMapping(value = "/diagnosis-keys")
+  public ResponseEntity<Void> submitDiagnosisKey(
       @RequestBody TemporaryExposureKey exposureKeys,
       @RequestHeader(value = "cwa-fake") Integer fake,
       @RequestHeader(value = "cwa-authorization") String tan) {
@@ -56,15 +54,14 @@ public ResponseEntity<String> submitDiagnosisKey(
   /**
    * @return A response that indicates that an invalid TAN was specified in the request.
    */
-  private ResponseEntity<String> buildTanInvalidResponseEntity() {
-    // TODO implement
-    return null;
+  private ResponseEntity<Void> buildTanInvalidResponseEntity() {
+    return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
   }

   /**
    * @return A response that indicates successful request processing.
    */
-  private ResponseEntity<String> buildSuccessResponseEntity() {
+  private ResponseEntity<Void> buildSuccessResponseEntity() {
     return ResponseEntity.ok().build();
   }

diff --git a/services/submission/src/main/resources/application.properties b/services/submission/src/main/resources/application.properties
--- a/services/submission/src/main/resources/application.properties
+++ b/services/submission/src/main/resources/application.properties
@@ -1,3 +1,3 @@
 logging.level.org.springframework.web=DEBUG
-spring.jpa.hibernate.ddl-auto = create
+spring.jpa.hibernate.ddl-auto=create
 spring.flyway.enabled=false

diff --git a/services/distribution/src/test/resources/application.properties b/services/distribution/src/test/resources/application.properties
--- a/services/distribution/src/test/resources/application.properties
+++ b/services/distribution/src/test/resources/application.properties
@@ -1,5 +1,4 @@
-spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
-spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1
-spring.datasource.username=cwa
-spring.datasource.password=cwa
-spring.jpa.hibernate.ddl-auto=create
\ No newline at end of file
+spring.jpa.hibernate.ddl-auto=create
+logging.level.org.springframework=off
+logging.level.root=warn
+spring.main.banner-mode=off
diff --git a/services/distribution/src/test/resources/logback-test.xml b/services/distribution/src/test/resources/logback-test.xml
new file mode 100644
--- /dev/null
+++ b/services/distribution/src/test/resources/logback-test.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <include resource="org/springframework/boot/logging/logback/base.xml" />
+  <logger name="org.springframework" level="off"/>
+</configuration>
diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/ServerApplicationTests.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/ServerApplicationTests.java
--- a/services/submission/src/test/java/app/coronawarn/server/services/submission/ServerApplicationTests.java
+++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/ServerApplicationTests.java
@@ -1,13 +1,20 @@
 package app.coronawarn.server.services.submission;

+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import app.coronawarn.server.services.submission.controller.SubmissionController;
 import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;

 @SpringBootTest
 class ServerApplicationTests {

-	@Test
-	void contextLoads() {
-	}
+  @Autowired
+  private SubmissionController controller;

+  @Test
+  public void contextLoads() {
+    assertNotNull(this.controller);
+  }
 }
diff --git a/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java
new file mode 100644
--- /dev/null
+++ b/services/submission/src/test/java/app/coronawarn/server/services/submission/controller/SubmissionControllerTest.java
@@ -0,0 +1,159 @@
+package app.coronawarn.server.services.submission.controller;
+
+
+import static app.coronawarn.server.common.protocols.generated.ExposureKeys.TemporaryExposureKey;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyCollection;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import app.coronawarn.server.services.common.persistence.service.DiagnosisKeyService;
+import app.coronawarn.server.services.submission.verification.TanVerifier;
+import com.google.protobuf.ByteString;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+public class SubmissionControllerTest {
+
+  private static final URI SUBMISSION_URL = URI.create("/version/v1/diagnosis-keys");
+
+  @MockBean
+  private DiagnosisKeyService diagnosisKeyService;
+
+  @MockBean
+  private TanVerifier tanVerifier;
+
+  @Autowired
+  private TestRestTemplate testRestTemplate;
+
+  @BeforeEach
+  public void setUpMocks() {
+    when(tanVerifier.verifyTan(anyString())).thenReturn(true);
+  }
+
+  @Test
+  public void checkResponseStatusForValidParameters() {
+    ResponseEntity<Void> actResponse =
+        executeRequest(buildTemporaryExposureKey(), buildOkHeaders());
+
+    assertEquals(HttpStatus.OK, actResponse.getStatusCode());
+  }
+
+  @Test
+  public void checkSaveOperationCallForValidParameters() {
+    executeRequest(buildTemporaryExposureKey(), buildOkHeaders());
+
+    verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(anyCollection());
+  }
+
+  @ParameterizedTest
+  @MethodSource("createIncompleteHeaders")
+  public void badRequestIfCwaHeadersMissing(HttpHeaders headers) {
+    ResponseEntity<Void> actResponse = executeRequest(buildTemporaryExposureKey(), headers);
+
+    verify(diagnosisKeyService, never()).saveDiagnosisKeys(any());
+    assertEquals(HttpStatus.BAD_REQUEST, actResponse.getStatusCode());
+  }
+
+  private static Stream<Arguments> createIncompleteHeaders() {
+    return Stream.of(
+        Arguments.of(setContentTypeProtoBufHeader(new HttpHeaders())),
+        Arguments.of(setContentTypeProtoBufHeader(setCwaFakeHeader(new HttpHeaders(), "0"))),
+        Arguments.of(setContentTypeProtoBufHeader(setCwaAuthHeader(new HttpHeaders()))));
+  }
+
+  @Test
+  public void checkAcceptedHttpMethods() {
+    Set<HttpMethod> expAllowedMethods =
+        Stream.of(HttpMethod.POST, HttpMethod.OPTIONS)
+            .collect(Collectors.toCollection(HashSet::new));
+
+    Set<HttpMethod> actAllowedMethods = testRestTemplate.optionsForAllow(SUBMISSION_URL.toString());
+
+    assertEquals(expAllowedMethods, actAllowedMethods);
+  }
+
+  @Test
+  public void invalidTanHandling() {
+    when(tanVerifier.verifyTan(anyString())).thenReturn(false);
+
+    ResponseEntity<Void> actResponse =
+        executeRequest(buildTemporaryExposureKey(), buildOkHeaders());
+
+    verify(diagnosisKeyService, never()).saveDiagnosisKeys(any());
+    assertEquals(HttpStatus.FORBIDDEN, actResponse.getStatusCode());
+  }
+
+  @Test
+  public void fakeRequestHandling() {
+    HttpHeaders headers = buildOkHeaders();
+    setCwaFakeHeader(headers, "1");
+
+    ResponseEntity<Void> actResponse = executeRequest(buildTemporaryExposureKey(), headers);
+
+    verify(diagnosisKeyService, never()).saveDiagnosisKeys(any());
+    assertEquals(HttpStatus.OK, actResponse.getStatusCode());
+  }
+
+  private static HttpHeaders buildOkHeaders() {
+    HttpHeaders headers = setCwaAuthHeader(setContentTypeProtoBufHeader(new HttpHeaders()));
+
+    return setCwaFakeHeader(headers, "0");
+  }
+
+  private static HttpHeaders setContentTypeProtoBufHeader(HttpHeaders headers) {
+    headers.setContentType(MediaType.valueOf("application/x-protobuf"));
+    return headers;
+  }
+
+  private static HttpHeaders setCwaAuthHeader(HttpHeaders headers) {
+    headers.set("cwa-authorization", "okTan");
+    return headers;
+  }
+
+  private static HttpHeaders setCwaFakeHeader(HttpHeaders headers, String value) {
+    headers.set("cwa-fake", value);
+    return headers;
+  }
+
+  private static TemporaryExposureKey buildTemporaryExposureKey() {
+    return buildTemporaryExposureKey("testKey123456789", 3L, 2);
+  }
+
+  private static TemporaryExposureKey buildTemporaryExposureKey(
+      String keyData, long rollingStartNumber, int riskLevelValue) {
+    return TemporaryExposureKey.newBuilder()
+        .setKeyData(ByteString.copyFromUtf8(keyData))
+        .setRollingStartNumber(rollingStartNumber)
+        .setRiskLevelValue(riskLevelValue).build();
+  }
+
+  private ResponseEntity<Void> executeRequest(TemporaryExposureKey body, HttpHeaders headers) {
+    RequestEntity<TemporaryExposureKey> request =
+        new RequestEntity<>(body, headers, HttpMethod.POST, SUBMISSION_URL);
+    return testRestTemplate.postForEntity(SUBMISSION_URL, request, Void.class);
+  }
+}
diff --git a/services/submission/src/test/resources/application.properties b/services/submission/src/test/resources/application.properties
--- a/services/submission/src/test/resources/application.properties
+++ b/services/submission/src/test/resources/application.properties
@@ -1,5 +1,4 @@
-spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
-spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1
-spring.datasource.username=cwa
-spring.datasource.password=cwa
-spring.jpa.hibernate.ddl-auto=create
\ No newline at end of file
+spring.jpa.hibernate.ddl-auto=create
+logging.level.org.springframework=off
+logging.level.root=warn
+spring.main.banner-mode=off
diff --git a/services/submission/src/test/resources/logback-test.xml b/services/submission/src/test/resources/logback-test.xml
new file mode 100644
--- /dev/null
+++ b/services/submission/src/test/resources/logback-test.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <include resource="org/springframework/boot/logging/logback/base.xml" />
+  <logger name="org.springframework" level="off"/>
+</configuration>

Diagnosis key submission unit tests
Implement unit tests for the diagnosis key submission service according to best practices.


diff --git a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/repository/DiagnosisKeyRepository.java b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/repository/DiagnosisKeyRepository.java
--- a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/repository/DiagnosisKeyRepository.java
+++ b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/repository/DiagnosisKeyRepository.java
@@ -1,9 +1,18 @@
 package app.coronawarn.server.common.persistence.repository;

 import app.coronawarn.server.common.persistence.domain.DiagnosisKey;
+import javax.transaction.Transactional;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;

 @Repository
 public interface DiagnosisKeyRepository extends JpaRepository<DiagnosisKey, Long> {
+
+  /**
+   * Deletes all entries that have a submission timestamp lesser or equal to the specified one.
+   *
+   * @param submissionTimestamp the submission timestamp up to which entries will be deleted.
+   */
+  @Transactional
+  public void deleteBySubmissionTimestampIsLessThanEqual(long submissionTimestamp);
 }
diff --git a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyService.java b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyService.java
--- a/common/persistence/src/main/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyService.java
+++ b/common/persistence/src/main/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyService.java
@@ -1,7 +1,11 @@
 package app.coronawarn.server.common.persistence.service;

+import static java.time.ZoneOffset.UTC;
+
 import app.coronawarn.server.common.persistence.domain.DiagnosisKey;
 import app.coronawarn.server.common.persistence.repository.DiagnosisKeyRepository;
+import java.time.Instant;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,4 +34,18 @@ public void saveDiagnosisKeys(Collection<DiagnosisKey> diagnosisKeys) {
   public List<DiagnosisKey> getDiagnosisKeys() {
     return keyRepository.findAll(Sort.by(Sort.Direction.ASC, "submissionTimestamp"));
   }
+
+  /**
+   * Deletes all diagnosis key entries which have a submission timestamp that is older than the
+   * specified number of days.
+   *
+   * @param daysToRetain the number of days until which diagnosis keys will be retained.
+   */
+  public void applyRetentionPolicy(int daysToRetain) {
+    long threshold = LocalDateTime
+        .ofInstant(Instant.now(), UTC)
+        .minusDays(daysToRetain)
+        .toEpochSecond(UTC) / 3600L;
+    keyRepository.deleteBySubmissionTimestampIsLessThanEqual(threshold);
+  }
 }
diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/DiagnosisKeyDistributionRunner.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/DiagnosisKeyDistributionRunner.java
--- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/DiagnosisKeyDistributionRunner.java
+++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/DiagnosisKeyDistributionRunner.java
@@ -24,7 +24,7 @@
 import org.springframework.stereotype.Component;

 @Component
-@Order(2)
+@Order(3)
 /**
  * This runner retrieves stored diagnosis keys, the generates and persists the respective diagnosis
  * key bundles.
diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/ExposureConfigurationDistributionRunner.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/ExposureConfigurationDistributionRunner.java
--- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/ExposureConfigurationDistributionRunner.java
+++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/ExposureConfigurationDistributionRunner.java
@@ -23,7 +23,7 @@
 import org.springframework.stereotype.Component;

 @Component
-@Order(1)
+@Order(2)
 /**
  * Reads the exposure configuration parameters from the respective file in the class path, then
  * generates and persists the respective exposure configuration bundle.
diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/RetentionPolicyRunner.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/RetentionPolicyRunner.java
new file mode 100644
--- /dev/null
+++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/RetentionPolicyRunner.java
@@ -0,0 +1,37 @@
+package app.coronawarn.server.services.distribution.runner;
+
+import app.coronawarn.server.common.persistence.service.DiagnosisKeyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ * This runner removes any diagnosis keys from the database that were submitted before a configured
+ * threshold of days.
+ */
+@Component
+@Order(1)
+public class RetentionPolicyRunner implements ApplicationRunner {
+
+  private static final Logger logger = LoggerFactory
+      .getLogger(RetentionPolicyRunner.class);
+
+  @Autowired
+  private DiagnosisKeyService diagnosisKeyService;
+
+  @Value("${app.coronawarn.server.services.distribution.retention_days}")
+  private Integer rententionDays;
+
+  @Override
+  public void run(ApplicationArguments args) {
+    diagnosisKeyService.applyRetentionPolicy(rententionDays);
+
+    logger.debug("Retention policy applied successfully. Deleted all entries older that {} days.",
+        rententionDays);
+  }
+}
diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/S3DistributionRunner.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/S3DistributionRunner.java
--- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/S3DistributionRunner.java
+++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/runner/S3DistributionRunner.java
@@ -16,7 +16,7 @@
  * This runner will sync the base working directory to the S3.
  */
 @Component
-@Order(3)
+@Order(4)
 public class S3DistributionRunner implements ApplicationRunner {

   private Logger logger = LoggerFactory.getLogger(this.getClass());
diff --git a/services/distribution/src/main/resources/application.properties b/services/distribution/src/main/resources/application.properties
--- a/services/distribution/src/main/resources/application.properties
+++ b/services/distribution/src/main/resources/application.properties
@@ -2,6 +2,7 @@ logging.level.org.springframework.web=INFO
 logging.level.app.coronawarn=INFO
 spring.main.web-application-type=NONE

+app.coronawarn.server.services.distribution.retention_days=14
 app.coronawarn.server.services.distribution.version=v1
 app.coronawarn.server.services.distribution.paths.output=out
 app.coronawarn.server.services.distribution.paths.privatekey=classpath:certificates/client/private.pem

diff --git a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/TestApplication.java b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/TestApplication.java
new file mode 100644
--- /dev/null
+++ b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/TestApplication.java
@@ -0,0 +1,15 @@
+package app.coronawarn.server.common.persistence;
+
+import app.coronawarn.server.common.persistence.service.DiagnosisKeyService;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@SpringBootApplication
+@Configuration
+public class TestApplication {
+  @Bean
+  DiagnosisKeyService createDiagnosisKeyService() {
+    return new DiagnosisKeyService();
+  }
+}
diff --git a/common/persistence/src/test/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyServiceTest.java b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyServiceTest.java
new file mode 100644
--- /dev/null
+++ b/common/persistence/src/test/java/app/coronawarn/server/common/persistence/service/DiagnosisKeyServiceTest.java
@@ -0,0 +1,125 @@
+package app.coronawarn.server.common.persistence.service;
+
+import static java.time.ZoneOffset.UTC;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import app.coronawarn.server.common.persistence.domain.DiagnosisKey;
+import app.coronawarn.server.common.persistence.repository.DiagnosisKeyRepository;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.assertj.core.util.Lists;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+
+@DataJpaTest
+public class DiagnosisKeyServiceTest {
+
+  @Autowired
+  private DiagnosisKeyService diagnosisKeyService;
+
+  @Autowired
+  private DiagnosisKeyRepository diagnosisKeyRepository;
+
+  @AfterEach
+  public void tearDown() {
+    diagnosisKeyRepository.deleteAll();
+  }
+
+  @Test
+  void testRetrievalForEmptyDB() {
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+    assertDiagnosisKeysEqual(Lists.emptyList(), actKeys);
+  }
+
+  @Test
+  void testSaveAndRetrieve() {
+    var expKeys = List.of(buildDiagnosisKeyForSubmissionTimestamp(0L));
+
+    diagnosisKeyService.saveDiagnosisKeys(expKeys);
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+
+    assertDiagnosisKeysEqual(expKeys, actKeys);
+  }
+
+  @Test
+  void testSortedRetrievalResult() {
+    var expKeys = new ArrayList<>(List.of(
+        buildDiagnosisKeyForSubmissionTimestamp(1L),
+        buildDiagnosisKeyForSubmissionTimestamp(0L)));
+
+    diagnosisKeyService.saveDiagnosisKeys(expKeys);
+
+    // reverse to match expected sort order
+    Collections.reverse(expKeys);
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+
+    assertDiagnosisKeysEqual(expKeys, actKeys);
+  }
+
+  @Test
+  void testApplyRetentionPolicyForEmptyDb() {
+    diagnosisKeyService.applyRetentionPolicy(1);
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+    assertDiagnosisKeysEqual(Lists.emptyList(), actKeys);
+  }
+
+  @Test
+  void testApplyRetentionPolicyForOneNotApplicableEntry() {
+    var expKeys = List.of(buildDiagnosisKeyForDateTime(OffsetDateTime.now(UTC).minusHours(23)));
+
+    diagnosisKeyService.saveDiagnosisKeys(expKeys);
+    diagnosisKeyService.applyRetentionPolicy(1);
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+
+    assertDiagnosisKeysEqual(expKeys, actKeys);
+  }
+
+  @Test
+  void testApplyRetentionPolicyForOneApplicableEntry() {
+    var keys = List.of(buildDiagnosisKeyForDateTime(OffsetDateTime.now(UTC).minusDays(1L)));
+
+    diagnosisKeyService.saveDiagnosisKeys(keys);
+    diagnosisKeyService.applyRetentionPolicy(1);
+    var actKeys = diagnosisKeyService.getDiagnosisKeys();
+
+    assertDiagnosisKeysEqual(Lists.emptyList(), actKeys);
+  }
+
+  private void assertDiagnosisKeysEqual(List<DiagnosisKey> expDiagKeys,
+      List<DiagnosisKey> actDiagKeys) {
+    assertEquals(expDiagKeys.size(), actDiagKeys.size(), "Cardinality mismatch");
+
+    for (int i = 0; i < expDiagKeys.size(); i++) {
+      var expDiagKey = expDiagKeys.get(i);
+      var actDiagKey = actDiagKeys.get(i);
+
+      assertEquals(
+          expDiagKey.getKeyData(), actDiagKey.getKeyData(), "keyData mismatch");
+      assertEquals(expDiagKey.getRollingStartNumber(), actDiagKey.getRollingStartNumber(),
+          "rollingStartNumber mismatch");
+      assertEquals(expDiagKey.getRollingPeriod(), actDiagKey.getRollingPeriod(),
+          "rollingPeriod mismatch");
+      assertEquals(expDiagKey.getTransmissionRiskLevel(), actDiagKey.getTransmissionRiskLevel(),
+          "transmissionRiskLevel mismatch");
+      assertEquals(expDiagKey.getSubmissionTimestamp(), actDiagKey.getSubmissionTimestamp(),
+          "submissionTimestamp mismatch");
+    }
+  }
+
+  public static DiagnosisKey buildDiagnosisKeyForSubmissionTimestamp(long submissionTimeStamp) {
+    return DiagnosisKey.builder()
+        .withKeyData(new byte[16])
+        .withRollingStartNumber(0L)
+        .withRollingPeriod(1L)
+        .withTransmissionRiskLevel(2)
+        .withSubmissionTimestamp(submissionTimeStamp).build();
+  }
+
+  public static DiagnosisKey buildDiagnosisKeyForDateTime(OffsetDateTime dateTime) {
+    return buildDiagnosisKeyForSubmissionTimestamp(dateTime.toEpochSecond() / 3600);
+  }
+}
diff --git a/common/persistence/src/test/resources/application.properties b/common/persistence/src/test/resources/application.properties
new file mode 100644
--- /dev/null
+++ b/common/persistence/src/test/resources/application.properties
@@ -0,0 +1,6 @@
+spring.flyway.enabled=false
+spring.jpa.hibernate.ddl-auto=create
+spring.jpa.properties.hibernate.show_sql=false
+spring.main.banner-mode=off
+logging.level.org.springframework=off
+logging.level.root=off
\ No newline at end of file
diff --git a/common/persistence/src/test/resources/logback-test.xml b/common/persistence/src/test/resources/logback-test.xml
new file mode 100644
--- /dev/null
+++ b/common/persistence/src/test/resources/logback-test.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <include resource="org/springframework/boot/logging/logback/base.xml" />
+  <logger name="org.springframework" level="off"/>
+  <logger name="root" level="ERROR"/>
+</configuration>
diff --git a/services/distribution/src/test/resources/application.properties b/services/distribution/src/test/resources/application.properties
--- a/services/distribution/src/test/resources/application.properties
+++ b/services/distribution/src/test/resources/application.properties
@@ -3,6 +3,7 @@ logging.level.org.springframework=off
 logging.level.root=off
 spring.main.banner-mode=off

+app.coronawarn.server.services.distribution.retention_days=14
 app.coronawarn.server.services.distribution.version=v1
 app.coronawarn.server.services.distribution.paths.output=out
 app.coronawarn.server.services.distribution.paths.privatekey=classpath:certificates/client/private.pem

Set Retention Days to 14
Any key data that is associated to a point in time older than 14 days shall be removed from the database and will also be removed from the CDN and therefore the index file.