Switch to Mockito Kotlin

This commit is contained in:
Erik C. Thauvin 2025-05-07 00:46:59 -07:00
parent 6c392ae5a2
commit 14418918ee
Signed by: erik
GPG key ID: 776702A6A2DA330E
5 changed files with 393 additions and 71 deletions

5
.idea/kotlinc.xml generated
View file

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="1.9" />

View file

@ -37,7 +37,6 @@ import rife.bld.extension.*;
import rife.bld.extension.dokka.LoggingLevel;
import rife.bld.extension.dokka.OutputFormat;
import rife.bld.extension.dokka.SourceSet;
import rife.bld.extension.kotlin.CompileOptions;
import rife.bld.extension.kotlin.CompilerPlugin;
import rife.bld.operations.exceptions.ExitStatusException;
import rife.bld.publish.PomBuilder;
@ -83,7 +82,7 @@ public class AkismetBuild extends Project {
scope(provided)
.include(dependency("jakarta.servlet", "jakarta.servlet-api", version(6, 1, 0)));
scope(test)
.include(dependency("org.mockito", "mockito-core", version(5, 17, 0)))
.include(dependency("org.mockito.kotlin", "mockito-kotlin", version(5, 4, 0)))
.include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin))
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2)))
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2)))
@ -139,11 +138,10 @@ public class AkismetBuild extends Project {
@Override
public void compile() throws Exception {
genver();
var options = new CompileOptions().verbose(true).jvmOptions("--enable-native-access=ALL-UNNAMED");
var op = new CompileKotlinOperation()
.fromProject(this)
.compileOptions(options)
.plugins(CompilerPlugin.KOTLIN_SERIALIZATION);
op.compileOptions().verbose(true);
op.execute();
}

View file

@ -1,5 +1,5 @@
/*
* AkismetTest.kt
* AkismetTests.kt
*
* Copyright 2019-2024 Erik C. Thauvin (erik@thauvin.net)
*
@ -43,12 +43,13 @@ import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.kotlin.whenever
import java.io.File
import java.io.FileInputStream
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.logging.ConsoleHandler
import java.util.logging.Level
import kotlin.test.assertEquals
@ -56,32 +57,12 @@ import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
fun getKey(key: String): String {
var value = System.getenv(key) ?: ""
if (value.isBlank()) {
val localProps = File("local.properties")
if (localProps.exists())
localProps.apply {
if (exists()) {
FileInputStream(this).use { fis ->
Properties().apply {
load(fis)
value = getProperty(key, "")
}
}
}
}
}
return value
}
/**
* [Akismet] Tests
*
* `AKISMET_API_KEY` and `AKISMET_BLOG` should be set in env vars or `local.properties`
*/
class AkismetTest {
class AkismetTests {
private val emptyFormBody = FormBody.Builder().build()
companion object {
@ -111,6 +92,7 @@ class AkismetTest {
.isTest(true)
.build()
private val mockComment: AkismetComment = AkismetComment(request = getMockRequest())
private val isFirstRun = AtomicBoolean(true)
init {
with(comment) {
@ -150,25 +132,45 @@ class AkismetTest {
@JvmStatic
@BeforeClass
fun beforeClass() {
if (isFirstRun.getAndSet(false)) {
with(akismet.logger) {
addHandler(ConsoleHandler().apply { level = Level.FINE })
level = Level.FINE
}
}
akismet.logger.info(comment.toString())
akismet.logger.info(mockComment.toJson())
}
private fun getKey(key: String): String {
var value = System.getenv(key) ?: ""
if (value.isBlank()) {
val localProps = File("local.properties")
localProps.apply {
if (exists()) {
FileInputStream(this).use { fis ->
Properties().apply {
load(fis)
value = getProperty(key, "")
}
}
}
}
}
return value
}
private fun getMockRequest(): HttpServletRequest {
val request = Mockito.mock(HttpServletRequest::class.java)
with(request) {
`when`(remoteAddr).thenReturn(comment.userIp)
`when`(requestURI).thenReturn("/blog/post=1")
`when`(getHeader("referer")).thenReturn(REFERER)
`when`(getHeader("Cookie")).thenReturn("name=value; name2=value2; name3=value3")
`when`(getHeader("User-Agent")).thenReturn(comment.userAgent)
`when`(getHeader("Accept-Encoding")).thenReturn("gzip")
`when`(headerNames).thenReturn(
whenever(remoteAddr).thenReturn(comment.userIp)
whenever(requestURI).thenReturn("/blog/post=1")
whenever(getHeader("referer")).thenReturn(REFERER)
whenever(getHeader("Cookie")).thenReturn("name=value; name2=value2; name3=value3")
whenever(getHeader("User-Agent")).thenReturn(comment.userAgent)
whenever(getHeader("Accept-Encoding")).thenReturn("gzip")
whenever(headerNames).thenReturn(
Collections.enumeration(listOf("User-Agent", "referer", "Cookie", "Accept-Encoding", "Null"))
)
}
@ -197,6 +199,8 @@ class AkismetTest {
assertThat(akismet::response).isEqualTo("true")
assertThat(akismet::httpStatusCode).isEqualTo(200)
comment.userRole = AkismetComment.ADMIN_ROLE
}
}
@ -264,22 +268,6 @@ class AkismetTest {
}
}
@Test
fun jsonComment() {
val jsonComment = Akismet.jsonComment(mockComment.toJson())
assertEquals(jsonComment, mockComment, "jsonComment = mockComment")
assertEquals(jsonComment.hashCode(), mockComment.hashCode(), "jsonComment.hashCode = mockComment.hashcode")
assertNotEquals(jsonComment, comment, "json")
assertNotEquals(jsonComment.hashCode(), comment.hashCode(), "jsonComment.hashCode != mockComment.hashcode")
jsonComment.recheckReason = ""
assertNotEquals(jsonComment, mockComment, "jsonComment != jsonComment")
assertThat(this, "this != comment").isNotEqualTo(comment)
}
@Test
fun mockComment() {
assertThat(mockComment, "mockComment").all {
@ -346,13 +334,57 @@ class AkismetTest {
}
@Test
fun dateToGmtTest() {
fun dateToGmt() {
val localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
val utcDate = Akismet.dateToGmt(date)
assertEquals(Akismet.dateToGmt(localDateTime), utcDate, "dateGmt(localDateTime)")
assertThat(comment::dateGmt).isEqualTo(utcDate)
}
@Nested
@DisplayName("JSON Comment Tests")
inner class JsonCommentTest {
@Test
fun jsonCommentEqualHashCode() {
val jsonComment = Akismet.jsonComment(mockComment.toJson())
assertEquals(
jsonComment.hashCode(),
mockComment.hashCode(),
"jsonComment.hashCode = mockComment.hashcode"
)
}
@Test
fun jsonCommentEqualsMockComment() {
val jsonComment = Akismet.jsonComment(mockComment.toJson())
assertEquals(jsonComment, mockComment, "jsonComment = mockComment")
}
@Test
fun jsonCommentNotEqualsComment() {
val jsonComment = Akismet.jsonComment(mockComment.toJson())
assertNotEquals(jsonComment, comment, "json")
assertNotEquals(
jsonComment.hashCode(),
comment.hashCode(),
"jsonComment.hashCode != mockComment.hashcode"
)
}
@Test
fun jsonCommentNotEqualsMockComment() {
val jsonComment = Akismet.jsonComment(mockComment.toJson())
jsonComment.recheckReason = ""
assertNotEquals(jsonComment, mockComment, "jsonComment != jsonComment")
}
@Test
fun jsonCommentNotEqualsThis() {
Akismet.jsonComment(mockComment.toJson())
assertThat(this, "this != comment").isNotEqualTo(comment)
}
}
@Nested
@DisplayName("Response Tests")
inner class ResponseTests {
@ -417,15 +449,14 @@ class AkismetTest {
}
@Test
fun proTipResponse() {
fun proTip() {
assertFalse(
akismet.executeMethod(
"https://postman-echo.com/response-headers?x-akismet-pro-tip=discard".toHttpUrl(),
emptyFormBody
)
)
assertThat(akismet, "executeMethod(pro-tip)").all {
assertThat(akismet, "executeMethod(x-akismet-pro-tip=discard)").all {
prop(Akismet::proTip).isEqualTo("discard")
prop(Akismet::isDiscard).isTrue()
}
@ -445,20 +476,49 @@ class AkismetTest {
@Test
fun submitHam() {
assertTrue(akismet.submitHam(comment), "submitHam")
}
@Test
fun submitHamMocked() {
assertTrue(akismet.submitHam(mockComment), "submitHam(mock)")
}
@Test
fun submitSpam() {
assertTrue(akismet.submitSpam(comment), "submitHam")
}
@Test
fun submitSpamMocked() {
assertTrue(akismet.submitSpam(mockComment), "submitHam(mock)")
}
}
@Nested
@DisplayName("User Agent Tests")
inner class UserAgentTests {
val libAgent = "${GeneratedVersion.PROJECT}/${GeneratedVersion.VERSION}"
@Test
fun userAgentCustom() {
akismet.appUserAgent = "My App/1.0"
assertEquals(
akismet.buildUserAgent(),
"${akismet.appUserAgent} | $libAgent",
"buildUserAgent(My App/1.0)"
)
}
@Test
fun userAgentDefault() {
assertEquals(akismet.buildUserAgent(), libAgent, "buildUserAgent($libAgent)")
}
}
@Nested
@DisplayName("Validation Tests")
inner class ValidationTests {
@Test
fun blogProperty() {
assertThrows(IllegalArgumentException::class.java) {
@ -470,7 +530,7 @@ class AkismetTest {
@Test
fun validateConfig() {
assertThat(AkismetComment(config) == comment).isTrue()
assertThat(AkismetComment(config)).isEqualTo(comment)
}
@Test
@ -489,16 +549,9 @@ class AkismetTest {
prop(Akismet::httpStatusCode).isEqualTo(0)
}
assertThat(Akismet("123456789012"), "akismet(123456789012)").prop(Akismet::verifyKey).isFalse()
}
@Test
fun userAgent() {
val libAgent = "${GeneratedVersion.PROJECT}/${GeneratedVersion.VERSION}"
assertEquals(akismet.buildUserAgent(), libAgent, "buildUserAgent($libAgent)")
akismet.appUserAgent = "My App/1.0"
assertEquals(akismet.buildUserAgent(), "${akismet.appUserAgent} | $libAgent", "buildUserAgent(My App/1.0)")
assertThat(Akismet("123456789012"), "akismet(123456789012)")
.prop(Akismet::verifyKey)
.isFalse()
}
}
}

View file

@ -0,0 +1,165 @@
/*
* CommentConfigTests.kt
*
* Copyright 2019-2025 Erik C. Thauvin (erik@thauvin.net)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.akismet
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class CommentConfigTests {
@Test
fun `test default optional fields`() {
val commentConfig = CommentConfig.Builder("192.168.0.1", "DefaultAgent").build()
assertEquals("", commentConfig.referrer)
assertEquals("", commentConfig.permalink)
assertEquals(CommentType.NONE, commentConfig.type)
assertEquals("", commentConfig.author)
assertEquals("", commentConfig.authorEmail)
assertEquals("", commentConfig.authorUrl)
assertEquals("", commentConfig.content)
assertEquals("", commentConfig.dateGmt)
assertEquals("", commentConfig.postModifiedGmt)
assertEquals("", commentConfig.blogLang)
assertEquals("", commentConfig.blogCharset)
assertEquals("", commentConfig.userRole)
assertEquals(false, commentConfig.isTest)
assertEquals("", commentConfig.recheckReason)
assertTrue(commentConfig.serverEnv.isEmpty())
}
@Test
fun `test empty server environment`() {
val commentConfig = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
.serverEnv(emptyMap())
.build()
assertTrue(commentConfig.serverEnv.isEmpty())
}
@Test
fun `test invalid inputs for mandatory fields`() {
try {
CommentConfig.Builder("", "UserAgent").build()
} catch (e: IllegalArgumentException) {
assertEquals("User IP cannot be empty", e.message)
}
try {
CommentConfig.Builder("127.0.0.1", "").build()
} catch (e: IllegalArgumentException) {
assertEquals("User Agent cannot be empty", e.message)
}
}
@Nested
@DisplayName("Builder Tests")
inner class BuilderTests {
@Test
fun `test builder with all optional fields`() {
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
.referrer("http://example.com")
.permalink("http://example.com/post")
.type(CommentType.COMMENT)
.author("John Doe")
.authorEmail("john.doe@example.com")
.authorUrl("http://johndoe.com")
.content("This is a test comment.")
.dateGmt("2025-05-28T00:00:00Z")
.postModifiedGmt("2025-05-28T01:00:00Z")
.blogLang("en")
.blogCharset("UTF-8")
.userRole("admin")
.isTest(true)
.recheckReason("manual recheck")
.serverEnv(mapOf("key" to "value"))
val commentConfig = builder.build()
assertEquals("127.0.0.1", commentConfig.userIp)
assertEquals("TestUserAgent", commentConfig.userAgent)
assertEquals("http://example.com", commentConfig.referrer)
assertEquals("http://example.com/post", commentConfig.permalink)
assertEquals(CommentType.COMMENT, commentConfig.type)
assertEquals("John Doe", commentConfig.author)
assertEquals("john.doe@example.com", commentConfig.authorEmail)
assertEquals("http://johndoe.com", commentConfig.authorUrl)
assertEquals("This is a test comment.", commentConfig.content)
assertEquals("2025-05-28T00:00:00Z", commentConfig.dateGmt)
assertEquals("2025-05-28T01:00:00Z", commentConfig.postModifiedGmt)
assertEquals("en", commentConfig.blogLang)
assertEquals("UTF-8", commentConfig.blogCharset)
assertEquals("admin", commentConfig.userRole)
assertEquals(true, commentConfig.isTest)
assertEquals("manual recheck", commentConfig.recheckReason)
assertEquals(mapOf("key" to "value"), commentConfig.serverEnv)
}
@Test
fun `test builder with mandatory fields only`() {
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
val commentConfig = builder.build()
assertEquals("127.0.0.1", commentConfig.userIp)
assertEquals("TestUserAgent", commentConfig.userAgent)
assertEquals("", commentConfig.referrer)
assertEquals("", commentConfig.permalink)
assertEquals(CommentType.NONE, commentConfig.type)
assertEquals("", commentConfig.author)
assertEquals("", commentConfig.authorEmail)
assertEquals("", commentConfig.authorUrl)
assertEquals("", commentConfig.content)
assertEquals("", commentConfig.dateGmt)
assertEquals("", commentConfig.postModifiedGmt)
assertEquals("", commentConfig.blogLang)
assertEquals("", commentConfig.blogCharset)
assertEquals("", commentConfig.userRole)
assertEquals(false, commentConfig.isTest)
assertEquals("", commentConfig.recheckReason)
assertTrue(commentConfig.serverEnv.isEmpty())
}
@Test
fun `test builder with modified mandatory fields`() {
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
.userIp("192.168.1.1")
.userAgent("ModifiedUserAgent")
val commentConfig = builder.build()
assertEquals("192.168.1.1", commentConfig.userIp)
assertEquals("ModifiedUserAgent", commentConfig.userAgent)
}
}
}

View file

@ -0,0 +1,103 @@
/*
* CommentTypeTest.kt
*
* Copyright 2019-2025 Erik C. Thauvin (erik@thauvin.net)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of this project nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.thauvin.erik.akismet
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class CommentTypeTest {
@Test
fun `verify BLOG_POST value`() {
val commentType = CommentType.BLOG_POST
assertEquals("blog-post", commentType.value)
}
@Test
fun `verify COMMENT value`() {
val commentType = CommentType.COMMENT
assertEquals("comment", commentType.value)
}
@Test
fun `verify CONTACT_FORM value`() {
val commentType = CommentType.CONTACT_FORM
assertEquals("contact-form", commentType.value)
}
@Test
fun `verify FORUM_POST value`() {
val commentType = CommentType.FORUM_POST
assertEquals("forum-post", commentType.value)
}
@Test
fun `verify MESSAGE value`() {
val commentType = CommentType.MESSAGE
assertEquals("message", commentType.value)
}
@Test
fun `verify NONE value`() {
val commentType = CommentType.NONE
assertEquals("", commentType.value)
}
@Test
fun `verify PINGBACK value`() {
val commentType = CommentType.PINGBACK
assertEquals("pingback", commentType.value)
}
@Test
fun `verify REPLY value`() {
val commentType = CommentType.REPLY
assertEquals("reply", commentType.value)
}
@Test
fun `verify SIGNUP value`() {
val commentType = CommentType.SIGNUP
assertEquals("signup", commentType.value)
}
@Test
fun `verify TRACKBACK value`() {
val commentType = CommentType.TRACKBACK
assertEquals("trackback", commentType.value)
}
@Test
fun `verify TWEET value`() {
val commentType = CommentType.TWEET
assertEquals("tweet", commentType.value)
}
}