diff --git a/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/ArchivedJpaEventPublication.java b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/ArchivedJpaEventPublication.java new file mode 100644 index 00000000..8c22a2cf --- /dev/null +++ b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/ArchivedJpaEventPublication.java @@ -0,0 +1,34 @@ +package org.springframework.modulith.events.jpa; + +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +import java.time.Instant; +import java.util.UUID; + +/** + * JPA entity to represent archived event publications. + * + * @author Oliver Drotbohm + */ +@Entity +@Table(name = "EVENT_PUBLICATION_ARCHIVE") +class ArchivedJpaEventPublication extends JpaEventPublication { + + /** + * Creates a new {@link ArchivedJpaEventPublication} for the given publication date, listener id, serialized event and + * event type. + * + * @param id + * @param publicationDate must not be {@literal null}. + * @param listenerId must not be {@literal null} or empty. + * @param serializedEvent must not be {@literal null} or empty. + * @param eventType must not be {@literal null}. + */ + public ArchivedJpaEventPublication(UUID id, Instant publicationDate, String listenerId, String serializedEvent, + Class eventType) { + super(id, publicationDate, listenerId, serializedEvent, eventType); + } + + public ArchivedJpaEventPublication() {} +} diff --git a/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublication.java b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublication.java index fd574acf..71b85b71 100644 --- a/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublication.java +++ b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublication.java @@ -15,10 +15,7 @@ */ package org.springframework.modulith.events.jpa; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import jakarta.persistence.*; import java.time.Instant; import java.util.UUID; @@ -34,6 +31,7 @@ import org.springframework.util.Assert; */ @Entity @Table(name = "EVENT_PUBLICATION") +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) class JpaEventPublication { final @Id @Column(length = 16) UUID id; @@ -68,7 +66,7 @@ class JpaEventPublication { this.eventType = eventType; } - JpaEventPublication() { + protected JpaEventPublication() { this.id = null; this.publicationDate = null; @@ -77,9 +75,11 @@ class JpaEventPublication { this.eventType = null; } - JpaEventPublication markCompleted() { + ArchivedJpaEventPublication archive(Instant instant) { - this.completionDate = Instant.now(); - return this; + var result = new ArchivedJpaEventPublication(id, publicationDate, listenerId, serializedEvent, eventType); + result.completionDate = instant; + + return result; } } diff --git a/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepository.java b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepository.java index 2bb762f6..da1a1724 100644 --- a/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepository.java +++ b/spring-modulith-events/spring-modulith-events-jpa/src/main/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepository.java @@ -38,6 +38,7 @@ import org.springframework.util.Assert; * @author Oliver Drotbohm * @author Dmitry Belyaev * @author Björn Kieling + * @author Cora Iberkleid */ @Transactional class JpaEventPublicationRepository implements EventPublicationRepository { @@ -53,7 +54,7 @@ class JpaEventPublicationRepository implements EventPublicationRepository { private static String COMPLETE = """ select p - from JpaEventPublication p + from %s p where p.completionDate is not null order by @@ -113,14 +114,14 @@ class JpaEventPublicationRepository implements EventPublicationRepository { private static final String DELETE_COMPLETED = """ delete - from JpaEventPublication p + from %s p where p.completionDate is not null """; private static final String DELETE_COMPLETED_BEFORE = """ delete - from JpaEventPublication p + from %s p where p.completionDate < ?1 """; @@ -131,6 +132,8 @@ class JpaEventPublicationRepository implements EventPublicationRepository { private final EventSerializer serializer; private final CompletionMode completionMode; + private final String getCompleted, deleteCompleted, deleteCompletedBefore; + /** * Creates a new {@link JpaEventPublicationRepository} for the given {@link EntityManager} and * {@link EventSerializer}. @@ -148,7 +151,15 @@ class JpaEventPublicationRepository implements EventPublicationRepository { this.entityManager = entityManager; this.serializer = serializer; this.completionMode = completionMode; - } + + var archiveEntityName = completionMode == CompletionMode.ARCHIVE + ? ArchivedJpaEventPublication.class.getSimpleName() + : JpaEventPublication.class.getSimpleName(); + + this.getCompleted = COMPLETE.formatted(archiveEntityName); + this.deleteCompleted = DELETE_COMPLETED.formatted(archiveEntityName); + this.deleteCompletedBefore = DELETE_COMPLETED_BEFORE.formatted(archiveEntityName); + } /* * (non-Javadoc) @@ -179,6 +190,18 @@ class JpaEventPublicationRepository implements EventPublicationRepository { .setParameter(2, identifierValue) .executeUpdate(); + } else if (completionMode == CompletionMode.ARCHIVE) { + + var publication = entityManager.createQuery(BY_EVENT_AND_LISTENER_ID, JpaEventPublication.class) + .setParameter(1, serializedEvent) + .setParameter(2, identifierValue) + .getSingleResult(); + + var archived = publication.archive(completionDate); + + entityManager.remove(publication); + entityManager.persist(archived); + } else { entityManager.createQuery(MARK_COMPLETED_BY_EVENT_AND_LISTENER_ID) @@ -202,6 +225,15 @@ class JpaEventPublicationRepository implements EventPublicationRepository { .setParameter(1, identifier) .executeUpdate(); + } else if (completionMode == CompletionMode.ARCHIVE) { + + var publication = entityManager.find(JpaEventPublication.class, identifier); + + var archived = publication.archive(completionDate); + + entityManager.remove(publication); + entityManager.persist(archived); + } else { entityManager.createQuery(MARK_COMPLETED_BY_ID) @@ -260,7 +292,11 @@ class JpaEventPublicationRepository implements EventPublicationRepository { @Override public List findCompletedPublications() { - return entityManager.createQuery(COMPLETE, JpaEventPublication.class) + var type = completionMode == CompletionMode.ARCHIVE + ? ArchivedJpaEventPublication.class + : JpaEventPublication.class; + + return entityManager.createQuery(getCompleted, type) .getResultList() .stream() .map(this::entityToDomain) @@ -285,7 +321,7 @@ class JpaEventPublicationRepository implements EventPublicationRepository { */ @Override public void deleteCompletedPublications() { - entityManager.createQuery(DELETE_COMPLETED).executeUpdate(); + entityManager.createQuery(deleteCompleted).executeUpdate(); } /* @@ -297,7 +333,7 @@ class JpaEventPublicationRepository implements EventPublicationRepository { Assert.notNull(instant, "Instant must not be null!"); - entityManager.createQuery(DELETE_COMPLETED_BEFORE) + entityManager.createQuery(deleteCompletedBefore) .setParameter(1, instant) .executeUpdate(); } @@ -341,6 +377,7 @@ class JpaEventPublicationRepository implements EventPublicationRepository { private final JpaEventPublication publication; private final EventSerializer serializer; + private Object deserializedEvent; /** * Creates a new {@link JpaEventPublicationAdapter} for the given {@link JpaEventPublication} and @@ -373,7 +410,12 @@ class JpaEventPublicationRepository implements EventPublicationRepository { */ @Override public Object getEvent() { - return serializer.deserialize(publication.serializedEvent, publication.eventType); + + if (deserializedEvent == null) { + this.deserializedEvent = serializer.deserialize(publication.serializedEvent, publication.eventType); + } + + return deserializedEvent; } /* diff --git a/spring-modulith-events/spring-modulith-events-jpa/src/test/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepositoryIntegrationTests.java b/spring-modulith-events/spring-modulith-events-jpa/src/test/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepositoryIntegrationTests.java index 1d69929a..e5e14007 100644 --- a/spring-modulith-events/spring-modulith-events-jpa/src/test/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepositoryIntegrationTests.java +++ b/spring-modulith-events/spring-modulith-events-jpa/src/test/java/org/springframework/modulith/events/jpa/JpaEventPublicationRepositoryIntegrationTests.java @@ -61,9 +61,8 @@ import org.springframework.transaction.annotation.Transactional; * @author Oliver Drotbohm * @author Dmitry Belyaev * @author Björn Kieling + * @author Cora Iberkleid */ -@SpringBootTest -@Transactional class JpaEventPublicationRepositoryIntegrationTests { private static final PublicationTargetIdentifier TARGET_IDENTIFIER = PublicationTargetIdentifier.of("listener"); @@ -112,218 +111,278 @@ class JpaEventPublicationRepositoryIntegrationTests { } } - @Autowired JpaEventPublicationRepository repository; - @Autowired EntityManager em; - @Autowired Environment environment; + @SpringBootTest + @Transactional + @ContextConfiguration(classes = TestConfig.class) + static abstract class TestBase { - CompletionMode completionMode; + @Autowired JpaEventPublicationRepository repository; + @Autowired EntityManager em; + @Autowired Environment environment; - @BeforeEach - void init() { - this.completionMode = environment.getProperty(CompletionMode.PROPERTY, CompletionMode.class); - } + CompletionMode completionMode; - @AfterEach - public void flush() { - em.flush(); - } + @BeforeEach + void init() { + this.completionMode = environment.getProperty(CompletionMode.PROPERTY, CompletionMode.class); + } - @Test - void persistsJpaEventPublication() { + @AfterEach + public void flush() { + em.flush(); + } - var testEvent = new TestEvent("abc"); - var serializedEvent = "{\"eventId\":\"abc\"}"; + @Test + void persistsJpaEventPublication() { - when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); - when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent); + var testEvent = new TestEvent("abc"); + var serializedEvent = "{\"eventId\":\"abc\"}"; - var publication = repository.create(TargetEventPublication.of(testEvent, TARGET_IDENTIFIER)); + when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); + when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent); - var eventPublications = repository.findIncompletePublications(); + var publication = repository.create(TargetEventPublication.of(testEvent, TARGET_IDENTIFIER)); - assertThat(eventPublications).hasSize(1); - assertThat(eventPublications.get(0).getEvent()).isEqualTo(publication.getEvent()); - assertThat(eventPublications.get(0).getTargetIdentifier()).isEqualTo(publication.getTargetIdentifier()); - assertThat(repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER)) - .isPresent(); + var eventPublications = repository.findIncompletePublications(); - repository.markCompleted(publication, Instant.now()); + assertThat(eventPublications).hasSize(1); + assertThat(eventPublications.get(0).getEvent()).isEqualTo(publication.getEvent()); + assertThat(eventPublications.get(0).getTargetIdentifier()).isEqualTo(publication.getTargetIdentifier()); + assertThat(repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER)) + .isPresent(); - assertThat(repository.findIncompletePublications()).isEmpty(); - } + repository.markCompleted(publication, Instant.now()); - @Test // GH-25 - void shouldTolerateEmptyResult() { - - var testEvent = new TestEvent("id"); - var serializedEvent = "{\"eventId\":\"id\"}"; - - when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); - - assertThat(repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER)).isEmpty(); - } - - @Test // GH-25 - void shouldNotReturnCompletedEvents() { - - TestEvent testEvent = new TestEvent("abc"); - String serializedEvent = "{\"eventId\":\"abc\"}"; - - when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); - when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent); - - var publication = TargetEventPublication.of(testEvent, TARGET_IDENTIFIER); - - repository.create(publication); - repository.markCompleted(publication, Instant.now()); - - var actual = repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER); - - assertThat(actual).isEmpty(); - } - - @Test // GH-20 - void shouldDeleteCompletedEvents() { - - var testEvent1 = new TestEvent("abc"); - var serializedEvent1 = "{\"eventId\":\"abc\"}"; - var testEvent2 = new TestEvent("def"); - var serializedEvent2 = "{\"eventId\":\"def\"}"; - - when(eventSerializer.serialize(testEvent1)).thenReturn(serializedEvent1); - when(eventSerializer.deserialize(serializedEvent1, TestEvent.class)).thenReturn(testEvent1); - when(eventSerializer.serialize(testEvent2)).thenReturn(serializedEvent2); - when(eventSerializer.deserialize(serializedEvent2, TestEvent.class)).thenReturn(testEvent2); - - repository.create(TargetEventPublication.of(testEvent1, TARGET_IDENTIFIER)); - repository.create(TargetEventPublication.of(testEvent2, TARGET_IDENTIFIER)); - repository.markCompleted(testEvent1, TARGET_IDENTIFIER, Instant.now()); - repository.deleteCompletedPublications(); - - assertThat(em.createQuery("select p from JpaEventPublication p", JpaEventPublication.class).getResultList()) - .hasSize(1) // - .element(0).extracting(it -> it.serializedEvent).isEqualTo(serializedEvent2); - } - - @Test // GH-133 - void returnsOldestIncompletePublicationsFirst() { - - var now = LocalDateTime.now(); - - savePublicationAt(now.withHour(3)); - savePublicationAt(now.withHour(0)); - savePublicationAt(now.withHour(1)); - - assertThat(repository.findIncompletePublications()) - .isSortedAccordingTo(Comparator.comparing(TargetEventPublication::getPublicationDate)); - } - - @Test // GH-251 - void shouldDeleteCompletedEventsBefore() { - - assumeTrue(completionMode == CompletionMode.UPDATE); - - var testEvent1 = new TestEvent("abc"); - var serializedEvent1 = "{\"eventId\":\"abc\"}"; - var testEvent2 = new TestEvent("def"); - var serializedEvent2 = "{\"eventId\":\"def\"}"; - - when(eventSerializer.serialize(testEvent1)).thenReturn(serializedEvent1); - when(eventSerializer.deserialize(serializedEvent1, TestEvent.class)).thenReturn(testEvent1); - when(eventSerializer.serialize(testEvent2)).thenReturn(serializedEvent2); - when(eventSerializer.deserialize(serializedEvent2, TestEvent.class)).thenReturn(testEvent2); - - repository.create(TargetEventPublication.of(testEvent1, TARGET_IDENTIFIER)); - repository.create(TargetEventPublication.of(testEvent2, TARGET_IDENTIFIER)); - - var now = Instant.now(); - - repository.markCompleted(testEvent1, TARGET_IDENTIFIER, now.minusSeconds(30)); - repository.markCompleted(testEvent2, TARGET_IDENTIFIER, now); - repository.deleteCompletedPublicationsBefore(now.minusSeconds(15)); - - assertThat(em.createQuery("select p from JpaEventPublication p", JpaEventPublication.class).getResultList()) - .hasSize(1) // - .element(0).extracting(it -> it.serializedEvent).isEqualTo(serializedEvent2); - } - - @Test // GH-294 - void deletesPublicationsByIdentifier() { - - var first = createPublication(new TestEvent("first")); - var second = createPublication(new TestEvent("second")); - - repository.deletePublications(List.of(first.getIdentifier())); - - assertThat(repository.findIncompletePublications()) - .hasSize(1) - .element(0) - .matches(it -> it.getIdentifier().equals(second.getIdentifier())) - .matches(it -> it.getEvent().equals(second.getEvent())); - } - - @Test // GH-294 - void findsPublicationsOlderThanReference() throws Exception { - - var first = createPublication(new TestEvent("first")); - - Thread.sleep(100); - - var now = Instant.now(); - var second = createPublication(new TestEvent("second")); - - assertThat(repository.findIncompletePublications()) - .extracting(TargetEventPublication::getIdentifier) - .containsExactly(first.getIdentifier(), second.getIdentifier()); - - assertThat(repository.findIncompletePublicationsPublishedBefore(now)) - .hasSize(1) - .element(0).extracting(TargetEventPublication::getIdentifier).isEqualTo(first.getIdentifier()); - } - - @Test // GH-451 - void findsCompletedPublications() { - - var event = new TestEvent("first"); - var publication = createPublication(event); - - repository.markCompleted(publication, Instant.now()); - - if (completionMode == CompletionMode.DELETE) { - - assertThat(repository.findCompletedPublications()).isEmpty(); assertThat(repository.findIncompletePublications()).isEmpty(); + } - } else { + @Test // GH-25 + void shouldTolerateEmptyResult() { - assertThat(repository.findCompletedPublications()) + var testEvent = new TestEvent("id"); + var serializedEvent = "{\"eventId\":\"id\"}"; + + when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); + + assertThat(repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER)).isEmpty(); + } + + @Test // GH-25 + void shouldNotReturnCompletedEvents() { + + TestEvent testEvent = new TestEvent("abc"); + String serializedEvent = "{\"eventId\":\"abc\"}"; + + when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent); + when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent); + + var publication = TargetEventPublication.of(testEvent, TARGET_IDENTIFIER); + + repository.create(publication); + repository.markCompleted(publication, Instant.now()); + + var actual = repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER); + + assertThat(actual).isEmpty(); + } + + @Test // GH-20 + void shouldDeleteCompletedEvents() { + + var testEvent1 = new TestEvent("abc"); + var serializedEvent1 = "{\"eventId\":\"abc\"}"; + var testEvent2 = new TestEvent("def"); + var serializedEvent2 = "{\"eventId\":\"def\"}"; + + when(eventSerializer.serialize(testEvent1)).thenReturn(serializedEvent1); + when(eventSerializer.deserialize(serializedEvent1, TestEvent.class)).thenReturn(testEvent1); + when(eventSerializer.serialize(testEvent2)).thenReturn(serializedEvent2); + when(eventSerializer.deserialize(serializedEvent2, TestEvent.class)).thenReturn(testEvent2); + + repository.create(TargetEventPublication.of(testEvent1, TARGET_IDENTIFIER)); + repository.create(TargetEventPublication.of(testEvent2, TARGET_IDENTIFIER)); + repository.markCompleted(testEvent1, TARGET_IDENTIFIER, Instant.now()); + repository.deleteCompletedPublications(); + + assertThat(em.createQuery("select p from JpaEventPublication p", JpaEventPublication.class).getResultList()) + .hasSize(1) // + .element(0).extracting(it -> it.serializedEvent).isEqualTo(serializedEvent2); + + if (completionMode == CompletionMode.ARCHIVE) { + assertThat(em.createQuery("select p from ArchivedJpaEventPublication p", ArchivedJpaEventPublication.class).getResultList()) + .hasSize(0); + } + } + + @Test // GH-133 + void returnsOldestIncompletePublicationsFirst() { + + var now = LocalDateTime.now(); + + savePublicationAt(now.withHour(3)); + savePublicationAt(now.withHour(0)); + savePublicationAt(now.withHour(1)); + + assertThat(repository.findIncompletePublications()) + .isSortedAccordingTo(Comparator.comparing(TargetEventPublication::getPublicationDate)); + } + + @Test // GH-251 + void shouldDeleteCompletedEventsBefore() { + + assumeFalse(completionMode == CompletionMode.DELETE); + + var testEvent1 = new TestEvent("abc"); + var serializedEvent1 = "{\"eventId\":\"abc\"}"; + var testEvent2 = new TestEvent("def"); + var serializedEvent2 = "{\"eventId\":\"def\"}"; + + when(eventSerializer.serialize(testEvent1)).thenReturn(serializedEvent1); + when(eventSerializer.deserialize(serializedEvent1, TestEvent.class)).thenReturn(testEvent1); + when(eventSerializer.serialize(testEvent2)).thenReturn(serializedEvent2); + when(eventSerializer.deserialize(serializedEvent2, TestEvent.class)).thenReturn(testEvent2); + + repository.create(TargetEventPublication.of(testEvent1, TARGET_IDENTIFIER)); + repository.create(TargetEventPublication.of(testEvent2, TARGET_IDENTIFIER)); + + var now = Instant.now(); + + repository.markCompleted(testEvent1, TARGET_IDENTIFIER, now.minusSeconds(30)); + repository.markCompleted(testEvent2, TARGET_IDENTIFIER, now); + + repository.deleteCompletedPublicationsBefore(now.minusSeconds(15)); + + var entityName = completionMode == CompletionMode.ARCHIVE + ? ArchivedJpaEventPublication.class.getSimpleName() + : JpaEventPublication.class.getSimpleName(); + + var type = completionMode == CompletionMode.ARCHIVE + ? ArchivedJpaEventPublication.class + : JpaEventPublication.class; + + assertThat(em.createQuery("select p from " + entityName + " p", type).getResultList()) + .hasSize(1) // + .element(0).extracting(it -> it.serializedEvent).isEqualTo(serializedEvent2); + + } + + @Test // GH-294 + void deletesPublicationsByIdentifier() { + + var first = createPublication(new TestEvent("first")); + var second = createPublication(new TestEvent("second")); + + repository.deletePublications(List.of(first.getIdentifier())); + + assertThat(repository.findIncompletePublications()) .hasSize(1) .element(0) - .extracting(TargetEventPublication::getEvent) - .isEqualTo(event); + .matches(it -> it.getIdentifier().equals(second.getIdentifier())) + .matches(it -> it.getEvent().equals(second.getEvent())); + } + + @Test // GH-294 + void findsPublicationsOlderThanReference() throws Exception { + + var first = createPublication(new TestEvent("first")); + + Thread.sleep(100); + + var now = Instant.now(); + var second = createPublication(new TestEvent("second")); + + assertThat(repository.findIncompletePublications()) + .extracting(TargetEventPublication::getIdentifier) + .containsExactly(first.getIdentifier(), second.getIdentifier()); + + assertThat(repository.findIncompletePublicationsPublishedBefore(now)) + .hasSize(1) + .element(0).extracting(TargetEventPublication::getIdentifier).isEqualTo(first.getIdentifier()); + } + + @Test // GH-451 + void findsCompletedPublications() { + + var event = new TestEvent("first"); + var publication = createPublication(event); + + repository.markCompleted(publication, Instant.now()); + + if (completionMode == CompletionMode.DELETE) { + + assertThat(repository.findCompletedPublications()).isEmpty(); + assertThat(repository.findIncompletePublications()).isEmpty(); + + } else { + + assertThat(repository.findCompletedPublications()) + .hasSize(1) + .element(0) + .extracting(TargetEventPublication::getEvent) + .isEqualTo(event); + } + } + + @Test // GH 806 + void archivesByEvent() { + + assumeTrue(completionMode == CompletionMode.ARCHIVE); + + var event = new TestEvent("abc"); + var publication = createPublication(event); + + repository.markCompleted(publication, Instant.now()); + + assertThat(repository.findCompletedPublications()) + .hasSize(1); + + assertThat(em.createQuery("select p from JpaEventPublication p", JpaEventPublication.class).getResultList()) + .hasSize(0); + } + + @Test // GH 806 + void archivesById() { + + assumeTrue(completionMode == CompletionMode.ARCHIVE); + + var event = new TestEvent("abc"); + var publication = createPublication(event); + + repository.markCompleted(publication.getIdentifier(), Instant.now()); + + assertThat(repository.findCompletedPublications()) + .hasSize(1); + + assertThat(em.createQuery("select p from JpaEventPublication p", JpaEventPublication.class).getResultList()) + .hasSize(0); + } + + private TargetEventPublication createPublication(Object event) { + + var token = event.toString(); + + doReturn(token).when(eventSerializer).serialize(event); + doReturn(event).when(eventSerializer).deserialize(token, event.getClass()); + + return repository.create(TargetEventPublication.of(event, TARGET_IDENTIFIER)); + } + + private void savePublicationAt(LocalDateTime date) { + em.persist(new JpaEventPublication(UUID.randomUUID(), date.toInstant(ZoneOffset.UTC), "", "", Object.class)); + } + + private record TestEvent(String eventId) { } } @Nested - @ContextConfiguration(classes = TestConfig.class) + class WithUpdateCompletionTests extends TestBase {} + + @Nested @TestPropertySource(properties = CompletionMode.PROPERTY + "=DELETE") - static class WithDeleteCompletionTests extends JpaEventPublicationRepositoryIntegrationTests { + class WithDeleteCompletionTests extends TestBase {} - } - - private TargetEventPublication createPublication(Object event) { - - var token = event.toString(); - - doReturn(token).when(eventSerializer).serialize(event); - doReturn(event).when(eventSerializer).deserialize(token, event.getClass()); - - return repository.create(TargetEventPublication.of(event, TARGET_IDENTIFIER)); - } - - private void savePublicationAt(LocalDateTime date) { - em.persist(new JpaEventPublication(UUID.randomUUID(), date.toInstant(ZoneOffset.UTC), "", "", Object.class)); - } - - private record TestEvent(String eventId) {} + @Nested + @TestPropertySource(properties = CompletionMode.PROPERTY + "=ARCHIVE") + class WithArchiveCompletionTests extends TestBase {} }