diff --git a/scalabot-app/src/main/kotlin/AppConfigs.kt b/scalabot-app/src/main/kotlin/AppConfigs.kt index 355c28d..b692e64 100644 --- a/scalabot-app/src/main/kotlin/AppConfigs.kt +++ b/scalabot-app/src/main/kotlin/AppConfigs.kt @@ -238,6 +238,7 @@ private object GsonConst { .registerTypeAdapter(ProxyType::class.java, ProxyTypeSerializer) .registerTypeAdapter(BotConfig::class.java, BotConfigSerializer) .registerTypeAdapter(Artifact::class.java, ArtifactSerializer) + .registerTypeAdapter(BotAccount::class.java, BotAccountSerializer) .create() } diff --git a/scalabot-meta/src/main/kotlin/serializer/Serializer.kt b/scalabot-meta/src/main/kotlin/serializer/Serializer.kt index 9b55257..af67722 100644 --- a/scalabot-meta/src/main/kotlin/serializer/Serializer.kt +++ b/scalabot-meta/src/main/kotlin/serializer/Serializer.kt @@ -13,6 +13,7 @@ import org.eclipse.aether.util.repository.AuthenticationBuilder import java.lang.reflect.Type import java.net.MalformedURLException import java.net.URL +import java.util.regex.Pattern object ProxyTypeSerializer : JsonDeserializer, JsonSerializer { @@ -261,3 +262,39 @@ object BotConfigSerializer : JsonSerializer, JsonDeserializer { + + private val tokenCheckRegex = Pattern.compile("\\d{9}:[a-zA-Z\\d_-]{35}") + + override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): BotAccount { + if (json == null || json.isJsonNull) { + throw JsonParseException("Missing `account` field.") + } else if (!json.isJsonObject) { + throw JsonParseException("Invalid `account` field type.") + } + val jsonObj = json.asJsonObject + + val name = jsonObj.getPrimitiveValueOrThrow("name").asString + val token = jsonObj.getPrimitiveValueOrThrow("token").asString.let { + if (it.isEmpty()) { + throw JsonParseException("`token` cannot be empty.") + } else if (!tokenCheckRegex.matcher(it).matches()) { + throw JsonParseException("`token` is invalid.") + } else { + it + } + } + val creatorId = try { + jsonObj.getPrimitiveValueOrThrow("creatorId").asLong + } catch (e: NumberFormatException) { + throw JsonParseException("`creatorId` must be a number.") + }.apply { + if (this < 0) { + throw JsonParseException("`creatorId` must be a positive number.") + } + } + + return BotAccount(name, token, creatorId) + } + +} diff --git a/scalabot-meta/src/test/kotlin/serializer/SerializeUtilsTest.kt b/scalabot-meta/src/test/kotlin/serializer/SerializeUtilsTest.kt index 6029775..90c3b89 100644 --- a/scalabot-meta/src/test/kotlin/serializer/SerializeUtilsTest.kt +++ b/scalabot-meta/src/test/kotlin/serializer/SerializeUtilsTest.kt @@ -692,3 +692,161 @@ internal class UsernameAuthenticatorSerializerTest { } } + +internal class BotAccountSerializerTest { + + private val expectToken = "123456789:AAHErDroUTznQsOd_oZPJ6cQEj4Z5mGHO10" + private val gson = GsonBuilder() + .registerTypeAdapter(BotAccount::class.java, BotAccountSerializer) + .create() + + @Test + fun `Invalid json type check test`() { + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(null, null, null) + } + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonNull.INSTANCE, null, null) + } + + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonPrimitive("A STRING"), null, null) + } + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonArray(), null, null) + } + } + + @Test + fun `Field missing test`() { + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonObject(), null, null) + } + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonObject().apply { + addProperty("token", expectToken) + addProperty("creatorId", 1) + }, null, null) + } + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonObject().apply { + addProperty("name", "testUser") + addProperty("creatorId", 1) + }, null, null) + } + assertThrows(JsonParseException::class.java) { + BotAccountSerializer.deserialize(JsonObject().apply { + addProperty("name", "testUser") + addProperty("token", expectToken) + }, null, null) + } + + val account = BotAccountSerializer.deserialize(JsonObject().apply { + addProperty("name", "testUser") + addProperty("token", expectToken) + addProperty("creatorId", 1) + }, null, null) + assertNotNull(account) + assertEquals("testUser", account.name) + assertEquals(expectToken, account.token) + assertEquals(1, account.creatorId) + } + + @Test + fun `'token' check test`() { + val jsonObject = JsonObject().apply { + addProperty("name", "testUser") + addProperty("token", expectToken) + addProperty("creatorId", 1) + } + + val looksGoodAccount = BotAccountSerializer.deserialize(jsonObject, null, null) + + assertNotNull(looksGoodAccount) + assertEquals("testUser", looksGoodAccount.name) + assertEquals(expectToken, looksGoodAccount.token) + assertEquals(1, looksGoodAccount.creatorId) + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("token", "") + }, null, null) + fail("Token 为空,但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`token` cannot be empty.", e.message) + } + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("token", "abcdefghijklmnopqrstuvwxyz") + }, null, null) + fail("Token 格式错误(基本格式错误),但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`token` is invalid.", e.message) + } + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("token", "abcdefgh:ijklmnopqrstuvwxyz-1234567890_abcde") + }, null, null) + fail("Token 格式错误(ID 不为数字),但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`token` is invalid.", e.message) + } + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("token", "0123456789:ijklmnopqrstu-vwxyz_123456") + }, null, null) + fail("Token 格式错误(授权令牌长度错误),但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`token` is invalid.", e.message) + } + } + + @Test + fun `'creatorId' check test`() { + val jsonObject = JsonObject().apply { + addProperty("name", "testUser") + addProperty("token", expectToken) + addProperty("creatorId", 1) + } + + val looksGoodAccount = gson.fromJson(jsonObject, BotAccount::class.java) + assertEquals(1, looksGoodAccount.creatorId) + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("creatorId", "A STRING") + }, null, null) + fail("creatorId 不是一个数字,但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`creatorId` must be a number.", e.message) + } + + try { + BotAccountSerializer.deserialize(jsonObject.deepCopy().apply { + addProperty("creatorId", -1) + }, null, null) + fail("creatorId 不能为负数,但是没有抛出异常。") + } catch (e: JsonParseException) { + assertEquals("`creatorId` must be a positive number.", e.message) + } + } + + @Test + fun `json deserialize test`() { + val jsonObject = JsonObject().apply { + addProperty("name", "testUser") + addProperty("token", expectToken) + addProperty("creatorId", 1) + } + + val looksGoodAccount = gson.fromJson(jsonObject, BotAccount::class.java) + + assertNotNull(looksGoodAccount) + assertEquals("testUser", looksGoodAccount.name) + assertEquals(expectToken, looksGoodAccount.token) + assertEquals(1, looksGoodAccount.creatorId) + } +}