Repository: urlaubsverwaltung/urlaubsverwaltung
Pull request #4756 (2024-06-28 08:25:22)

Improve ical tests with text blocks
The DTSTAMP:* is replaced by DTSTAMP:<removedByConversionMethod> - because this will be generated new for every creation

refs #4755 

<!--

Thanks for contributing to the Urlaubsverwaltung.
Please review the following notes before submitting you pull request.

Please look for other issues or pull requests which already work on this topic. Is somebody already on it? Do you need to synchronize?

# Security Vulnerabilities

🛑 STOP! 🛑 If your contribution fixes a security vulnerability, please do not submit it.
Instead, please open a report of a security vulnerability via
[Report a security vulnerability](https://github.com/urlaubsverwaltung/urlaubsverwaltung/security/advisories/new)


# Describing Your Changes

If, having reviewed the notes above, you're ready to submit your pull request, please
provide a brief description of the proposed changes.

If they:
 🐞 fix a bug, please describe the broken behaviour and how the changes fix it.
    Please label with 'type: bug' and 'status: new'
    
 🎁 make an enhancement, please describe the new functionality and why you believe it's useful.
    Please label with 'type: enhancement' and 'status: new'
 
If your pull request relates to any existing issues,
please reference them by using the issue number prefixed with #.

-->



Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java b/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
index 0ff3122cb..d1daedb24 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
@@ -24,6 +24,7 @@ import org.synyx.urlaubsverwaltung.person.Person;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URI;
+import java.time.LocalDate;
 import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Optional;
@@ -95,12 +96,7 @@ public class ICalService {
 
         final VEvent event;
         if (absence.isAllDay()) {
-            if (isSameDay(startDateTime, endDateTime)) {
-                event = new VEvent(startDateTime, absence.getEventSubject());
-            } else {
-                event = new VEvent(startDateTime, endDateTime, absence.getEventSubject());
-            }
-            event.add(new XProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"));
+            event = generateAllDayEvent(absence.getEventSubject(), startDateTime, endDateTime);
         } else {
             event = new VEvent(startDateTime, endDateTime, absence.getEventSubject());
         }
@@ -123,6 +119,20 @@ public class ICalService {
         return Optional.of(event);
     }
 
+    private VEvent generateAllDayEvent(String eventSubject, ZonedDateTime startDateTime, ZonedDateTime endDateTime) {
+        final LocalDate startDate = startDateTime.toLocalDate();
+        final LocalDate endDate = endDateTime.toLocalDate();
+
+        final VEvent event;
+        if (isSameDay(startDate, endDate)) {
+            event = new VEvent(startDate, eventSubject);
+        } else {
+            event = new VEvent(startDate, endDate, eventSubject);
+        }
+        event.add(new XProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"));
+        return event;
+    }
+
     private Attendee generateAttendee(Absence absence) {
         final Attendee attendee = new Attendee(URI.create("mailto:" + absence.getPerson().getEmail()));
         attendee.add(REQ_PARTICIPANT);
@@ -131,8 +141,8 @@ public class ICalService {
         return attendee;
     }
 
-    private boolean isSameDay(ZonedDateTime startDateTime, ZonedDateTime endDate) {
-        return startDateTime.toLocalDate().isEqual(endDate.toLocalDate().minusDays(1));
+    private boolean isSameDay(LocalDate startDate, LocalDate endDate) {
+        return startDate.isEqual(endDate.minusDays(1));
     }
 
     private String generateUid(Absence absence) {


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java b/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
index 053995688..e6ba4dd91 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
@@ -43,14 +43,16 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(), null);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -63,20 +65,26 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(fullDayAbsence), person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE")
-            .contains("DTSTART;TZID=Etc/UTC:20190326T000000")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;VALUE=DATE:20190326
+                SUMMARY:Marlene Muster abwesend
+                X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+                UID:F5C924EEB91550EBDD74CDA428649C8B
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -89,20 +97,26 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(morningAbsence), person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("DTSTART;TZID=Etc/UTC:20190426T080000")
-            .contains("DTEND;TZID=Etc/UTC:20190426T120000")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;TZID=Etc/UTC:20190426T080000
+                DTEND;TZID=Etc/UTC:20190426T120000
+                SUMMARY:Marlene Muster abwesend
+                UID:45BCD3F64AD76CEF9040DF93047C41B3
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -116,21 +130,27 @@ class ICalServiceTest {
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(manyFullDayAbsence), person);
 
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE")
-            .contains("DTSTART;TZID=Etc/UTC:20190326T000000")
-            .contains("DTEND;TZID=Etc/UTC:20190402T000000")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;VALUE=DATE:20190326
+                DTEND;VALUE=DATE:20190402
+                SUMMARY:Marlene Muster abwesend
+                X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+                UID:D4C6D48B54BAED09F894FBDE0151381F
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -143,20 +163,26 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(noonAbsence), person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T120000")
-            .contains("DTEND;TZID=Etc/UTC:20190526T160000")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
+                SUMMARY:Marlene Muster abwesend
+                UID:BB2267885BD8DF263E88D3062853E8A7
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -172,21 +198,26 @@ class ICalServiceTest {
         final ICalService sut = new ICalService(calendarProperties);
         final ByteArrayResource calendar = sut.getCalendar("Abwesenheitskalender", List.of(noonAbsence), person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("X-WR-CALNAME:Abwesenheitskalender")
-            .contains("REFRESH-INTERVAL:P1D")
-
-            .contains("UID:BB2267885BD8DF263E88D3062853E8A7")
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T120000")
-            .contains("DTEND;TZID=Etc/UTC:20190526T160000")
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .contains("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                X-WR-CALNAME:Abwesenheitskalender
+                REFRESH-INTERVAL:P1D
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
+                SUMMARY:Marlene Muster abwesend
+                UID:BB2267885BD8DF263E88D3062853E8A7
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -203,22 +234,26 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getSingleAppointment(noonAbsence, CANCELLED, person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-            .contains("METHOD:CANCEL")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T120000")
-            .contains("DTEND;TZID=Etc/UTC:20190526T160000")
-            .contains("UID:BB2267885BD8DF263E88D3062853E8A7")
-            .contains("SEQUENCE:1")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .contains("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                METHOD:CANCEL
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
+                SUMMARY:Marlene Muster abwesend
+                UID:BB2267885BD8DF263E88D3062853E8A7
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                SEQUENCE:1
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -234,20 +269,24 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getSingleAppointment(noonAbsence, PUBLISHED, person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T120000")
-            .contains("DTEND;TZID=Etc/UTC:20190526T160000")
-            .contains("UID:BB2267885BD8DF263E88D3062853E8A7")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .contains("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org")
-
-            .doesNotContain("TRANS:TRANSPARENT");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
+                SUMMARY:Marlene Muster abwesend
+                UID:BB2267885BD8DF263E88D3062853E8A7
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -267,19 +306,25 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getSingleAppointment(absence, PUBLISHED, recipient);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T000000")
-            .contains("TRANSP:TRANSPARENT")
-            .contains("UID:22D8DC26F4271C049ED5601345B58D9C")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .contains("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;VALUE=DATE:20190526
+                SUMMARY:Marlene Muster abwesend
+                X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+                UID:22D8DC26F4271C049ED5601345B58D9C
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                TRANSP:TRANSPARENT
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -299,19 +344,23 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getSingleAppointment(absence, PUBLISHED, recipient);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-
-            .contains("SUMMARY:Marlene Muster abwesend")
-            .contains("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T000000")
-            .contains("TRANSP:TRANSPARENT")
-            .contains("UID:22D8DC26F4271C049ED5601345B58D9C")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .doesNotContain("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:null");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;VALUE=DATE:20190526
+                SUMMARY:Marlene Muster abwesend
+                X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+                UID:22D8DC26F4271C049ED5601345B58D9C
+                TRANSP:TRANSPARENT
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     @Test
@@ -327,19 +376,25 @@ class ICalServiceTest {
 
         final ByteArrayResource calendar = sut.getSingleAppointment(holidayReplacement, PUBLISHED, person);
         assertThat(convertCalendar(calendar))
-            .contains("VERSION:2.0")
-            .contains("CALSCALE:GREGORIAN")
-            .contains("PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE")
-            .contains("X-MICROSOFT-CALSCALE:GREGORIAN")
-
-            .contains("SUMMARY:Vertretung für Marlene Muster")
-            .contains("X-MICROSOFT-CDO-ALLDAYEVENT:TRUE")
-            .contains("DTSTART;TZID=Etc/UTC:20190526T000000")
-            .contains("TRANSP:TRANSPARENT")
-            .contains("UID:D2A4772AEB3FD20D5F6997FCD8F28719")
-
-            .contains("ORGANIZER:mailto:no-reply@example.org")
-            .contains("ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org");
+            .isEqualToIgnoringNewLines("""
+                BEGIN:VCALENDAR
+                VERSION:2.0
+                PRODID:-//Urlaubsverwaltung//iCal4j 1.0//DE
+                CALSCALE:GREGORIAN
+                X-MICROSOFT-CALSCALE:GREGORIAN
+                BEGIN:VEVENT
+                DTSTAMP:<removedByConversionMethod>
+                DTSTART;VALUE=DATE:20190526
+                SUMMARY:Vertretung für Marlene Muster
+                X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
+                UID:D2A4772AEB3FD20D5F6997FCD8F28719
+                ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
+                \s
+                TRANSP:TRANSPARENT
+                ORGANIZER:mailto:no-reply@example.org
+                END:VEVENT
+                END:VCALENDAR
+                """);
     }
 
     private Absence absence(Person person, LocalDate start, LocalDate end, DayLength length) {
@@ -359,7 +414,15 @@ class ICalServiceTest {
         return new Absence(person, period, timeConfig, absenceType);
     }
 
+    /**
+     * Converts the calendar byte array to a string and removes the "DTSTAMP" value,
+     * because this is different for every creation of a calendar
+     *
+     * @param calendar as byte array
+     * @return calendar as string
+     */
     private String convertCalendar(ByteArrayResource calendar) {
-        return new String(calendar.getByteArray(), UTF_8);
+        return new String(calendar.getByteArray(), UTF_8)
+            .replaceAll("(?m)^DTSTAMP.*", "DTSTAMP:<removedByConversionMethod>");
     }
 }