Ignore unknown keys when deserializing JSON
This commit is contained in:
parent
a39948e174
commit
722dd9a888
9 changed files with 635 additions and 406 deletions
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
|
||||
</state>
|
||||
</component>
|
|
@ -12,6 +12,7 @@
|
|||
<ID>ReturnCount:Akismet.kt$Akismet$@JvmOverloads fun executeMethod(apiUrl: HttpUrl, formBody: FormBody, trueOnError: Boolean = false): Boolean</ID>
|
||||
<ID>ReturnCount:AkismetTest.kt$fun getKey(key: String): String</ID>
|
||||
<ID>TooManyFunctions:CommentConfig.kt$CommentConfig$Builder</ID>
|
||||
<ID>WildcardImport:AkismetCommentTest.kt$import assertk.assertions.*</ID>
|
||||
<ID>WildcardImport:AkismetTests.kt$import assertk.assertions.*</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
@ -32,8 +32,10 @@
|
|||
package net.thauvin.erik.akismet
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
|
||||
|
||||
private fun String?.ifNull() = this ?: ""
|
||||
|
||||
|
@ -54,6 +56,8 @@ private fun String?.ifNull() = this ?: ""
|
|||
* @param userAgent User agent string of the web browser submitting the comment
|
||||
*/
|
||||
@Serializable
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonIgnoreUnknownKeys
|
||||
open class AkismetComment(val userIp: String, val userAgent: String) {
|
||||
companion object {
|
||||
/**
|
||||
|
|
267
src/test/kotlin/net/thauvin/erik/akismet/AkismetCommentTest.kt
Normal file
267
src/test/kotlin/net/thauvin/erik/akismet/AkismetCommentTest.kt
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* AkismetCommentTest.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 assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ExtendWith(BeforeAllTests::class)
|
||||
class AkismetCommentTest {
|
||||
private val apiKey = TestUtils.getKey("AKISMET_API_KEY")
|
||||
private val blog = TestUtils.getKey("AKISMET_BLOG")
|
||||
private val config = CommentConfig.Builder(
|
||||
userIp = "127.0.0.1",
|
||||
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6"
|
||||
)
|
||||
.referrer("https://www.google.com")
|
||||
.permalink("https://yourblogdomainname.com/blog/post=1")
|
||||
.type(CommentType.COMMENT)
|
||||
.author("admin")
|
||||
.authorEmail("test@test.com")
|
||||
.authorUrl("https://www.CheckOutMyCoolSite.com")
|
||||
.content("It means a lot that you would take the time to review our software. Thanks again.")
|
||||
.dateGmt(Akismet.dateToGmt(LocalDateTime.of(2025, 5, 29, 0, 0, 0)))
|
||||
.postModifiedGmt(Akismet.dateToGmt(LocalDateTime.of(2025, 5, 29, 1, 0, 0)))
|
||||
.blogLang("en")
|
||||
.blogCharset("UTF-8")
|
||||
.userRole(AkismetComment.ADMIN_ROLE)
|
||||
.recheckReason("edit")
|
||||
.isTest(true)
|
||||
.build()
|
||||
|
||||
|
||||
@Test
|
||||
fun `toJson returns correct JSON representation`() {
|
||||
val comment = AkismetComment("127.0.0.1", "TestAgent")
|
||||
comment.referrer = "https://example.com"
|
||||
comment.permalink = "https://permalink.com"
|
||||
comment.type = CommentType.COMMENT
|
||||
comment.author = "Author"
|
||||
comment.content = "Sample comment"
|
||||
|
||||
val json = comment.toJson()
|
||||
assertEquals(Json.encodeToString(comment), json)
|
||||
assertThat(comment).prop(AkismetComment::toString).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Equals and hashCode methods work as expected`() {
|
||||
val comment1 = AkismetComment("127.0.0.1", "TestAgent")
|
||||
val comment2 = AkismetComment("127.0.0.1", "TestAgent")
|
||||
val comment3 = AkismetComment("192.168.0.1", "OtherAgent")
|
||||
|
||||
assertEquals(comment1, comment2)
|
||||
assertNotEquals(comment1, comment3)
|
||||
assertEquals(comment1.hashCode(), comment2.hashCode())
|
||||
assertNotEquals(comment1.hashCode(), comment3.hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Property setters handle null values correctly`() {
|
||||
val comment = AkismetComment("127.0.0.1", "TestAgent")
|
||||
|
||||
comment.referrer = null
|
||||
comment.permalink = null
|
||||
comment.author = null
|
||||
comment.authorEmail = null
|
||||
comment.authorUrl = null
|
||||
comment.content = null
|
||||
comment.dateGmt = null
|
||||
comment.postModifiedGmt = null
|
||||
comment.blogLang = null
|
||||
comment.blogCharset = null
|
||||
comment.userRole = null
|
||||
comment.recheckReason = null
|
||||
|
||||
assertThat(comment).all {
|
||||
prop(AkismetComment::referrer).isEqualTo("")
|
||||
prop(AkismetComment::permalink).isEqualTo("")
|
||||
prop(AkismetComment::author).isEqualTo("")
|
||||
prop(AkismetComment::authorEmail).isEqualTo("")
|
||||
prop(AkismetComment::authorUrl).isEqualTo("")
|
||||
prop(AkismetComment::content).isEqualTo("")
|
||||
prop(AkismetComment::dateGmt).isEqualTo("")
|
||||
prop(AkismetComment::postModifiedGmt).isEqualTo("")
|
||||
prop(AkismetComment::blogLang).isEqualTo("")
|
||||
prop(AkismetComment::blogCharset).isEqualTo("")
|
||||
prop(AkismetComment::userRole).isEqualTo("")
|
||||
prop(AkismetComment::recheckReason).isEqualTo("")
|
||||
prop(AkismetComment::isTest).isFalse()
|
||||
prop(AkismetComment::serverEnv).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Check Comment Tests")
|
||||
inner class CheckCommentTests {
|
||||
@Test
|
||||
fun `Check comment with admin role`() {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
val comment = AkismetComment(config).apply {
|
||||
userRole = AkismetComment.ADMIN_ROLE
|
||||
isTest = true
|
||||
}
|
||||
assertThat(akismet.checkComment(comment), "checkComment()").isFalse()
|
||||
assertThat(akismet).prop(Akismet::response).isEqualTo("false")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Check comment with no user role`() {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
val comment = AkismetComment(config).apply {
|
||||
userRole = ""
|
||||
isTest = true
|
||||
}
|
||||
assertTrue(akismet.checkComment(comment), "checkComment()")
|
||||
assertThat(akismet).prop(Akismet::response).isEqualTo("true")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Check comment with no user IP or user agent`() {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
assertThrows(
|
||||
java.lang.IllegalArgumentException::class.java
|
||||
) { akismet.checkComment(AkismetComment("", "")) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Constructor Tests")
|
||||
inner class ConstructorTests {
|
||||
@Test
|
||||
fun `Constructor with userIp and userAgent initializes fields correctly`() {
|
||||
val comment = AkismetComment("127.0.0.1", "TestAgent")
|
||||
|
||||
assertThat(comment).all {
|
||||
prop(AkismetComment::userIp).isEqualTo("127.0.0.1")
|
||||
prop(AkismetComment::userAgent).isEqualTo("TestAgent")
|
||||
prop(AkismetComment::referrer).isEqualTo("")
|
||||
prop(AkismetComment::permalink).isEqualTo("")
|
||||
prop(AkismetComment::type).isEqualTo(CommentType.NONE)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Constructor with HttpServletRequest initializes fields correctly`() {
|
||||
val request = mock(HttpServletRequest::class.java)
|
||||
whenever(request.remoteAddr).thenReturn("192.168.0.1")
|
||||
whenever(request.getHeader("User-Agent")).thenReturn("MockAgent")
|
||||
whenever(request.getHeader("referer")).thenReturn("https://example.com")
|
||||
whenever(request.requestURI).thenReturn("/test-uri")
|
||||
whenever(request.headerNames)
|
||||
.thenReturn(Collections.enumeration(listOf("header1", "header2", "cookie")))
|
||||
whenever(request.getHeader("header1")).thenReturn("value1")
|
||||
whenever(request.getHeader("header2")).thenReturn("value2")
|
||||
whenever(request.getHeader("cookie")).thenReturn("foo")
|
||||
|
||||
val comment = AkismetComment(request)
|
||||
|
||||
assertThat(comment).all {
|
||||
prop(AkismetComment::userIp).isEqualTo("192.168.0.1")
|
||||
prop(AkismetComment::userAgent).startsWith("MockAgent")
|
||||
prop(AkismetComment::referrer).isEqualTo("https://example.com")
|
||||
prop(AkismetComment::serverEnv).isEqualTo(
|
||||
mapOf(
|
||||
"REMOTE_ADDR" to "192.168.0.1",
|
||||
"REQUEST_URI" to "/test-uri",
|
||||
"HTTP_HEADER1" to "value1",
|
||||
"HTTP_HEADER2" to "value2"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Constructor with CommentConfig initializes fields correctly`() {
|
||||
val comment = AkismetComment(config).apply {
|
||||
serverEnv = mapOf("key" to "value")
|
||||
}
|
||||
|
||||
assertThat(comment).all {
|
||||
prop(AkismetComment::userIp).isEqualTo("127.0.0.1")
|
||||
prop(AkismetComment::userAgent).startsWith("Mozilla/5.0")
|
||||
prop(AkismetComment::referrer).isEqualTo("https://www.google.com")
|
||||
prop(AkismetComment::permalink).isEqualTo("https://yourblogdomainname.com/blog/post=1")
|
||||
prop(AkismetComment::type).isEqualTo(CommentType.COMMENT)
|
||||
prop(AkismetComment::author).isEqualTo("admin")
|
||||
prop(AkismetComment::authorEmail).isEqualTo("test@test.com")
|
||||
prop(AkismetComment::authorUrl).isEqualTo("https://www.CheckOutMyCoolSite.com")
|
||||
prop(AkismetComment::content)
|
||||
.isEqualTo("It means a lot that you would take the time to review our software. Thanks again.")
|
||||
prop(AkismetComment::dateGmt).isEqualTo("2025-05-29T00:00:00-07:00")
|
||||
prop(AkismetComment::postModifiedGmt).isEqualTo("2025-05-29T01:00:00-07:00")
|
||||
prop(AkismetComment::blogLang).isEqualTo("en")
|
||||
prop(AkismetComment::blogCharset).isEqualTo("UTF-8")
|
||||
prop(AkismetComment::userRole).isEqualTo(AkismetComment.ADMIN_ROLE)
|
||||
prop(AkismetComment::recheckReason).isEqualTo("edit")
|
||||
prop(AkismetComment::isTest).isEqualTo(true)
|
||||
prop(AkismetComment::serverEnv).isEqualTo(mapOf("key" to "value"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Submit Test")
|
||||
inner class SubmitTests {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
val comment = AkismetComment(config).apply {
|
||||
isTest = true
|
||||
userRole = AkismetComment.ADMIN_ROLE
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Submit ham`() {
|
||||
assertTrue(akismet.submitHam(comment), "submitHam")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Submit spam`() {
|
||||
assertTrue(akismet.submitSpam(comment), "submitSpam")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,285 +34,46 @@ package net.thauvin.erik.akismet
|
|||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.*
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.MissingFieldException
|
||||
import kotlinx.serialization.SerializationException
|
||||
import net.thauvin.erik.akismet.Akismet.Companion.jsonComment
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.jupiter.api.Assertions
|
||||
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.kotlin.whenever
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Level
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.text.contains
|
||||
|
||||
/**
|
||||
* [Akismet] Tests
|
||||
*
|
||||
* `AKISMET_API_KEY` and `AKISMET_BLOG` should be set in env vars or `local.properties`
|
||||
*/
|
||||
@ExtendWith(BeforeAllTests::class)
|
||||
class AkismetTests {
|
||||
private val emptyFormBody = FormBody.Builder().build()
|
||||
|
||||
companion object {
|
||||
private const val REFERER = "https://www.google.com"
|
||||
private val apiKey = getKey("AKISMET_API_KEY")
|
||||
private val blog = getKey("AKISMET_BLOG")
|
||||
private val akismet = Akismet(apiKey, blog)
|
||||
private val comment = AkismetComment(
|
||||
userIp = "127.0.0.1",
|
||||
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6"
|
||||
)
|
||||
private val date = Date()
|
||||
private val config = CommentConfig.Builder(comment.userIp, comment.userAgent)
|
||||
.referrer(REFERER)
|
||||
.permalink("https://yourblogdomainname.com/blog/post=1")
|
||||
.type(CommentType.COMMENT)
|
||||
.author("admin")
|
||||
.authorEmail("test@test.com")
|
||||
.authorUrl("https://www.CheckOutMyCoolSite.com")
|
||||
.content("It means a lot that you would take the time to review our software. Thanks again.")
|
||||
.dateGmt(Akismet.dateToGmt(date))
|
||||
.postModifiedGmt(Akismet.dateToGmt(date))
|
||||
.blogLang("en")
|
||||
.blogCharset("UTF-8")
|
||||
.userRole(AkismetComment.ADMIN_ROLE)
|
||||
.recheckReason("edit")
|
||||
.isTest(true)
|
||||
.build()
|
||||
private val mockComment: AkismetComment = AkismetComment(request = getMockRequest())
|
||||
private val isFirstRun = AtomicBoolean(true)
|
||||
|
||||
init {
|
||||
with(comment) {
|
||||
referrer = config.referrer
|
||||
permalink = config.permalink
|
||||
type = CommentType("comment")
|
||||
author = config.author
|
||||
authorEmail = config.authorEmail
|
||||
authorUrl = config.authorUrl
|
||||
content = config.content
|
||||
dateGmt = config.dateGmt
|
||||
postModifiedGmt = config.postModifiedGmt
|
||||
blogLang = config.blogLang
|
||||
blogCharset = config.blogCharset
|
||||
userRole = config.userRole
|
||||
recheckReason = config.recheckReason
|
||||
isTest = config.isTest
|
||||
}
|
||||
|
||||
with(mockComment) {
|
||||
permalink = comment.permalink
|
||||
type = comment.type
|
||||
authorEmail = comment.authorEmail
|
||||
author = comment.author
|
||||
authorUrl = comment.authorUrl
|
||||
content = comment.content
|
||||
dateGmt = comment.dateGmt
|
||||
postModifiedGmt = comment.dateGmt
|
||||
blogLang = comment.blogLang
|
||||
blogCharset = comment.blogCharset
|
||||
userRole = AkismetComment.ADMIN_ROLE
|
||||
recheckReason = comment.recheckReason
|
||||
isTest = true
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
return System.getenv(key)?.takeUnless { it.isBlank() }
|
||||
?: loadPropertyValue(key)
|
||||
}
|
||||
|
||||
private fun getMockRequest(): HttpServletRequest {
|
||||
val request = Mockito.mock(HttpServletRequest::class.java)
|
||||
with(request) {
|
||||
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"))
|
||||
)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
private fun loadPropertyValue(key: String): String {
|
||||
return File("local.properties")
|
||||
.takeIf { it.exists() }
|
||||
?.let { file ->
|
||||
FileInputStream(file).use { fis ->
|
||||
Properties().apply { load(fis) }.getProperty(key, "")
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Comment Tests")
|
||||
inner class CommentTests {
|
||||
@Test
|
||||
fun checkComment() {
|
||||
with(akismet) {
|
||||
assertFalse(checkComment(comment), "checkComment(admin)")
|
||||
assertThat(akismet::response).isEqualTo("false")
|
||||
|
||||
comment.userRole = ""
|
||||
assertTrue(checkComment(comment), "checkComment()")
|
||||
assertThat(akismet::response).isEqualTo("true")
|
||||
|
||||
assertFalse(checkComment(mockComment), "checkComment(mock)")
|
||||
assertThat(akismet::response).isEqualTo("false")
|
||||
|
||||
mockComment.userRole = ""
|
||||
assertTrue(checkComment(mockComment), "checkComment(mock)")
|
||||
assertThat(akismet::response).isEqualTo("true")
|
||||
|
||||
assertThat(akismet::httpStatusCode).isEqualTo(200)
|
||||
|
||||
comment.userRole = AkismetComment.ADMIN_ROLE
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emptyComment() {
|
||||
assertThrows(
|
||||
java.lang.IllegalArgumentException::class.java
|
||||
) { akismet.checkComment(AkismetComment("", "")) }
|
||||
|
||||
|
||||
val empty = AkismetComment("", "")
|
||||
assertThat(empty, "AkismetComment(empty)").all {
|
||||
prop(AkismetComment::isTest).isFalse()
|
||||
prop(AkismetComment::referrer).isEqualTo("")
|
||||
prop(AkismetComment::permalink).isEqualTo("")
|
||||
prop(AkismetComment::type).isEqualTo(CommentType.NONE)
|
||||
prop(AkismetComment::authorEmail).isEqualTo("")
|
||||
prop(AkismetComment::author).isEqualTo("")
|
||||
prop(AkismetComment::authorUrl).isEqualTo("")
|
||||
prop(AkismetComment::content).isEqualTo("")
|
||||
prop(AkismetComment::dateGmt).isEqualTo("")
|
||||
prop(AkismetComment::postModifiedGmt).isEqualTo("")
|
||||
prop(AkismetComment::blogLang).isEqualTo("")
|
||||
prop(AkismetComment::blogCharset).isEqualTo("")
|
||||
prop(AkismetComment::userRole).isEqualTo("")
|
||||
prop(AkismetComment::recheckReason).isEqualTo("")
|
||||
prop(AkismetComment::serverEnv).size().isEqualTo(0)
|
||||
}
|
||||
|
||||
with(receiver = empty) {
|
||||
for (s in listOf("test", "", null)) {
|
||||
referrer = s
|
||||
permalink = s
|
||||
if (s != null) type = CommentType(s)
|
||||
authorEmail = s
|
||||
author = s
|
||||
authorUrl = s
|
||||
content = s
|
||||
dateGmt = s
|
||||
postModifiedGmt = s
|
||||
blogLang = s
|
||||
blogCharset = s
|
||||
userRole = s
|
||||
recheckReason = s
|
||||
|
||||
val expected = if (s.isNullOrEmpty()) "" else s
|
||||
|
||||
assertThat(empty, "AkismetComment($s)").all {
|
||||
prop(AkismetComment::referrer).isEqualTo(expected)
|
||||
prop(AkismetComment::permalink).isEqualTo(expected)
|
||||
prop(AkismetComment::type).isEqualTo(CommentType(expected))
|
||||
prop(AkismetComment::authorEmail).isEqualTo(expected)
|
||||
prop(AkismetComment::author).isEqualTo(expected)
|
||||
prop(AkismetComment::authorUrl).isEqualTo(expected)
|
||||
prop(AkismetComment::content).isEqualTo(expected)
|
||||
prop(AkismetComment::dateGmt).isEqualTo(expected)
|
||||
prop(AkismetComment::postModifiedGmt).isEqualTo(expected)
|
||||
prop(AkismetComment::blogLang).isEqualTo(expected)
|
||||
prop(AkismetComment::blogCharset).isEqualTo(expected)
|
||||
prop(AkismetComment::userRole).isEqualTo(expected)
|
||||
prop(AkismetComment::recheckReason).isEqualTo(expected)
|
||||
prop(AkismetComment::serverEnv).size().isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mockComment() {
|
||||
assertThat(mockComment, "mockComment").all {
|
||||
prop(AkismetComment::userIp).isEqualTo(comment.userIp)
|
||||
prop(AkismetComment::userAgent).isEqualTo(comment.userAgent)
|
||||
prop(AkismetComment::referrer).isEqualTo(comment.referrer)
|
||||
prop(AkismetComment::serverEnv).all {
|
||||
key("HTTP_ACCEPT_ENCODING").isEqualTo("gzip")
|
||||
key("REMOTE_ADDR").isEqualTo(comment.userIp)
|
||||
key("HTTP_NULL").isEmpty()
|
||||
size().isEqualTo(6)
|
||||
}
|
||||
}
|
||||
}
|
||||
private val apiKey = TestUtils.getKey("AKISMET_API_KEY")
|
||||
private val blog = TestUtils.getKey("AKISMET_BLOG")
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Constructor Tests")
|
||||
inner class ConstructorTests {
|
||||
@Test
|
||||
fun apiKeyTooLong() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("123456789 12")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apiKeyTooShort() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("1234")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidKeyAndBlog() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("1234", "foo")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noApiKey() {
|
||||
fun `Constructor with API key arg empty`() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
|
@ -321,64 +82,159 @@ class AkismetTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun noBlog() {
|
||||
fun `Constructor with API key arg too long`() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("123456789 12")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Constructor with API key arg too short`() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("1234")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Constructor with empty blog arg`() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("123456789012", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
@Test
|
||||
fun `Constructor with invalid key and blog args`() {
|
||||
assertThrows(
|
||||
IllegalArgumentException::class.java
|
||||
) {
|
||||
Akismet("1234", "foo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("JSON Comment Tests")
|
||||
inner class JsonCommentTest {
|
||||
@DisplayName("Date Conversion Tests")
|
||||
inner class DateConversionTests {
|
||||
val sampleDate: ZonedDateTime = LocalDateTime.of(1997, 8, 29, 2, 0, 0)
|
||||
.atZone(ZoneId.of("America/New_York"))
|
||||
|
||||
@Test
|
||||
fun jsonCommentEqualHashCode() {
|
||||
val jsonComment = Akismet.jsonComment(mockComment.toJson())
|
||||
assertEquals(
|
||||
jsonComment.hashCode(),
|
||||
mockComment.hashCode(),
|
||||
"jsonComment.hashCode = mockComment.hashcode"
|
||||
)
|
||||
fun `Date should convert correctly to GMT string`() {
|
||||
val date = Date.from(sampleDate.toInstant())
|
||||
val result = Akismet.dateToGmt(date)
|
||||
assertEquals("1997-08-28T23:00:00-07:00", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jsonCommentEqualsMockComment() {
|
||||
val jsonComment = Akismet.jsonComment(mockComment.toJson())
|
||||
assertEquals(jsonComment, mockComment, "jsonComment = mockComment")
|
||||
fun `LocalDateTime should convert correctly to GMT string`() {
|
||||
val result = Akismet.dateToGmt(sampleDate.toLocalDateTime())
|
||||
assertEquals("1997-08-29T02:00:00-07:00", result)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("JSON Deserialization Tests")
|
||||
inner class JsonDeserializationTests {
|
||||
@Test
|
||||
fun `Validate JSON deserialization`() {
|
||||
val config = CommentConfig.Builder("127.0.0.1", "Mozilla/5.0")
|
||||
.referrer("https://example.com")
|
||||
.type(CommentType.COMMENT)
|
||||
.author("John Doe")
|
||||
.authorEmail("john.doe@example.com")
|
||||
.authorUrl("https://johndoe.com")
|
||||
.content("This is a comment")
|
||||
.dateGmt("2023-10-20T10:20:30Z")
|
||||
.postModifiedGmt("2023-10-20T11:00:00Z")
|
||||
.blogLang("en")
|
||||
.blogCharset("UTF-8")
|
||||
.userRole("administrator")
|
||||
.isTest(true)
|
||||
.recheckReason("Check the spam detection")
|
||||
.serverEnv(mapOf("key1" to "value1", "key2" to "value2"))
|
||||
.build()
|
||||
val validJson = AkismetComment(config).toJson();
|
||||
|
||||
val comment = jsonComment(validJson)
|
||||
|
||||
Assertions.assertEquals("127.0.0.1", comment.userIp)
|
||||
Assertions.assertEquals("Mozilla/5.0", comment.userAgent)
|
||||
Assertions.assertEquals("https://example.com", comment.referrer)
|
||||
Assertions.assertEquals("comment", comment.type.value)
|
||||
Assertions.assertEquals("John Doe", comment.author)
|
||||
Assertions.assertEquals("john.doe@example.com", comment.authorEmail)
|
||||
Assertions.assertEquals("https://johndoe.com", comment.authorUrl)
|
||||
Assertions.assertEquals("This is a comment", comment.content)
|
||||
Assertions.assertEquals("2023-10-20T10:20:30Z", comment.dateGmt)
|
||||
Assertions.assertEquals("2023-10-20T11:00:00Z", comment.postModifiedGmt)
|
||||
Assertions.assertEquals("en", comment.blogLang)
|
||||
Assertions.assertEquals("UTF-8", comment.blogCharset)
|
||||
Assertions.assertEquals("administrator", comment.userRole)
|
||||
Assertions.assertTrue(comment.isTest)
|
||||
Assertions.assertEquals("Check the spam detection", comment.recheckReason)
|
||||
Assertions.assertEquals(mapOf("key1" to "value1", "key2" to "value2"), comment.serverEnv)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jsonCommentNotEqualsComment() {
|
||||
val jsonComment = Akismet.jsonComment(mockComment.toJson())
|
||||
assertNotEquals(jsonComment, comment, "json")
|
||||
assertNotEquals(
|
||||
jsonComment.hashCode(),
|
||||
comment.hashCode(),
|
||||
"jsonComment.hashCode != mockComment.hashcode"
|
||||
)
|
||||
fun `Invalid JSON deserialization`() {
|
||||
val invalidJson = """
|
||||
{
|
||||
"userIp": "127.0.0.1",
|
||||
"userAgent": "Mozilla/5.0"
|
||||
// Missing closing brace
|
||||
""".trimIndent()
|
||||
|
||||
val exception = Assertions.assertThrows(SerializationException::class.java) {
|
||||
jsonComment(invalidJson)
|
||||
}
|
||||
|
||||
Assertions.assertTrue(exception.message?.contains("Unexpected JSON token") == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jsonCommentNotEqualsMockComment() {
|
||||
val jsonComment = Akismet.jsonComment(mockComment.toJson())
|
||||
jsonComment.recheckReason = ""
|
||||
assertNotEquals(jsonComment, mockComment, "jsonComment != jsonComment")
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun `Empty JSON deserialization`() {
|
||||
val emptyJson = "{}"
|
||||
|
||||
Assertions.assertThrows(MissingFieldException::class.java) {
|
||||
jsonComment(emptyJson)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jsonCommentNotEqualsThis() {
|
||||
Akismet.jsonComment(mockComment.toJson())
|
||||
assertThat(this, "this != comment").isNotEqualTo(comment)
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
fun `JSON deserialization with missing mandatory fields`() {
|
||||
val partialJson = """
|
||||
{
|
||||
"userIp": "127.0.0.1"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
Assertions.assertThrows(MissingFieldException::class.java) {
|
||||
jsonComment(partialJson)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `JSON deserialization with unexpected fields`() {
|
||||
val extraFieldJson = """
|
||||
{
|
||||
"userIp": "127.0.0.1",
|
||||
"userAgent": "Mozilla/5.0",
|
||||
"extraField": "unexpected"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val comment = jsonComment(extraFieldJson)
|
||||
|
||||
Assertions.assertEquals("127.0.0.1", comment.userIp)
|
||||
Assertions.assertEquals("Mozilla/5.0", comment.userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,30 +242,14 @@ class AkismetTests {
|
|||
@DisplayName("Response Tests")
|
||||
inner class ResponseTests {
|
||||
@Test
|
||||
fun emptyResponse() {
|
||||
assertTrue(
|
||||
akismet.executeMethod(
|
||||
"https://postman-echo.com/status/200".toHttpUrl(), emptyFormBody, true
|
||||
)
|
||||
)
|
||||
var expected = "{\n \"status\": 200\n}"
|
||||
assertThat(akismet, "executeMethod(200)").all {
|
||||
prop(Akismet::response).isEqualTo(expected)
|
||||
prop(Akismet::errorMessage).contains(expected)
|
||||
}
|
||||
|
||||
akismet.reset()
|
||||
assertThat(akismet, "akismet.reset()").all {
|
||||
prop(Akismet::httpStatusCode).isEqualTo(0)
|
||||
prop(Akismet::errorMessage).isEmpty()
|
||||
}
|
||||
|
||||
fun `Handle blank response`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
assertTrue(
|
||||
akismet.executeMethod(
|
||||
"https://erik.thauvin.net/blank.html".toHttpUrl(), emptyFormBody, true
|
||||
)
|
||||
)
|
||||
expected = ""
|
||||
val expected = ""
|
||||
assertThat(akismet, "executeMethod(blank)").all {
|
||||
prop(Akismet::response).isEqualTo(expected)
|
||||
prop(Akismet::errorMessage).contains("blank")
|
||||
|
@ -417,12 +257,17 @@ class AkismetTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun executeMethod() {
|
||||
fun `Handle debug help header`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
akismet.executeMethod(
|
||||
"https://$apiKey.rest.akismet.com/1.1/comment-check".toHttpUrl(),
|
||||
FormBody.Builder().apply { add("is_test", "1") }.build()
|
||||
)
|
||||
assertThat(akismet::debugHelp).isNotEmpty()
|
||||
|
||||
assertThat(akismet, "x-akismet-debug-help").all {
|
||||
prop(Akismet::httpStatusCode).isEqualTo(200)
|
||||
prop(Akismet::debugHelp).isEqualTo("Empty \"blog\" value")
|
||||
}
|
||||
|
||||
akismet.reset()
|
||||
assertThat(akismet, "akismet.reset()").all {
|
||||
|
@ -433,20 +278,44 @@ class AkismetTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun invalidApi() {
|
||||
fun `Handle invalid response`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
assertTrue(
|
||||
akismet.executeMethod(
|
||||
"https://postman-echo.com/status/200".toHttpUrl(), emptyFormBody, true
|
||||
)
|
||||
)
|
||||
val expected = "{\n \"status\": 200\n}"
|
||||
assertThat(akismet, "executeMethod(200)").all {
|
||||
prop(Akismet::response).isEqualTo(expected)
|
||||
prop(Akismet::errorMessage).contains(expected)
|
||||
}
|
||||
|
||||
akismet.reset()
|
||||
assertThat(akismet, "akismet.reset()").all {
|
||||
prop(Akismet::httpStatusCode).isEqualTo(0)
|
||||
prop(Akismet::errorMessage).isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handle IO error`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
akismet.executeMethod("https://www.foobarxyz.com".toHttpUrl(), emptyFormBody)
|
||||
assertThat(akismet).prop(Akismet::errorMessage).contains("IO error")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Handle invalid API URL`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
assertThrows(
|
||||
java.lang.IllegalArgumentException::class.java
|
||||
) { akismet.executeMethod("https://.com".toHttpUrl(), emptyFormBody) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ioError() {
|
||||
akismet.executeMethod("https://www.foobarxyz.com".toHttpUrl(), emptyFormBody)
|
||||
assertThat(akismet::errorMessage).contains("IO error")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun proTip() {
|
||||
fun `Handle pro tip header`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
assertFalse(
|
||||
akismet.executeMethod(
|
||||
"https://postman-echo.com/response-headers?x-akismet-pro-tip=discard".toHttpUrl(),
|
||||
|
@ -465,90 +334,70 @@ class AkismetTests {
|
|||
prop(Akismet::httpStatusCode).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("Submit Test")
|
||||
inner class SubmitTests {
|
||||
@Test
|
||||
fun submitHam() {
|
||||
assertTrue(akismet.submitHam(comment), "submitHam")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun submitHamMocked() {
|
||||
assertTrue(akismet.submitHam(mockComment), "submitHam(mock)")
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("Validation Tests")
|
||||
inner class ValidationTests {
|
||||
@Test
|
||||
fun `Validate api key`() {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
assertThat(akismet, "akismet").all {
|
||||
prop(Akismet::isVerifiedKey).isFalse()
|
||||
prop(Akismet::verifyKey).isTrue()
|
||||
prop(Akismet::response).isEqualTo("valid")
|
||||
prop(Akismet::isVerifiedKey).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun submitSpam() {
|
||||
assertTrue(akismet.submitSpam(comment), "submitHam")
|
||||
}
|
||||
akismet.reset()
|
||||
assertThat(akismet, "akismet.reset()").all {
|
||||
prop(Akismet::isVerifiedKey).isFalse()
|
||||
prop(Akismet::response).isEmpty()
|
||||
prop(Akismet::httpStatusCode).isEqualTo(0)
|
||||
}
|
||||
|
||||
@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) {
|
||||
akismet.blog = ""
|
||||
assertThat(Akismet("123456789012"), "akismet(123456789012)")
|
||||
.prop(Akismet::verifyKey)
|
||||
.isFalse()
|
||||
}
|
||||
|
||||
assertThat(akismet::blog).isEqualTo(blog)
|
||||
}
|
||||
@Test
|
||||
fun `Validate blog property`() {
|
||||
val akismet = Akismet(apiKey, blog)
|
||||
assertThrows(IllegalArgumentException::class.java) {
|
||||
akismet.blog = ""
|
||||
}
|
||||
|
||||
@Test
|
||||
fun validateConfig() {
|
||||
assertThat(AkismetComment(config)).isEqualTo(comment)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyKey() {
|
||||
assertThat(akismet, "akismet").all {
|
||||
prop(Akismet::isVerifiedKey).isFalse()
|
||||
prop(Akismet::verifyKey).isTrue()
|
||||
prop(Akismet::response).isEqualTo("valid")
|
||||
prop(Akismet::isVerifiedKey).isTrue()
|
||||
assertThat(akismet).prop(Akismet::blog).isEqualTo(blog)
|
||||
}
|
||||
|
||||
akismet.reset()
|
||||
assertThat(akismet, "akismet.reset()").all {
|
||||
prop(Akismet::isVerifiedKey).isFalse()
|
||||
prop(Akismet::response).isEmpty()
|
||||
prop(Akismet::httpStatusCode).isEqualTo(0)
|
||||
}
|
||||
|
||||
assertThat(Akismet("123456789012"), "akismet(123456789012)")
|
||||
.prop(Akismet::verifyKey)
|
||||
.isFalse()
|
||||
@Nested
|
||||
@DisplayName("User Agent Validation Tests")
|
||||
inner class UserAgentValidationTests {
|
||||
val libAgent = "${GeneratedVersion.PROJECT}/${GeneratedVersion.VERSION}"
|
||||
|
||||
@Test
|
||||
fun `Validate custom user agent`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
akismet.appUserAgent = "My App/1.0"
|
||||
|
||||
assertEquals(
|
||||
"${akismet.appUserAgent} | $libAgent",
|
||||
akismet.buildUserAgent(),
|
||||
"buildUserAgent(My App/1.0)"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Validate default user agent`() {
|
||||
val akismet = Akismet(apiKey)
|
||||
assertEquals(
|
||||
libAgent, akismet.buildUserAgent(),
|
||||
"buildUserAgent($libAgent)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
51
src/test/kotlin/net/thauvin/erik/akismet/BeforeAllTests.kt
Normal file
51
src/test/kotlin/net/thauvin/erik/akismet/BeforeAllTests.kt
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* BeforeAllTests.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.extension.BeforeAllCallback
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.logging.ConsoleHandler
|
||||
import java.util.logging.Level
|
||||
|
||||
class BeforeAllTests : BeforeAllCallback {
|
||||
private val isFirstTime: AtomicBoolean = AtomicBoolean(true)
|
||||
|
||||
override fun beforeAll(context: ExtensionContext?) {
|
||||
if (isFirstTime.getAndSet(false)) {
|
||||
with(Akismet.logger) {
|
||||
addHandler(ConsoleHandler().apply { level = Level.FINE })
|
||||
level = Level.FINE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ import kotlin.test.assertTrue
|
|||
|
||||
class CommentConfigTests {
|
||||
@Test
|
||||
fun `test default optional fields`() {
|
||||
fun `Default optional fields`() {
|
||||
val commentConfig = CommentConfig.Builder("192.168.0.1", "DefaultAgent").build()
|
||||
|
||||
assertEquals("", commentConfig.referrer)
|
||||
|
@ -60,7 +60,7 @@ class CommentConfigTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `test empty server environment`() {
|
||||
fun `Empty server environment`() {
|
||||
val commentConfig = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
|
||||
.serverEnv(emptyMap())
|
||||
.build()
|
||||
|
@ -69,7 +69,7 @@ class CommentConfigTests {
|
|||
|
||||
|
||||
@Test
|
||||
fun `test invalid inputs for mandatory fields`() {
|
||||
fun `Invalid inputs for mandatory fields`() {
|
||||
try {
|
||||
CommentConfig.Builder("", "UserAgent").build()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
|
@ -88,7 +88,7 @@ class CommentConfigTests {
|
|||
@DisplayName("Builder Tests")
|
||||
inner class BuilderTests {
|
||||
@Test
|
||||
fun `test builder with all optional fields`() {
|
||||
fun `Builder with all optional fields`() {
|
||||
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
|
||||
.referrer("http://example.com")
|
||||
.permalink("http://example.com/post")
|
||||
|
@ -128,7 +128,7 @@ class CommentConfigTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `test builder with mandatory fields only`() {
|
||||
fun `Builder with mandatory fields only`() {
|
||||
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
|
||||
val commentConfig = builder.build()
|
||||
|
||||
|
@ -152,7 +152,7 @@ class CommentConfigTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `test builder with modified mandatory fields`() {
|
||||
fun `Builder with modified mandatory fields`() {
|
||||
val builder = CommentConfig.Builder("127.0.0.1", "TestUserAgent")
|
||||
.userIp("192.168.1.1")
|
||||
.userAgent("ModifiedUserAgent")
|
||||
|
|
|
@ -36,67 +36,67 @@ import org.junit.jupiter.api.Test
|
|||
|
||||
class CommentTypeTest {
|
||||
@Test
|
||||
fun `verify BLOG_POST value`() {
|
||||
fun `Verify BLOG_POST value`() {
|
||||
val commentType = CommentType.BLOG_POST
|
||||
assertEquals("blog-post", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify COMMENT value`() {
|
||||
fun `Verify COMMENT value`() {
|
||||
val commentType = CommentType.COMMENT
|
||||
assertEquals("comment", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify CONTACT_FORM value`() {
|
||||
fun `Verify CONTACT_FORM value`() {
|
||||
val commentType = CommentType.CONTACT_FORM
|
||||
assertEquals("contact-form", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify FORUM_POST value`() {
|
||||
fun `Verify FORUM_POST value`() {
|
||||
val commentType = CommentType.FORUM_POST
|
||||
assertEquals("forum-post", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify MESSAGE value`() {
|
||||
fun `Verify MESSAGE value`() {
|
||||
val commentType = CommentType.MESSAGE
|
||||
assertEquals("message", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify NONE value`() {
|
||||
fun `Verify NONE value`() {
|
||||
val commentType = CommentType.NONE
|
||||
assertEquals("", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify PINGBACK value`() {
|
||||
fun `Verify PINGBACK value`() {
|
||||
val commentType = CommentType.PINGBACK
|
||||
assertEquals("pingback", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify REPLY value`() {
|
||||
fun `Verify REPLY value`() {
|
||||
val commentType = CommentType.REPLY
|
||||
assertEquals("reply", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify SIGNUP value`() {
|
||||
fun `Verify SIGNUP value`() {
|
||||
val commentType = CommentType.SIGNUP
|
||||
assertEquals("signup", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify TRACKBACK value`() {
|
||||
fun `Verify TRACKBACK value`() {
|
||||
val commentType = CommentType.TRACKBACK
|
||||
assertEquals("trackback", commentType.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify TWEET value`() {
|
||||
fun `Verify TWEET value`() {
|
||||
val commentType = CommentType.TWEET
|
||||
assertEquals("tweet", commentType.value)
|
||||
}
|
||||
|
|
52
src/test/kotlin/net/thauvin/erik/akismet/TestUtils.kt
Normal file
52
src/test/kotlin/net/thauvin/erik/akismet/TestUtils.kt
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* TestUtils.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 java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.util.Properties
|
||||
|
||||
object TestUtils {
|
||||
fun getKey(key: String): String {
|
||||
return System.getenv(key)?.takeUnless { it.isBlank() } ?: loadPropertyValue(key)
|
||||
}
|
||||
|
||||
private fun loadPropertyValue(key: String): String {
|
||||
return File("local.properties")
|
||||
.takeIf { it.exists() }
|
||||
?.let { file ->
|
||||
FileInputStream(file).use { fis ->
|
||||
Properties().apply { load(fis) }.getProperty(key, "")
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue