Skip to content
This repository was archived by the owner on Sep 22, 2022. It is now read-only.

Commit efaca7f

Browse files
authored
Feature/date matchers regexp support (#57)
* add iso date helpers
1 parent ce62a4a commit efaca7f

File tree

12 files changed

+220
-56
lines changed

12 files changed

+220
-56
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ buildscript {
44
ext.exposed_version = '0.36.2'
55
ext.restAssured_version = '4.4.0'
66
ext.klogging_version = '2.0.11'
7-
ext.libVersion = '6.0.0-beta-6'
7+
ext.libVersion = '6.0.0-beta-7'
88
repositories {
99
mavenCentral()
1010
}

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/Content.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.github.adven27.concordion.extensions.exam.core
33
import io.github.adven27.concordion.extensions.exam.core.html.Html
44
import io.github.adven27.concordion.extensions.exam.core.utils.JsonPrettyPrinter
55
import io.github.adven27.concordion.extensions.exam.core.utils.PlaceholderSupportDiffEvaluator
6+
import mu.KLogging
67
import net.javacrumbs.jsonunit.JsonAssert
78
import net.javacrumbs.jsonunit.core.Configuration
89
import net.javacrumbs.jsonunit.core.internal.JsonUtils
@@ -119,6 +120,7 @@ interface ContentVerifier {
119120
}
120121
}
121122
} catch (e: AssertionError) {
123+
logger.warn("Content verification error", e)
122124
Result.failure(Fail(e.message ?: "$e", expected, actual))
123125
}
124126

@@ -135,6 +137,8 @@ interface ContentVerifier {
135137

136138
class Exception(actual: String, expected: String, throwable: Throwable) :
137139
RuntimeException("Failed to verify content:\n$actual\nExpected:\n$expected", throwable)
140+
141+
companion object : KLogging()
138142
}
139143

