From ae4a36f848e16af01c5822c14c73ce29e3080e76 Mon Sep 17 00:00:00 2001 From: rkiselev <67838927+rkiselev@users.noreply.github.com> Date: Wed, 19 Aug 2020 20:54:15 +0300 Subject: [PATCH] 481 accept date and time as string in ISO 8601 format for ChronoLocalDateTime 2 (#552) #481 added String parameter instead of a ChronoLocalDateTime --- .../en_GB/chronoLocalDateTimeAssertions.kt | 100 +++++++++++ .../en_GB/ChronoLocalDateTimeAssertionSpec.kt | 163 ++++++++++++++++-- .../ChronoLocalDateTimeAssertionsImpl.kt | 2 - 3 files changed, 248 insertions(+), 17 deletions(-) diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/chronoLocalDateTimeAssertions.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/chronoLocalDateTimeAssertions.kt index 9114a80310..85ae34e65c 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/chronoLocalDateTimeAssertions.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/main/kotlin/ch/tutteli/atrium/api/fluent/en_GB/chronoLocalDateTimeAssertions.kt @@ -74,3 +74,103 @@ fun > Expect.isAfterOrEqual( fun > Expect.isEqual( expected: ChronoLocalDateTime<*> ): Expect = _logicAppend { isEqual(expected) } + +/** + * Expects that the subject of the assertion (a [ChronoLocalDateTime]) + * is before the [expected] [LocalDateTime] given as [String] in (modified) ISO 8601 format. + * The string will be converted to a LocalDateTime according to ISO 8601 but with a slight deviation. + * The alternative notation (e.g. 20200401120001 instead of 2020-04-01T12:00:01) is not supported and we accept a + * date without time in which case 00:00:00 is used. Following the supported formats from the longest to the shortest: + * yyyy-mm-ddThh:mm:ss.sss (up to 9 digits for nanoseconds) + * yyyy-mm-ddThh:mm:ss + * yyyy-mm-ddThh:mm + * yyyy-mm-dd + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +fun > Expect.isBefore( + expected: String +): Expect = _logicAppend { isBefore(expected) } + +/** + * Expects that the subject of the assertion (a [ChronoLocalDateTime]) + * is before the [expected] [LocalDateTime] given as [String] in (modified) ISO 8601 format. + * The string will be converted to a LocalDateTime according to ISO 8601 but with a slight deviation. + * The alternative notation (e.g. 20200401120001 instead of 2020-04-01T12:00:01) is not supported and we accept a + * date without time in which case 00:00:00 is used. Following the supported formats from the longest to the shortest: + * yyyy-mm-ddThh:mm:ss.sss (up to 9 digits for nanoseconds) + * yyyy-mm-ddThh:mm:ss + * yyyy-mm-ddThh:mm + * yyyy-mm-dd + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +fun > Expect.isBeforeOrEqual( + expected: String +): Expect = _logicAppend { isBeforeOrEqual(expected) } + +/** + * Expects that the subject of the assertion (a [ChronoLocalDateTime]) + * is before the [expected] [LocalDateTime] given as [String] in (modified) ISO 8601 format. + * The string will be converted to a LocalDateTime according to ISO 8601 but with a slight deviation. + * The alternative notation (e.g. 20200401120001 instead of 2020-04-01T12:00:01) is not supported and we accept a date + * without time in which case 00:00:00 is used. Following the supported formats from the longest to the shortest: + * yyyy-mm-ddThh:mm:ss.sss (up to 9 digits for nanoseconds) + * yyyy-mm-ddThh:mm:ss + * yyyy-mm-ddThh:mm + * yyyy-mm-dd + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +fun > Expect.isAfter( + expected: String +): Expect = _logicAppend { isAfter(expected) } + +/** + * Expects that the subject of the assertion (a [ChronoLocalDateTime]) + * is before the [expected] [LocalDateTime] given as [String] in (modified) ISO 8601 format. + * The string will be converted to a LocalDateTime according to ISO 8601 but with a slight deviation. + * The alternative notation (e.g. 20200401120001 instead of 2020-04-01T12:00:01) is not supported and we accept a date + * without time in which case 00:00:00 is used. Following the supported formats from the longest to the shortest: + * yyyy-mm-ddThh:mm:ss.sss (up to 9 digits for nanoseconds) + * yyyy-mm-ddThh:mm:ss + * yyyy-mm-ddThh:mm + * yyyy-mm-dd + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +fun > Expect.isAfterOrEqual( + expected: String +): Expect = _logicAppend { isAfterOrEqual(expected) } + +/** + * Expects that the subject of the assertion (a [ChronoLocalDateTime]) + * is before the [expected] [LocalDateTime] given as [String] in (modified) ISO 8601 format. + * The string will be converted to a LocalDateTime according to ISO 8601 but with a slight deviation. + * The alternative notation (e.g. 20200401120001 instead of 2020-04-01T12:00:01) is not supported and we accept a date + * without time in which case 00:00:00 is used. Following the supported formats from the longest to the shortest: + * yyyy-mm-ddThh:mm:ss.sss (up to 9 digits for nanoseconds) + * yyyy-mm-ddThh:mm:ss + * yyyy-mm-ddThh:mm + * yyyy-mm-dd + * + * @return An [Expect] for the current subject of the assertion. + * @throws AssertionError Might throw an [AssertionError] if the assertion made is not correct. + * + * @since 0.13.0 + */ +fun > Expect.isEqual( + expected: String +): Expect = _logicAppend { isEqual(expected) } diff --git a/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/ChronoLocalDateTimeAssertionSpec.kt b/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/ChronoLocalDateTimeAssertionSpec.kt index 15598d539b..ce0f188e92 100644 --- a/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/ChronoLocalDateTimeAssertionSpec.kt +++ b/apis/fluent-en_GB/atrium-api-fluent-en_GB-jvm/src/test/kotlin/ch/tutteli/atrium/api/fluent/en_GB/ChronoLocalDateTimeAssertionSpec.kt @@ -1,34 +1,146 @@ package ch.tutteli.atrium.api.fluent.en_GB +import ch.tutteli.atrium.api.verbs.internal.expect import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.reporting.AtriumError import ch.tutteli.atrium.specs.fun1 import ch.tutteli.atrium.specs.notImplemented import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.specification.describe import java.time.LocalDate import java.time.LocalDateTime import java.time.chrono.ChronoLocalDate import java.time.chrono.ChronoLocalDateTime -import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatter.ISO_DATE_TIME +import java.time.format.DateTimeParseException class ChronoLocalDateTimeAssertionSpec : Spek({ include(ChronoLocalDateTimeSpec) include(StringSpec) + listOf( + "20200101010101111", + "20200101010101", + "202001010101", + "2020010101", + "2020-1-01T01:01", + "2020-01-1T01:01", + "20-01-01T01:01" , + "2020-01-01T1:01", + "2020-01-01T01:1", + "2020-01-01T01:01:1", + "2020-01-01t01:01:01" + ).forEach{ value -> + val now = expect(LocalDateTime.now()) + describe("wrong string format") { + it("isBefore throws an DateTimeParseException") { + expect { + now.isBefore(value) + }.toThrow { messageContains("could not be parsed") } + } + it("isBeforeOrEqual throws an DateTimeParseException") { + expect { + now.isBeforeOrEqual(value) + }.toThrow { messageContains("could not be parsed") } + } + it("isAfter throws an DateTimeParseException") { + expect { + now.isAfter(value) + }.toThrow { messageContains("could not be parsed") } + } + it("isAfterOrEqual throws an DateTimeParseException") { + expect { + now.isAfterOrEqual(value) + }.toThrow { messageContains("could not be parsed") } + } + it("isEqual throws an DateTimeParseException") { + expect { + now.isEqual(value) + }.toThrow { messageContains("could not be parsed") } + } + } + } + + listOf( + "2020-01-01T01:01:01", + "2020-01-01T01:01", + "2020-01-01" + ).forEach{value -> + val nowLocalDateTime = LocalDateTime.now() + val now = expect(nowLocalDateTime) + val before = LocalDateTime.now() + describe("allowed shortcuts") { + it("isBefore throws an AtriumError") { + expect { + now.isBefore(value) + }.toThrow { messageContains("is before: $value") } + } + it("isBefore doesn't throw any Error") { + expect { + now.isBefore(before.format(ISO_DATE_TIME)) + }.notToThrow() + } + it("isBeforeOrEqual doesn't throw any Error") { + expect { + now.isBeforeOrEqual(nowLocalDateTime.format(ISO_DATE_TIME)) + }.notToThrow() + } + it("isBeforeOrEqual throws an AtriumError") { + expect { + now.isBeforeOrEqual(value) + }.toThrow { messageContains("is before or equal: $value") } + } + it("isAfter doesn't throw any Error") { + expect { + now.isAfter(value) + }.notToThrow() + } + it("isAfter throws an AtriumError") { + expect { + now.isAfter(before.format(ISO_DATE_TIME)) + }.toThrow { messageContains("is after: ${before.format(ISO_DATE_TIME)}") } + } + it("isAfterOrEqual doesn't throw any Error") { + expect { + now.isAfterOrEqual(nowLocalDateTime.format(ISO_DATE_TIME)) + }.notToThrow() + } + it("isAfterOrEqual doesn't throw any Error") { + expect { + now.isAfterOrEqual(value) + }.notToThrow() + } + it("isAfterOrEqual throws an AtriumError") { + expect { + now.isAfterOrEqual(before.format(ISO_DATE_TIME)) + }.toThrow { messageContains("is after or equal: ${before.format(ISO_DATE_TIME)}") } + } + it("isEqual throws an AtriumError") { + expect { + now.isEqual(value) + }.toThrow { messageContains("is equal to: $value") } + } + it("isEqual doesn't throw any error") { + expect { + now.isEqual(nowLocalDateTime.format(ISO_DATE_TIME)) + }.notToThrow() + } + } + } }) { object ChronoLocalDateTimeSpec : ch.tutteli.atrium.specs.integration.ChronoLocalDateTimeAssertionSpec( - fun1(Expect>::isBefore), - fun1(Expect>::isBeforeOrEqual), - fun1(Expect>::isAfter), - fun1(Expect>::isAfterOrEqual), - fun1(Expect>::isEqual) + fun1, ChronoLocalDateTime<*>>(Expect>::isBefore), + fun1, ChronoLocalDateTime<*>>(Expect>::isBeforeOrEqual), + fun1, ChronoLocalDateTime<*>>(Expect>::isAfter), + fun1, ChronoLocalDateTime<*>>(Expect>::isAfterOrEqual), + fun1, ChronoLocalDateTime<*>>(Expect>::isEqual) ) object StringSpec : ch.tutteli.atrium.specs.integration.ChronoLocalDateTimeAssertionSpec( fun1(Companion::isBefore), - //TODO #481 introduce more companions - fun1(Expect>::isBeforeOrEqual), - fun1(Expect>::isAfter), - fun1(Expect>::isAfterOrEqual), - fun1(Expect>::isEqual) + fun1(Companion::isBeforeOrEqual), + fun1(Companion::isAfter), + fun1(Companion::isAfterOrEqual), + fun1(Companion::isEqual) ) companion object { @@ -36,11 +148,32 @@ class ChronoLocalDateTimeAssertionSpec : Spek({ expect: Expect>, expected: ChronoLocalDateTime<*> ): Expect> = - //TODO #481 turn into string in ISO format - //expect.isBefore(expected.format(DateTimeFormatter.ISO_DATE_TIME)) - expect.isBefore(expected) - } + expect.isBefore(expected.format(ISO_DATE_TIME)) + + fun isBeforeOrEqual( + expect: Expect>, + expected: ChronoLocalDateTime<*> + ): Expect> = + expect.isBeforeOrEqual(expected.format(ISO_DATE_TIME)) + + fun isAfter( + expect: Expect>, + expected: ChronoLocalDateTime<*> + ): Expect> = + expect.isAfter(expected.format(ISO_DATE_TIME)) + + fun isAfterOrEqual( + expect: Expect>, + expected: ChronoLocalDateTime<*> + ): Expect> = + expect.isAfterOrEqual(expected.format(ISO_DATE_TIME)) + fun isEqual( + expect: Expect>, + expected: ChronoLocalDateTime<*> + ): Expect> = + expect.isEqual(expected.format(ISO_DATE_TIME)) + } @Suppress("unused", "UNUSED_VALUE") private fun ambiguityTest() { diff --git a/misc/deprecated/domain/robstoll/atrium-domain-robstoll-jvm/src/main/kotlin/ch/tutteli/atrium/domain/robstoll/creating/ChronoLocalDateTimeAssertionsImpl.kt b/misc/deprecated/domain/robstoll/atrium-domain-robstoll-jvm/src/main/kotlin/ch/tutteli/atrium/domain/robstoll/creating/ChronoLocalDateTimeAssertionsImpl.kt index cf6dbdf207..32a58acc85 100644 --- a/misc/deprecated/domain/robstoll/atrium-domain-robstoll-jvm/src/main/kotlin/ch/tutteli/atrium/domain/robstoll/creating/ChronoLocalDateTimeAssertionsImpl.kt +++ b/misc/deprecated/domain/robstoll/atrium-domain-robstoll-jvm/src/main/kotlin/ch/tutteli/atrium/domain/robstoll/creating/ChronoLocalDateTimeAssertionsImpl.kt @@ -11,8 +11,6 @@ import ch.tutteli.atrium.assertions.Assertion import ch.tutteli.atrium.creating.Expect import ch.tutteli.atrium.domain.creating.ChronoLocalDateTimeAssertions import ch.tutteli.atrium.domain.robstoll.lib.creating.* -import java.time.LocalDate -import java.time.LocalDateTime import java.time.chrono.ChronoLocalDate import java.time.chrono.ChronoLocalDateTime