Cleanup tests

This commit is contained in:
Erik C. Thauvin 2025-05-03 17:43:43 -07:00
parent a72af5cac6
commit cce4ddbcb9
Signed by: erik
GPG key ID: 776702A6A2DA330E
7 changed files with 715 additions and 353 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ConstructorParameterNaming:CreateConfig.kt$CreateConfig.Builder$var long_url: String</ID>
<ID>FunctionParameterNaming:Bitlinks.kt$Bitlinks$bitlink_id: String</ID>
@ -42,7 +42,6 @@
<ID>VariableNaming:CreateConfig.kt$CreateConfig$val group_guid = builder.group_guid</ID>
<ID>VariableNaming:CreateConfig.kt$CreateConfig$val long_url = builder.long_url</ID>
<ID>VariableNaming:CreateConfig.kt$CreateConfig.Builder$var group_guid: String = Constants.EMPTY</ID>
<ID>WildcardImport:BitlyTest.kt$import assertk.assertions.*</ID>
<ID>WildcardImport:BitlyTest.kt$import kotlin.test.*</ID>
<ID>WildcardImport:BitlinksTest.kt$import assertk.assertions.*</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -174,8 +174,9 @@ object Utils {
return false
}
/**
* Removes http(s) scheme from string.
* Removes the `http` or `https` schemes from a string.
*/
@JvmStatic
fun String.removeHttp(): String {
@ -183,7 +184,7 @@ object Utils {
}
/**
* Builds the full API endpoint URL using the [Constants.API_BASE_URL].
* Converts a path to an API endpoint URL using the [Constants.API_BASE_URL], unless a URL is already specified.
*/
@JvmStatic
fun String.toEndPoint(): String {

View file

@ -0,0 +1,338 @@
/*
* BitlinksTest.kt
*
* Copyright 2020-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.bitly
import DisableOnCi
import assertk.all
import assertk.assertThat
import assertk.assertions.*
import net.thauvin.erik.bitly.config.CreateConfig
import net.thauvin.erik.bitly.config.UpdateConfig
import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
import net.thauvin.erik.bitly.config.deeplinks.enums.Os
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable
import java.io.File
import java.util.logging.Level
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class BitlinksTest {
private val bitly = with(File("local.properties")) {
if (exists()) {
Bitly(toPath())
} else {
Bitly()
}
}
private val longUrl = "https://erik.thauvin.net/blog"
private val shortUrl = "https://bit.ly/380ojFd"
companion object {
@JvmStatic
@BeforeAll
fun before() {
with(Utils.logger) {
level = Level.FINE
}
}
}
@Nested
@DisplayName("Bitlinks Tests")
inner class BitlinksTests {
@Test
fun `Clicks summary`() {
val bl = bitly.bitlinks()
val clicks = bl.clicks(shortUrl, unit = Units.MONTH, units = 1)
assertThat(bl.lastCallResponse).all {
prop(CallResponse::description).isEmpty()
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::statusCode).isEqualTo(200)
}
assertThat(clicks.toInt()).isGreaterThanOrEqualTo(0)
}
@Test
fun `Clicks summary as json`() {
val bl = bitly.bitlinks()
val clicks = bl.clicks(shortUrl, toJson = true)
assertThat(bl.lastCallResponse).all {
prop(CallResponse::description).isEmpty()
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::statusCode).isEqualTo(200)
}
assertThat(clicks).startsWith("{\"unit_reference\":")
}
}
@Nested
@DisplayName("Create Bitlink Tests")
inner class CreateBitlinkTests {
@Test
fun `Create bitlink`() {
assertThat(bitly.bitlinks().create(long_url = longUrl), "create(longUrl)")
.matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
assertEquals(
shortUrl,
bitly.bitlinks().create(
domain = "bit.ly",
title = "Erik's Blog",
tags = arrayOf("erik", "thauvin", "blog", "weblog"),
long_url = longUrl
)
)
}
@Test
fun `Create bitlink with config`() {
var config = CreateConfig.Builder(longUrl).build()
assertThat(bitly.bitlinks().create(config), "create(config)")
.matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
config = CreateConfig.Builder(longUrl)
.domain("bit.ly")
.title("Erik's Blog")
.tags(arrayOf("erik", "thauvin", "blog", "weblog"))
.build()
assertEquals(
shortUrl,
bitly.bitlinks().create(config)
)
}
@Test
fun `Create bitlink with deep links`() {
val bl = bitly.bitlinks()
val dl = CreateDeeplinks().apply {
app_uri_path("/store?id=123456")
install_type(InstallType.NO_INSTALL)
install_url("https://play.google.com/store/apps/details?id=com.bitly.app&hl=en_US")
}
val config = CreateConfig.Builder(longUrl)
.deeplinks(dl)
.domain("bit.ly")
.build()
assertThat(bl.create(config)).isEqualTo(Constants.EMPTY)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
}
@Nested
@DisplayName("Expand Test")
inner class ExpandTests {
@Test
fun `Expand as json`() {
assertTrue(
bitly.bitlinks().expand(shortUrl, toJson = true)
.startsWith("{\"created_at\":")
)
}
@Test
fun `Expand link`() {
assertEquals(longUrl, Bitlinks(bitly.accessToken).expand(shortUrl))
}
}
@Nested
@DisplayName("Shorten Tests")
inner class ShortenTests {
@Test
fun `Shorten as json`() {
assertTrue(
bitly.bitlinks().shorten(longUrl, toJson = true)
.startsWith("{\"created_at\":")
)
}
@Test
fun `Shorten last call response`() {
val bl = Bitlinks(bitly.accessToken)
bl.shorten(longUrl, domain = "bit.ly")
assertThat(bl.lastCallResponse, "shorten(longUrl)").all {
prop(CallResponse::body).contains("\"link\":\"$shortUrl\"")
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::message).isEmpty()
prop(CallResponse::statusCode).isEqualTo(200)
}
bl.shorten(shortUrl)
assertThat(bl.lastCallResponse, "shorten(shortUrl)").all {
prop(CallResponse::description).isEqualTo("The value provided is invalid.")
prop(CallResponse::isBadRequest).isTrue()
prop(CallResponse::isSuccessful).isFalse()
prop(CallResponse::message).isEqualTo("ALREADY_A_BITLY_LINK")
prop(CallResponse::statusCode).isEqualTo(400)
}
}
@Test
fun `Shorten link`() {
assertEquals(
shortUrl, Bitlinks(bitly.accessToken)
.shorten(longUrl, domain = "bit.ly")
)
}
@Test
fun `Shorten with invalid domain`() {
val bl = bitly.bitlinks()
bl.shorten("https://www.examples.com", domain = "foo.com")
assertThat(bl.lastCallResponse).all {
prop(CallResponse::description).contains("invalid")
prop(CallResponse::isBadRequest).isTrue()
prop(CallResponse::isSuccessful).isFalse()
prop(CallResponse::message).isEqualTo("INVALID_ARG_DOMAIN")
}
}
}
@Nested
@DisplayName("Update Bitlink Tests")
inner class UpdateBitlinkTests {
@Test
fun `Update bitlink`() {
val bl = bitly.bitlinks()
assertEquals(
Constants.TRUE,
bl.update(
shortUrl, title = "Erik's Weblog", tags = arrayOf("blog", "weblog"), archived = true
)
)
assertThat(bl.update(shortUrl, tags = emptyArray(), toJson = true), "update(tags)")
.contains("\"tags\":[]")
}
@Test
fun `Update bitlink with config`() {
val bl = bitly.bitlinks()
var config = UpdateConfig.Builder(shortUrl)
.archived(true)
.tags(arrayOf("blog", "weblog"))
.title("Erik's Weblog")
.build()
assertEquals(Constants.TRUE, bl.update(config))
config = UpdateConfig.Builder(shortUrl)
.toJson(true)
.build()
assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]")
}
@Test
fun `Update bitlink with deep links`() {
val bl = bitly.bitlinks()
val dl = UpdateDeeplinks().apply {
os(Os.ANDROID)
brand_guid("Ba1bc23dE4F")
}
val config = UpdateConfig.Builder(shortUrl)
.deeplinks(dl)
.build()
assertThat(bl.update(config)).isEqualTo(Constants.FALSE)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
@Test
@DisableOnCi
fun `Update custom bitlink`() {
val bl = bitly.bitlinks()
assertEquals(
Constants.TRUE,
bl.updateCustom("https://thauv.in/2NwtljT", "thauv.in/2NwtljT")
)
}
}
@Nested
@DisplayName("Validation Tests")
inner class ValidationTests {
@Test
fun `Empty URL should not shorten`() {
assertEquals(Constants.EMPTY, bitly.bitlinks().shorten(Constants.EMPTY))
}
@Test
fun `Short URL should not shorten`() {
assertEquals(shortUrl, bitly.bitlinks().shorten(shortUrl))
}
@Test
fun `Token not specified with API call`() {
assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
Utils.call("", "foo")
}
}
@Test
@DisableOnCi
fun `Token not specified`() {
val test = Bitly()
assertFailsWith(IllegalArgumentException::class) {
test.bitlinks().shorten(longUrl)
}
}
@Test
@EnabledIfEnvironmentVariable(named = "CI", matches = "true")
fun `Token not specified on CI`() {
val test = Bitly(Constants.EMPTY) // to void picking up the environment variable
assertFailsWith(IllegalArgumentException::class) {
test.bitlinks().shorten(longUrl)
}
}
@Test
fun `Token should be valid`() {
val test = Bitly().apply { accessToken = "12345679" }
assertEquals(
"{\"message\":\"FORBIDDEN\"}",
test.bitlinks().shorten("https://erik.thauvin.net/blog", toJson = true)
)
}
}
}

View file

@ -31,24 +31,22 @@
package net.thauvin.erik.bitly
import DisableOnCi
import assertk.all
import assertk.assertThat
import assertk.assertions.*
import net.thauvin.erik.bitly.Utils.isValidUrl
import assertk.assertions.contains
import assertk.assertions.isTrue
import assertk.assertions.prop
import net.thauvin.erik.bitly.Utils.removeHttp
import net.thauvin.erik.bitly.Utils.toEndPoint
import net.thauvin.erik.bitly.config.CreateConfig
import net.thauvin.erik.bitly.config.UpdateConfig
import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
import net.thauvin.erik.bitly.config.deeplinks.enums.Os
import org.json.JSONObject
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import java.io.File
import java.util.logging.Level
import kotlin.test.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class BitlyTest {
private val bitly = with(File("local.properties")) {
@ -58,82 +56,24 @@ class BitlyTest {
Bitly()
}
}
private val longUrl = "https://erik.thauvin.net/blog"
private val shortUrl = "https://bit.ly/380ojFd"
@Test
fun `token should be specified`() {
val test = Bitly()
if (System.getenv("CI") == "true") {
test.accessToken = Constants.EMPTY
companion object {
@JvmStatic
@BeforeAll
fun before() {
with(Utils.logger) {
level = Level.FINE
}
assertFailsWith(IllegalArgumentException::class) {
test.bitlinks().shorten(longUrl)
}
assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
Utils.call("", "foo")
}
}
@Test
fun `token should be valid`() {
val test = Bitly().apply { accessToken = "12345679" }
assertEquals(
"{\"message\":\"FORBIDDEN\"}",
test.bitlinks().shorten("https://erik.thauvin.net/blog", toJson = true)
)
}
@Nested
@DisplayName("API Call Tests")
inner class ApiCallTests {
@Test
fun `long url should be valid`() {
assertEquals(Constants.EMPTY, bitly.bitlinks().shorten(Constants.EMPTY))
}
@Test
fun `long url should not be short`() {
assertEquals(shortUrl, bitly.bitlinks().shorten(shortUrl))
}
@Test
fun `endPoint should be specified`() {
assertFailsWith(IllegalArgumentException::class, "bitly.call()") {
bitly.call("")
}
assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
Utils.call("1234568", "")
}
}
@Test
fun `endPoint conversion`() {
assertThat(Constants.API_BASE_URL.toEndPoint()).isEqualTo(Constants.API_BASE_URL)
assertThat("path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
assertThat("/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
}
@Test
fun `shorten = expand`() {
val shortUrl = bitly.bitlinks().shorten(longUrl, domain = "bit.ly")
assertEquals(longUrl, bitly.bitlinks().expand(shortUrl))
}
@Test
fun `shorten as json`() {
assertTrue(bitly.bitlinks().shorten(longUrl, toJson = true).startsWith("{\"created_at\":"))
}
@Test
fun `get user`() {
assertThat(bitly.call("user", method = Methods.GET), "call(user)")
.prop(CallResponse::isSuccessful).isTrue()
assertThat(Utils.call(bitly.accessToken, "user".toEndPoint(), method = Methods.GET), "call(/user)").all {
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::body).contains("login")
}
}
@Test
fun `created by`() {
fun `Created by`() {
assertEquals(
"ethauvin",
JSONObject(
@ -146,178 +86,26 @@ class BitlyTest {
}
@Test
fun `bitlinks shorten`() {
assertEquals(shortUrl, Bitlinks(bitly.accessToken).shorten(longUrl, domain = "bit.ly"))
fun `EndPoint should be specified`() {
assertFailsWith(IllegalArgumentException::class, "bitly.call()") {
bitly.call("")
}
assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
Utils.call("1234568", "")
}
}
@Test
fun `bitlinks expand`() {
assertEquals(longUrl, Bitlinks(bitly.accessToken).expand(shortUrl))
}
@Test
fun `bitlinks lastCallResponse`() {
val bl = Bitlinks(bitly.accessToken)
bl.shorten(longUrl, domain = "bit.ly")
assertThat(bl.lastCallResponse, "shorten(longUrl)").all {
fun `Get user`() {
assertThat(bitly.call("user", method = Methods.GET), "call(user)")
.prop(CallResponse::isSuccessful).isTrue()
assertThat(
Utils.call(
bitly.accessToken, "user".toEndPoint(), method = Methods.GET
), "call(/user)"
).all {
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::statusCode).isEqualTo(200)
prop(CallResponse::body).contains("\"link\":\"$shortUrl\"")
prop(CallResponse::message).isEmpty()
}
bl.shorten(shortUrl)
assertThat(bl.lastCallResponse, "shorten(shortUrl)").all {
prop(CallResponse::isSuccessful).isFalse()
prop(CallResponse::statusCode).isEqualTo(400)
prop(CallResponse::isBadRequest).isTrue()
prop(CallResponse::message).isEqualTo("ALREADY_A_BITLY_LINK")
prop(CallResponse::description).isEqualTo("The value provided is invalid.")
}
}
@Test
fun `clicks summary`() {
val bl = bitly.bitlinks()
assertThat(bl.clicks(shortUrl)).isNotEqualTo(Constants.EMPTY)
val clicks = bl.clicks(shortUrl, unit = Units.MONTH, units = 1)
assertThat(bl.lastCallResponse).all {
prop(CallResponse::isSuccessful).isTrue()
prop(CallResponse::statusCode).isEqualTo(200)
prop(CallResponse::description).isEmpty()
}
assertThat(clicks.toInt()).isGreaterThanOrEqualTo(0)
}
@Test
fun `create bitlink`() {
assertThat(bitly.bitlinks().create(long_url = longUrl), "create(longUrl)")
.matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
assertEquals(
shortUrl,
bitly.bitlinks().create(
domain = "bit.ly",
title = "Erik's Blog",
tags = arrayOf("erik", "thauvin", "blog", "weblog"),
long_url = longUrl
)
)
}
@Test
fun `create bitlink with config`() {
var config = CreateConfig.Builder(longUrl).build()
assertThat(bitly.bitlinks().create(config), "create(config)")
.matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
config = CreateConfig.Builder(longUrl)
.domain("bit.ly")
.title("Erik's Blog")
.tags(arrayOf("erik", "thauvin", "blog", "weblog"))
.build()
assertEquals(
shortUrl,
bitly.bitlinks().create(config)
)
}
@Test
fun `create bitlink with deeplinks`() {
val bl = bitly.bitlinks()
val dl = CreateDeeplinks().apply {
install_type(InstallType.NO_INSTALL)
app_uri_path("/store?id=123456")
install_url("https://play.google.com/store/apps/details?id=com.bitly.app&hl=en_US")
}
val config = CreateConfig.Builder(longUrl)
.domain("bit.ly")
.deeplinks(dl)
.build()
assertThat(bl.create(config)).isEqualTo(Constants.EMPTY)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
@Test
fun `shorten with invalid domain`() {
val bl = bitly.bitlinks()
bl.shorten("https://www.examples.com", domain = "foo.com")
assertThat(bl.lastCallResponse).all {
prop(CallResponse::isSuccessful).isFalse()
prop(CallResponse::isBadRequest).isTrue()
prop(CallResponse::message).isEqualTo("INVALID_ARG_DOMAIN")
prop(CallResponse::description).contains("invalid")
}
}
@Test
@DisableOnCi
fun `update custom bitlink`() {
val bl = bitly.bitlinks()
assertEquals(
Constants.TRUE,
bl.updateCustom("https://thauv.in/2NwtljT", "thauv.in/2NwtljT")
)
}
@Test
fun `update bitlink`() {
val bl = bitly.bitlinks()
assertEquals(
Constants.TRUE,
bl.update(shortUrl, title = "Erik's Weblog", tags = arrayOf("blog", "weblog"), archived = true)
)
assertThat(bl.update(shortUrl, tags = emptyArray(), toJson = true), "update(tags)")
.contains("\"tags\":[]")
}
@Test
fun `update bitlink with config`() {
val bl = bitly.bitlinks()
var config = UpdateConfig.Builder(shortUrl)
.title("Erik's Weblog")
.tags(arrayOf("blog", "weblog"))
.archived(true)
.build()
assertEquals(Constants.TRUE, bl.update(config))
config = UpdateConfig.Builder(shortUrl)
.toJson(true)
.build()
assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]")
}
@Test
fun `update bitlink with deeplinks`() {
val bl = bitly.bitlinks()
val dl = UpdateDeeplinks().apply {
os(Os.ANDROID)
brand_guid("Ba1bc23dE4F")
}
val config = UpdateConfig.Builder(shortUrl)
.deeplinks(dl)
.build()
assertThat(bl.update(config)).isEqualTo(Constants.FALSE)
assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
}
@Test
fun `validate URL`() {
assertTrue("https://www.example.com".isValidUrl(), "valid url")
assertFalse("this is a test".isValidUrl(), "invalid url")
}
companion object {
@JvmStatic
@BeforeAll
fun before() {
with(Utils.logger) {
level = Level.FINE
prop(CallResponse::body).contains("login")
}
}
}

View file

@ -0,0 +1,114 @@
/*
* UtilsTests.kt
*
* Copyright 2020-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.bitly
import assertk.assertThat
import assertk.assertions.isEqualTo
import net.thauvin.erik.bitly.Utils.isValidUrl
import net.thauvin.erik.bitly.Utils.removeHttp
import net.thauvin.erik.bitly.Utils.toEndPoint
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class UtilsTests {
@Nested
@DisplayName("Endpoint Conversion Tests")
inner class EndpointConversionTests {
@Test
fun `Convert endpoint with empty string`() {
assertThat("".toEndPoint()).isEqualTo("")
}
@Test
fun `Convert endpoint with full URL`() {
assertThat("https://example.com/path".toEndPoint()).isEqualTo("https://example.com/path")
}
@Test
fun `Convert endpoint with leading slash`() {
assertThat("/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
}
@Test
fun `Convert endpoint with multiple path segments`() {
assertThat("/existing/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/existing/path")
}
@Test
fun `Convert endpoint with no leading slash`() {
assertThat("path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
}
@Test
fun `Convert endpoint with trailing slash`() {
assertThat("path/".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path/")
}
}
@Nested
@DisplayName("Remote HTTP Tests")
inner class RemoteHTTPTests {
@Test
@Suppress("HttpUrlsUsage")
fun `Remove HTTP`() {
assertThat("http://example.com".removeHttp()).isEqualTo("example.com")
}
@Test
fun `Remove HTTPS`() {
assertThat("https://example.com".removeHttp()).isEqualTo("example.com")
}
@Test
fun `Remove mixed case`() {
assertThat("HtTPs://EXAMPLE.Com".removeHttp()).isEqualTo("EXAMPLE.Com")
}
@Test
fun `Remove no scheme`() {
assertThat("example.com".removeHttp()).isEqualTo("example.com")
}
}
@Test
fun `Validate invalid URL`() {
assertFalse("this is a test".isValidUrl(), "invalid url")
}
@Test
fun `Validate URL`() {
assertTrue("https://www.example.com".isValidUrl(), "valid url")
}
}

View file

@ -31,49 +31,69 @@
package net.thauvin.erik.bitly.config
import assertk.all
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isTrue
import assertk.assertions.prop
import net.thauvin.erik.bitly.Constants
import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
import net.thauvin.erik.bitly.config.deeplinks.enums.Os
import org.json.JSONObject
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import kotlin.test.Test
class ConfigTest {
@Nested
@DisplayName("Build Configuration Tests")
inner class BuildConfigurationTests {
@Test
fun `create config test`() {
fun `Build create configuration`() {
val deeplinks = CreateDeeplinks().apply {
app_id("app_id")
install_type(InstallType.AUTO_INSTALL)
}
val config = CreateConfig.Builder("long_url")
.deeplinks(deeplinks)
.domain("domain")
.groupGuid("group_guid")
.title("title")
.tags(arrayOf("tag", "tag2"))
.deeplinks(deeplinks)
.title("title")
.build()
assertThat(config).all {
prop(CreateConfig::deeplinks).isEqualTo(deeplinks)
prop(CreateConfig::domain).isEqualTo("domain")
prop(CreateConfig::group_guid).isEqualTo("group_guid")
prop(CreateConfig::long_url).isEqualTo("long_url")
prop(CreateConfig::tags).isEqualTo(arrayOf("tag", "tag2"))
prop(CreateConfig::title).isEqualTo("title")
prop(CreateConfig::toJson).isEqualTo(false)
}
val map = mapOf(
"long_url" to config.long_url,
"deeplinks" to arrayOf(deeplinks.links()),
"domain" to config.domain,
"group_guid" to config.group_guid,
"title" to config.title,
"long_url" to config.long_url,
"tags" to config.tags,
"deeplinks" to arrayOf(deeplinks.links())
"title" to config.title
)
assertThat(JSONObject(map).toString()).isEqualTo(
"""
{"group_guid":"group_guid","long_url":"long_url","title":"title","deeplinks":[{"app_id":"app_id","install_type":"auto_install"}],"domain":"domain","tags":["tag","tag2"]}
{"group_guid":"group_guid","deeplinks":[{"app_id":"app_id","install_type":"auto_install"}],"long_url":"long_url","title":"title","domain":"domain","tags":["tag","tag2"]}
""".trimIndent()
)
}
}
@Test
fun `update config test`() {
fun `Build update configuration`() {
val deeplinks = UpdateDeeplinks().apply {
os(Os.IOS)
install_type(InstallType.PROMOTE_INSTALL)
@ -81,25 +101,123 @@ class ConfigTest {
}
val config = UpdateConfig.Builder("blink")
.title("title")
.archived(true)
.tags(arrayOf("tag", "tag2"))
.deeplinks(deeplinks)
.tags(arrayOf("tag", "tag2"))
.title("title")
.build()
assertThat(config).all {
prop(UpdateConfig::archived).isTrue()
prop(UpdateConfig::bitlink).isEqualTo("blink")
prop(UpdateConfig::deeplinks).isEqualTo(deeplinks)
prop(UpdateConfig::tags).isEqualTo(arrayOf("tag", "tag2"))
prop(UpdateConfig::title).isEqualTo("title")
prop(UpdateConfig::toJson).isEqualTo(false)
}
val map = mapOf(
"bitlink" to config.bitlink,
"title" to config.title,
"archived" to config.archived,
"bitlink" to config.bitlink,
"deeplinks" to arrayOf(deeplinks.links()),
"tags" to config.tags,
"deeplinks" to arrayOf(deeplinks.links())
"title" to config.title
)
assertThat(JSONObject(map).toString()).isEqualTo(
"""
{"archived":true,"bitlink":"blink","title":"title","deeplinks":[{"os":"ios","app_guid":"app_guid","install_type":"promote_install"}],"tags":["tag","tag2"]}
{"archived":true,"bitlink":"blink","deeplinks":[{"os":"ios","app_guid":"app_guid","install_type":"promote_install"}],"title":"title","tags":["tag","tag2"]}
""".trimIndent()
)
}
@Nested
@DisplayName("Validate Configuration Tests")
inner class ValidateConfigurationTests {
@Test
fun `Validate create configuration`() {
val deeplinks = CreateDeeplinks().apply {
app_id("app_id")
install_type(InstallType.AUTO_INSTALL)
}
val config = CreateConfig.Builder("long_url")
.deeplinks(deeplinks)
.domain("domain")
.groupGuid("group_guid")
.tags(arrayOf("tag", "tag2"))
.title("title")
.toJson(true)
assertThat(config).all {
prop(CreateConfig.Builder::deeplinks).prop(CreateDeeplinks::links).isEqualTo(deeplinks.links())
prop(CreateConfig.Builder::domain).isEqualTo("domain")
prop(CreateConfig.Builder::group_guid).isEqualTo("group_guid")
prop(CreateConfig.Builder::long_url).isEqualTo("long_url")
prop(CreateConfig.Builder::tags).isEqualTo(arrayOf("tag", "tag2"))
prop(CreateConfig.Builder::title).isEqualTo("title")
prop(CreateConfig.Builder::toJson).isTrue()
}
config.longUrl("longer_url")
assertThat(config).prop(CreateConfig.Builder::long_url).isEqualTo("longer_url")
}
@Test
fun `Validate create default configuration`() {
val config = CreateConfig.Builder("long_url")
assertThat(config).all {
prop(CreateConfig.Builder::long_url).isEqualTo("long_url")
prop(CreateConfig.Builder::domain).isEqualTo(Constants.EMPTY)
prop(CreateConfig.Builder::group_guid).isEqualTo(Constants.EMPTY)
prop(CreateConfig.Builder::title).isEqualTo(Constants.EMPTY)
prop(CreateConfig.Builder::tags).isEqualTo(emptyArray())
prop(CreateConfig.Builder::deeplinks).prop(CreateDeeplinks::links).isEqualTo(CreateDeeplinks().links())
prop(CreateConfig.Builder::toJson).isEqualTo(false)
}
}
@Test
fun `Validate update configuration`() {
val deeplinks = UpdateDeeplinks().apply {
os(Os.IOS)
install_type(InstallType.PROMOTE_INSTALL)
app_guid("app_guid")
}
val config = UpdateConfig.Builder("bitlink")
.title("title")
.archived(true)
.tags(arrayOf("tag", "tag2"))
.deeplinks(deeplinks)
.toJson(true)
assertThat(config).all {
prop(UpdateConfig.Builder::bitlink).isEqualTo("bitlink")
prop(UpdateConfig.Builder::title).isEqualTo("title")
prop(UpdateConfig.Builder::archived).isTrue()
prop(UpdateConfig.Builder::tags).isEqualTo(arrayOf("tag", "tag2"))
prop(UpdateConfig.Builder::deeplinks).isEqualTo(deeplinks)
prop(UpdateConfig.Builder::toJson).isTrue()
}
config.bitlink("blink")
assertThat(config).prop(UpdateConfig.Builder::bitlink).isEqualTo("blink")
}
@Test
fun `Validate update default configuration`() {
val config = UpdateConfig.Builder("bitlink")
assertThat(config).all {
prop(UpdateConfig.Builder::bitlink).isEqualTo("bitlink")
prop(UpdateConfig.Builder::title).isEqualTo(Constants.EMPTY)
prop(UpdateConfig.Builder::archived).isEqualTo(false)
prop(UpdateConfig.Builder::tags).isEqualTo(emptyArray())
prop(UpdateConfig.Builder::deeplinks).prop(UpdateDeeplinks::links).isEqualTo(UpdateDeeplinks().links())
prop(UpdateConfig.Builder::toJson).isEqualTo(false)
}
}
}
}

View file

@ -31,11 +31,11 @@
package net.thauvin.erik.bitly.config.deeplinks
import assertk.all
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.doesNotContain
import assertk.assertions.isEqualTo
import assertk.assertions.isNull
import assertk.assertions.prop
import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
import net.thauvin.erik.bitly.config.deeplinks.enums.Os
import org.json.JSONObject
@ -45,88 +45,92 @@ import java.time.ZonedDateTime
class DeeplinksTest {
@Test
fun `create test`() {
fun `Create deeplink`() {
val deeplinks = CreateDeeplinks().apply {
app_uri_path("app_uri_path")
install_type(InstallType.NO_INSTALL)
}
assertThat(deeplinks.install_url()).isNull()
deeplinks.install_url("install_url")
assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path")
assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL)
assertThat(deeplinks).all {
prop(CreateDeeplinks::app_id).isNull()
prop(CreateDeeplinks::app_uri_path).isEqualTo("app_uri_path")
prop(CreateDeeplinks::install_type).isEqualTo(InstallType.NO_INSTALL)
prop(CreateDeeplinks::install_url).isNull()
prop(CreateDeeplinks::links).isEqualTo(deeplinks.links())
}
assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
"""
{"app_uri_path":"app_uri_path","install_type":"no_install","install_url":"install_url"}
{"app_uri_path":"app_uri_path","install_type":"no_install"}
""".trimIndent()
)
deeplinks.install_type(InstallType.PROMOTE_INSTALL)
deeplinks.app_id("app_id")
deeplinks.install_type(InstallType.PROMOTE_INSTALL)
deeplinks.install_url("install_url")
assertThat(deeplinks.app_id()).isEqualTo("app_id")
assertThat(JSONObject(deeplinks.links()).toString()).apply {
doesNotContain(InstallType.NO_INSTALL.type)
contains(InstallType.PROMOTE_INSTALL.type)
contains("\"app_id\":\"app_id\"")
assertThat(deeplinks).all {
prop(CreateDeeplinks::app_id).isEqualTo("app_id")
prop(CreateDeeplinks::install_type).isEqualTo(InstallType.PROMOTE_INSTALL)
prop(CreateDeeplinks::install_url).isEqualTo("install_url")
}
assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
"""
{"install_url":"install_url","app_id":"app_id","app_uri_path":"app_uri_path","install_type":"promote_install"}
""".trimIndent()
)
}
@Test
fun `update test`() {
fun `Update deeplink`() {
val deeplinks = UpdateDeeplinks().apply {
app_guid("app_guid")
os(Os.IOS)
install_type(InstallType.NO_INSTALL)
guid("guid")
install_url("install_url")
app_uri_path("app_uri_path")
created("created")
guid("guid")
install_type(InstallType.NO_INSTALL)
install_url("install_url")
modified("modified")
os(Os.IOS)
}
assertThat(deeplinks.brand_guid()).isNull()
assertThat(deeplinks).all {
prop(UpdateDeeplinks::app_guid).isEqualTo("app_guid")
prop(UpdateDeeplinks::app_uri_path).isEqualTo("app_uri_path")
prop(UpdateDeeplinks::bitlink).isNull()
prop(UpdateDeeplinks::brand_guid).isNull()
prop(UpdateDeeplinks::created).isEqualTo("created")
prop(UpdateDeeplinks::guid).isEqualTo("guid")
prop(UpdateDeeplinks::install_type).isEqualTo(InstallType.NO_INSTALL)
prop(UpdateDeeplinks::install_url).isEqualTo("install_url")
prop(UpdateDeeplinks::links).isEqualTo(deeplinks.links())
prop(UpdateDeeplinks::modified).isEqualTo("modified")
prop(UpdateDeeplinks::os).isEqualTo(Os.IOS)
}
val zdt = ZonedDateTime.of(1997, 8, 29, 2, 14, 0, 0, ZoneId.of("US/Eastern"))
deeplinks.bitlink("bitlink")
deeplinks.brand_guid("brand_guid")
deeplinks.created(zdt)
deeplinks.install_type(InstallType.PROMOTE_INSTALL)
deeplinks.modified(zdt)
deeplinks.os(Os.ANDROID)
assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path")
assertThat(deeplinks.install_url()).isEqualTo("install_url")
assertThat(deeplinks.os()).isEqualTo(Os.IOS)
assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL)
assertThat(deeplinks.app_guid()).isEqualTo("app_guid")
assertThat(deeplinks.modified()).isEqualTo("modified")
assertThat(deeplinks.brand_guid()).isEqualTo("brand_guid")
assertThat(deeplinks).all {
prop(UpdateDeeplinks::bitlink).isEqualTo("bitlink")
prop(UpdateDeeplinks::brand_guid).isEqualTo("brand_guid")
prop(UpdateDeeplinks::created).isEqualTo("1997-08-29T02:14:00-0400")
prop(UpdateDeeplinks::install_type).isEqualTo(InstallType.PROMOTE_INSTALL)
prop(UpdateDeeplinks::modified).isEqualTo("1997-08-29T02:14:00-0400")
prop(UpdateDeeplinks::os).isEqualTo(Os.ANDROID)
}
assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
"""
{"app_guid":"app_guid","install_url":"install_url","os":"ios","app_uri_path":"app_uri_path","created":"created","brand_guid":"brand_guid","guid":"guid","modified":"modified","install_type":"no_install"}
{"app_guid":"app_guid","install_url":"install_url","bitlink":"bitlink","os":"android","app_uri_path":"app_uri_path","created":"1997-08-29T02:14:00-0400","brand_guid":"brand_guid","guid":"guid","modified":"1997-08-29T02:14:00-0400","install_type":"promote_install"}
""".trimIndent()
)
deeplinks.install_type(InstallType.PROMOTE_INSTALL)
deeplinks.os(Os.ANDROID)
deeplinks.bitlink("bitlink")
val zdt = ZonedDateTime.of(1997, 8, 29, 2, 14, 0, 0, ZoneId.of("US/Eastern"))
deeplinks.modified(zdt)
deeplinks.created(zdt)
assertThat(deeplinks.bitlink()).isEqualTo("bitlink")
assertThat(deeplinks.created()).isEqualTo("1997-08-29T02:14:00-0400")
assertThat(deeplinks.modified()).isEqualTo("1997-08-29T02:14:00-0400")
assertThat(JSONObject(deeplinks.links()).toString()).apply {
doesNotContain(InstallType.NO_INSTALL.type)
contains(InstallType.PROMOTE_INSTALL.type)
doesNotContain(Os.IOS.type)
contains("\"os\":\"android\"")
contains("\"bitlink\":\"bitlink\"")
}
}
}