140144
open class XmlVerifier(private val nodeMatcher: NodeMatcher) : ContentVerifier.Default("xml") {

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/ExamExtension.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,11 @@ class ExamExtension constructor(private vararg var plugins: ExamPlugin) : Concor
192192
@JvmField
193193
val DEFAULT_JSON_UNIT_CFG: Configuration = `when`(IGNORING_ARRAY_ORDER).let { cfg ->
194194
MATCHERS.map { cfg to it }
195-
.reduce { acc, cur -> acc.first.withMatcher(cur.second.key, cur.second.value) to cur.second }.first
195+
.reduce { acc, cur ->
196+
acc.first
197+
.withMatcher(acc.second.key, acc.second.value)
198+
.withMatcher(cur.second.key, cur.second.value) to cur.second
199+
}.first
196200
}
197201

198202
val CONTENT_TYPE_CONFIGS: MutableMap<String, ContentTypeConfig> = mutableMapOf(

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/Utils.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package io.github.adven27.concordion.extensions.exam.core
44

55
import com.github.jknack.handlebars.internal.text.StringEscapeUtils
6+
import io.github.adven27.concordion.extensions.exam.core.handlebars.matchers.ISO_LOCAL_DATETIME_FORMAT
67
import io.github.adven27.concordion.extensions.exam.core.html.Html
78
import io.github.adven27.concordion.extensions.exam.core.html.codeHighlight
89
import io.github.adven27.concordion.extensions.exam.core.html.span
@@ -18,8 +19,10 @@ import java.time.Period
1819
import java.time.ZoneId
1920
import java.time.ZonedDateTime
2021
import java.time.format.DateTimeFormatter
22+
import java.time.format.DateTimeFormatterBuilder
2123
import java.time.format.DateTimeParseException
2224
import java.time.format.ResolverStyle
25+
import java.time.temporal.ChronoField.MICRO_OF_SECOND
2326
import java.util.Date
2427
import java.util.Random
2528

@@ -81,7 +84,13 @@ fun String.parseLocalDate(format: String? = null): LocalDate = LocalDate.parse(
8184
format?.toDatePattern() ?: DEFAULT_LOCAL_DATE_FORMAT
8285
)
8386

84-
fun String.toDatePattern(): DateTimeFormatter = DateTimeFormatter.ofPattern(this)
87+
@Suppress("MagicNumber")
88+
fun String.toDatePattern(): DateTimeFormatter = if (this == "ISO_LOCAL") {
89+
DateTimeFormatterBuilder()
90+
.appendPattern(ISO_LOCAL_DATETIME_FORMAT)
91+
.appendFraction(MICRO_OF_SECOND, 0, 9, true)
92+
.toFormatter()
93+
} else DateTimeFormatter.ofPattern(this)
8594

8695
fun String.fileExt() = substring(lastIndexOf('.') + 1).lowercase()
8796

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/commands/UtilCommands.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import io.github.adven27.concordion.extensions.exam.core.html.Html
77
import io.github.adven27.concordion.extensions.exam.core.html.html
88
import io.github.adven27.concordion.extensions.exam.core.readFile
99
import io.github.adven27.concordion.extensions.exam.core.resolve
10+
import io.github.adven27.concordion.extensions.exam.core.resolveNoType
1011
import io.github.adven27.concordion.extensions.exam.core.resolveToObj
11-
import io.github.adven27.concordion.extensions.exam.core.resolveXml
1212
import io.github.adven27.concordion.extensions.exam.core.vars
1313
import io.restassured.RestAssured
1414
import nu.xom.Element
@@ -40,8 +40,8 @@ open class SetVarCommand(
4040
}
4141
val value = when {
4242
valueAttr != null -> eval.resolveToObj(valueAttr)
43-
valueFrom != null -> eval.resolveXml(valueFrom.readFile())
44-
else -> eval.resolveXml(el.text().trimIndent()).apply {
43+
valueFrom != null -> eval.resolveNoType(valueFrom.readFile())
44+
else -> eval.resolveNoType(el.text().trimIndent()).apply {
4545
el.text(this)
4646
el.el.appendNonBreakingSpaceIfBlank()
4747
}

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/handlebars/date/DateHelpers.kt

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package io.github.adven27.concordion.extensions.exam.core.handlebars.date
22

33
import com.github.jknack.handlebars.Options
44
import com.github.jknack.handlebars.internal.lang3.LocaleUtils
5-
import com.github.jknack.handlebars.internal.lang3.Validate.isInstanceOf
5+
import com.github.jknack.handlebars.internal.lang3.Validate
66
import io.github.adven27.concordion.extensions.exam.core.handlebars.ExamHelper
77
import io.github.adven27.concordion.extensions.exam.core.handlebars.HB_RESULT
88
import io.github.adven27.concordion.extensions.exam.core.minus
@@ -31,45 +31,6 @@ enum class DateHelpers(
3131
override val expected: Any? = "",
3232
override val options: Map<String, String> = emptyMap()
3333
) : ExamHelper {
34-
weeksAgo(
35-
example = "{{weeksAgo 2}}",
36-
expected = now().minusWeeks(2).atStartOfDay().toDate()
37-
) {
38-
override fun invoke(context: Any?, options: Options) =
39-
now().minusWeeks(context?.toString()?.toLong() ?: 1).atStartOfDay().toDate()
40-
},
41-
daysAgo(
42-
example = "{{daysAgo 2}}",
43-
expected = now().minusDays(2).atStartOfDay().toDate()
44-
) {
45-
override fun invoke(context: Any?, options: Options) =
46-
now().minusDays(context?.toString()?.toLong() ?: 1).atStartOfDay().toDate()
47-
},
48-
dateFormat(
49-
example = """{{dateFormat date "yyyy-MM-dd'T'HH:mm O" tz="GMT+3" minus="1 y, 2 months, d 3" plus="4 h, 5 min, 6 s"}}""",
50-
context = mapOf("date" to "2000-01-02T10:20+03:00".parseDate()),
51-
expected = "1998-10-30T14:25 GMT+3",
52-
options = mapOf(TZ to "\"GMT+3\"", PLUS to "\"1 day\"", MINUS to "\"5 hours\"")
53-
) {
54-
override fun invoke(context: Any?, options: Options): Any? {
55-
isInstanceOf(
56-
Date::class.java,
57-
context,
58-
"Wrong context for helper '%s': '%s', expected instance of Date. Example: %s",
59-
options.fn.text(),
60-
context,
61-
example
62-
)
63-
return dateFormat(
64-
context as Date,
65-
options.param(0, DEFAULT_FORMAT),
66-
options.param(1, Locale.getDefault().toString()),
67-
options.hash(PLUS, ""),
68-
options.hash(MINUS, ""),
69-
options.hash(TZ)
70-
)
71-
}
72-
},
7334
now(
7435
example = """{{now "yyyy-MM-dd'T'HH:mm Z" tz="GMT+3" minus="1 y, 2 months, d 3" plus="4 h, 5 min, 6 s"}}""",
7536
expected = ZonedDateTime.now("GMT+3".timeZoneId())
@@ -128,6 +89,45 @@ enum class DateHelpers(
12889
} else {
12990
context as Date
13091
}
92+
},
93+
weeksAgo(
94+
example = "{{weeksAgo 2}}",
95+
expected = now().minusWeeks(2).atStartOfDay().toDate()
96+
) {
97+
override fun invoke(context: Any?, options: Options) =
98+
now().minusWeeks(context?.toString()?.toLongOrNull() ?: 1).atStartOfDay().toDate()
99+
},
100+
daysAgo(
101+
example = "{{daysAgo 2}}",
102+
expected = now().minusDays(2).atStartOfDay().toDate()
103+
) {
104+
override fun invoke(context: Any?, options: Options) =
105+
now().minusDays(context?.toString()?.toLongOrNull() ?: 1).atStartOfDay().toDate()
106+
},
107+
dateFormat(
108+
example = """{{dateFormat date "yyyy-MM-dd'T'HH:mm O" tz="GMT+3" minus="1 y, 2 months, d 3" plus="4 h, 5 min, 6 s"}}""",
109+
context = mapOf("date" to "2000-01-02T10:20+03:00".parseDate()),
110+
expected = "1998-10-30T14:25 GMT+3",
111+
options = mapOf(TZ to "\"GMT+3\"", PLUS to "\"1 day\"", MINUS to "\"5 hours\"")
112+
) {
113+
override fun invoke(context: Any?, options: Options): Any? {
114+
Validate.isInstanceOf(
115+
Date::class.java,
116+
context,
117+
"Wrong context for helper '%s': '%s', expected instance of Date. Example: %s",
118+
options.fn.text(),
119+
context,
120+
example
121+
)
122+
return dateFormat(
123+
context as Date,
124+
options.param(0, DEFAULT_FORMAT),
125+
options.param(1, Locale.getDefault().toString()),
126+
options.hash(PLUS, ""),
127+
options.hash(MINUS, ""),
128+
options.hash(TZ)
129+
)
130+
}
131131
};
132132

133133
@Suppress("LongParameterList")

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/handlebars/matchers/MatcherHelpers.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import java.util.regex.Pattern
1010

1111
const val PLACEHOLDER_TYPE = "placeholder_type"
1212
const val DB_ACTUAL = "db_actual"
13-
const val ISO_LOCAL_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS][.SSSSSS][.SSS]"
13+
const val ISO_LOCAL_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
1414
const val ISO_LOCAL_DATE_FORMAT = "yyyy-MM-dd"
1515

1616
@Suppress("EnumNaming")
@@ -20,7 +20,6 @@ enum class MatcherHelpers(
2020
override val expected: Any? = "",
2121
override val options: Map<String, String> = emptyMap()
2222
) : ExamHelper {
23-
2423
string(
2524
example = "{{string}}",
2625
context = mapOf(PLACEHOLDER_TYPE to "json"),
@@ -92,10 +91,10 @@ enum class MatcherHelpers(
9291
isoLocalDateTime(
9392
example = "{{isoLocalDateTime}}",
9493
context = mapOf(PLACEHOLDER_TYPE to "json"),
95-
expected = "\${json-unit.matches:formattedAs}$ISO_LOCAL_DATETIME_FORMAT"
94+
expected = "\${json-unit.matches:formattedAs}ISO_LOCAL"
9695
) {
9796
override fun invoke(context: Any?, options: Options): Any =
98-
"\${${placeholderType(options.context)}-unit.matches:formattedAs}$ISO_LOCAL_DATETIME_FORMAT"
97+
"\${${placeholderType(options.context)}-unit.matches:formattedAs}ISO_LOCAL"
9998
},
10099
formattedAndWithinNow(
101100
example = "{{formattedAndWithinNow \"yyyy-MM-dd'T'hh:mm:ss\" \"5s\"}}",
@@ -118,10 +117,10 @@ enum class MatcherHelpers(
118117
isoLocalDateTimeAndWithinNow(
119118
example = "{{isoLocalDateTimeAndWithinNow \"5s\"}}",
120119
context = mapOf(PLACEHOLDER_TYPE to "json"),
121-
expected = "\${json-unit.matches:formattedAndWithinNow}$ISO_LOCAL_DATETIME_FORMAT${PARAMS_SEPARATOR}5s"
120+
expected = "\${json-unit.matches:formattedAndWithinNow}ISO_LOCAL${PARAMS_SEPARATOR}5s"
122121
) {
123122
override fun invoke(context: Any?, options: Options): Any =
124-
"\${${placeholderType(options.context)}-unit.matches:formattedAndWithinNow}$ISO_LOCAL_DATETIME_FORMAT" +
123+
"\${${placeholderType(options.context)}-unit.matches:formattedAndWithinNow}ISO_LOCAL" +
125124
"${PARAMS_SEPARATOR}$context"
126125
},
127126
formattedAndWithin(
@@ -151,12 +150,12 @@ enum class MatcherHelpers(
151150
isoLocalDateTimeAndWithin(
152151
example = "{{isoLocalDateTimeAndWithin '5s' '1951-05-13'}}",
153152
context = mapOf(PLACEHOLDER_TYPE to "json"),
154-
expected = "\${json-unit.matches:formattedAndWithin}$ISO_LOCAL_DATETIME_FORMAT" +
153+
expected = "\${json-unit.matches:formattedAndWithin}ISO_LOCAL" +
155154
"${PARAMS_SEPARATOR}5s" +
156155
"${PARAMS_SEPARATOR}1951-05-13"
157156
) {
158157
override fun invoke(context: Any?, options: Options): Any =
159-
"\${${placeholderType(options.context)}-unit.matches:formattedAndWithin}$ISO_LOCAL_DATETIME_FORMAT" +
158+
"\${${placeholderType(options.context)}-unit.matches:formattedAndWithin}ISO_LOCAL" +
160159
"${PARAMS_SEPARATOR}$context" +
161160
"${PARAMS_SEPARATOR}${options.param(0, "")}"
162161
},

exam-core/src/main/java/io/github/adven27/concordion/extensions/exam/core/utils/DateMatchers.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ open class DateWithin(
8181
this.pattern = params[0]
8282
}
8383
this.period = parsePeriod(params[1])
84-
this.expected = if (now) ZonedDateTime.now() else params[2].parseDate(pattern).toZonedDateTime()
84+
this.expected = if (now) ZonedDateTime.now() else params[2].parseDate().toZonedDateTime()
8585
}
8686

8787
companion object : KLogging() {

exam-db/src/main/java/io/github/adven27/concordion/extensions/exam/db/commands/DataSetCommands.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class DataSetExecuteCommand(
4242
resultRecorder: ResultRecorder,
4343
fixture: Fixture
4444
) {
45-
cmd.html().also { root ->
45+
cmd.html().also { _ ->
4646
Attrs.from(cmd, evaluator, allowedSeedStrategies).also { attrs ->
4747
insertDataSet(attrs, evaluator).iterator().apply {
4848
while (next()) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package specs.core.handlebar
2+
3+
import io.github.adven27.concordion.extensions.exam.core.handlebars.date.DateHelpers
4+
import io.github.adven27.concordion.extensions.exam.core.handlebars.matchers.ISO_LOCAL_DATETIME_FORMAT
5+
import io.github.adven27.concordion.extensions.exam.core.handlebars.matchers.ISO_LOCAL_DATE_FORMAT
6+
import io.github.adven27.concordion.extensions.exam.core.handlebars.matchers.MatcherHelpers
7+
import io.github.adven27.concordion.extensions.exam.core.handlebars.misc.MiscHelpers
8+
import io.github.adven27.concordion.extensions.exam.core.toString
9+
import specs.Specs
10+
import java.util.Date
11+
12+
class Handlebar : Specs() {
13+
companion object {
14+
val JSON_DATE = Date().let {
15+
Triple(
16+
it.toString("yyyy/MM/dd'T'HH:mm.ss"),
17+
it.toString(ISO_LOCAL_DATE_FORMAT),
18+
it.toString(ISO_LOCAL_DATETIME_FORMAT)
19+
)
20+
}.let { (custom, date, datetime) -> // language=json
21+
"""
22+
{
23+
"formattedAs": "$custom",
24+
"isoLocalDate": "$date",
25+
"isoLocalDateTime": "$datetime",
26+
27+
"formattedAndWithinNow": "$custom",
28+
"isoLocalDateAndWithinNow": "$date",
29+
"isoLocalDateTimeAndWithinNow": "$datetime",
30+
31+
"formattedAndWithin": "$custom",
32+
"isoLocalDateAndWithin": "$date",
33+
"isoLocalDateTimeAndWithin": "$datetime",
34+
35+
"after": "$datetime",
36+
"before": "$datetime"
37+
}
38+
""".trimIndent()
39+
}
40+
}
41+
42+
val givenDateJson: String = JSON_DATE
43+
val givenDataJson: String = // language=json
44+
"""
45+
{
46+
"string": "some string",
47+
"number": 123,
48+
"bool": true,
49+
"ignore": "anything 123",
50+
"regex": "123"
51+
}
52+
""".trimIndent()
53+
val dateHelpers: String = DateHelpers.values().joinToString("\n") { it.describe() }
54+
val matcherHelpers: String = MatcherHelpers.values().joinToString("\n") { it.describe() }
55+
val miscHelpers: String = MiscHelpers.values().joinToString("\n") { it.describe() }
56+
}

0 commit comments

Comments
 (0)