fixed all unit tests

also added a new one I noticed was missing
This commit is contained in:
kageru 2019-06-14 23:16:31 +02:00
parent 3d80540c03
commit 120079ba3d
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
10 changed files with 261 additions and 100 deletions

View File

@ -11,7 +11,7 @@ import java.io.File
class Kagebot {
companion object {
fun processMessage(event: MessageCreateEvent) {
if (event.messageAuthor.isYourself) {
if (event.messageAuthor.isBotUser) {
return
}
for (command in Globals.config.commands) {

View File

@ -27,7 +27,7 @@ object Util {
fun hasOneOf(messageAuthor: MessageAuthor, roles: Set<Role>): Boolean {
return messageAuthor.asUser().ifNotEmpty { user ->
user.getRoles(server).map { it }.toSet().intersect(roles).isNotEmpty()
user.getRoles(server).toSet().intersect(roles).isNotEmpty()
} ?: false
}

View File

@ -13,12 +13,13 @@ class Config(rawConfig: RawConfig) {
?: throw IllegalArgumentException("No [system] block in config.")
var localization: Localization = rawConfig.localization?.let(::Localization)
?: throw IllegalArgumentException("No [localization] block in config.")
var commands: List<Command> = rawConfig.commands?.map(::Command) ?: emptyList()
var features: Features = rawConfig.features?.let(::Features)
?: throw IllegalArgumentException("No [feature] block in config.")
var commands: List<Command>
var features: Features
init {
Globals.server = api.getServerById(system.serverId).orElseThrow()
this.commands = rawConfig.commands?.map(::Command) ?: emptyList()
this.features = rawConfig.features?.let(::Features) ?: Features.NONE
}
fun reloadLocalization(rawLocalization: RawLocalization) {

View File

@ -7,8 +7,13 @@ import moe.kageru.kagebot.config.RawWelcomeFeature
import org.javacord.api.entity.channel.TextChannel
import org.javacord.api.entity.message.embed.EmbedBuilder
class Features(rawFeatures: RawFeatures) {
val welcome: WelcomeFeature? = rawFeatures.welcome?.let { WelcomeFeature(it) }
class Features(val welcome: WelcomeFeature?) {
constructor(rawFeatures: RawFeatures) : this(rawFeatures.welcome?.let(::WelcomeFeature))
companion object {
val NONE = Features(null)
}
}
class WelcomeFeature(rawWelcome: RawWelcomeFeature) {

View File

@ -61,6 +61,12 @@ response = "access granted"
[command.permissions]
hasNoneOf = ["452034011393425409"]
[[command]]
trigger = "!private"
response = "some long response that you don’t want in public channels"
[command.permissions]
onlyDM = true
# redirect every message that starts with !redirect to channel 555097559023222825
[[command]]
trigger = "!redirect"

View File

@ -1,92 +1,207 @@
package moe.kageru.kagebot
import io.kotlintest.matchers.string.shouldContain
import io.kotlintest.matchers.string.shouldNotContain
import io.kotlintest.shouldBe
import io.kotlintest.specs.StringSpec
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import moe.kageru.kagebot.Globals.config
import moe.kageru.kagebot.TestUtil.embedToString
import moe.kageru.kagebot.TestUtil.messageableAuthor
import moe.kageru.kagebot.TestUtil.mockMessage
import moe.kageru.kagebot.TestUtil.testMessageSuccess
import moe.kageru.kagebot.TestUtil.withConfig
import org.javacord.api.entity.message.embed.EmbedBuilder
import java.util.*
class CommandTest : StringSpec({
TestUtil.prepareServerConfig()
TestUtil.prepareTestEnvironment()
"should match prefix command" {
testMessageSuccess("!ping", "pong")
withConfig(
"""
[[command]]
trigger = "!ping"
response = "pong"
""".trimIndent()
) {
testMessageSuccess("!ping", "pong")
}
}
"should match contains command" {
testMessageSuccess("the trigger is somewhere in this message", "found it")
withConfig(
"""
[[command]]
trigger = "somewhere"
response = "found it"
matchType = "CONTAINS"
""".trimIndent()
) {
testMessageSuccess("the trigger is somewhere in this message", "found it")
}
}
"should match regex command" {
testMessageSuccess("AcsdB", "regex matched")
withConfig(
"""
[[command]]
trigger = "A.+B"
response = "regex matched"
matchType = "REGEX"
""".trimIndent()
) {
testMessageSuccess("AcsdB", "regex matched")
}
}
"should ping author" {
testMessageSuccess("answer me", "<@1> there you go")
withConfig(
"""
[[command]]
trigger = "answer me"
response = "@@ there you go"
""".trimIndent()
) {
testMessageSuccess("answer me", "<@1> there you go")
}
}
"should not react to own message" {
val calls = mutableListOf<String>()
Kagebot.processMessage(mockMessage("!ping", replies = calls, isSelf = true))
calls shouldBe mutableListOf()
withConfig(
"""
[[command]]
trigger = "!ping"
response = "pong"
""".trimIndent()
) {
val calls = mutableListOf<String>()
Kagebot.processMessage(mockMessage("!ping", replies = calls, isBot = true))
calls shouldBe mutableListOf()
}
}
"should delete messages and send copy to author" {
val replies = mutableListOf<EmbedBuilder>()
val messageContent = "delet this"
val mockMessage = mockMessage(messageContent)
every { mockMessage.deleteMessage() } returns mockk()
every { mockMessage.messageAuthor.asUser() } returns messageableAuthor(replies)
Kagebot.processMessage(mockMessage)
verify(exactly = 1) { mockMessage.deleteMessage() }
replies.size shouldBe 1
embedToString(replies[0]) shouldContain messageContent
withConfig(
"""
[[command]]
trigger = "delet this"
[command.action]
delete = true
""".trimIndent()
) {
val replies = mutableListOf<EmbedBuilder>()
val messageContent = "delet this"
val mockMessage = mockMessage(messageContent)
every { mockMessage.deleteMessage() } returns mockk()
every { mockMessage.messageAuthor.asUser() } returns Optional.of(messageableAuthor(replies))
Kagebot.processMessage(mockMessage)
verify(exactly = 1) { mockMessage.deleteMessage() }
replies.size shouldBe 1
embedToString(replies[0]) shouldContain messageContent
}
}
"should refuse command without permissions" {
val replies = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = replies)
every { mockMessage.messageAuthor.asUser() } returns messageableAuthor()
Kagebot.processMessage(mockMessage)
replies shouldBe mutableListOf(config.localization.permissionDenied)
withConfig(
"""
[[command]]
trigger = "!restricted"
response = "access granted"
[command.permissions]
hasOneOf = [
"testrole",
]
""".trimIndent()
) {
val replies = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = replies)
every { mockMessage.messageAuthor.asUser() } returns Optional.of(messageableAuthor())
Kagebot.processMessage(mockMessage)
replies shouldBe mutableListOf(config.localization.permissionDenied)
}
}
"should accept restricted command for owner" {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = calls)
every { mockMessage.messageAuthor.isBotOwner } returns true
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf("access granted")
withConfig(
"""
[[command]]
trigger = "!restricted"
response = "access granted"
[command.permissions]
hasOneOf = [
"testrole"
]
""".trimIndent()
) {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = calls)
every { mockMessage.messageAuthor.isBotOwner } returns true
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf("access granted")
}
}
"should accept restricted command with permissions" {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = calls)
every { mockMessage.messageAuthor.asUser() } returns mockk {
every { isPresent } returns true
every { get().getRoles(any()) } returns listOf(
mockk { every { id } returns 452034011393425409 }
)
withConfig(
"""
[[command]]
trigger = "!restricted"
response = "access granted"
[command.permissions]
hasOneOf = [
"testrole"
]
""".trimIndent()
) {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!restricted", replies = calls)
every { mockMessage.messageAuthor.asUser() } returns Optional.of(mockk {
every { getRoles(any()) } returns listOf(
Globals.server.getRolesByNameIgnoreCase("testrole")[0]
)
})
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf("access granted")
}
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf("access granted")
}
"should deny command to excluded roles" {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!almostUnrestricted", replies = calls)
// with the banned role
every { mockMessage.messageAuthor.asUser() } returns mockk {
every { isPresent } returns true
every { get().getRoles(any()) } returns listOf(
mockk { every { id } returns 452034011393425409 }
)
}
Kagebot.processMessage(mockMessage)
withConfig(
"""
[[command]]
trigger = "!almostUnrestricted"
response = "access granted"
[command.permissions]
hasNoneOf = ["testrole"]
""".trimIndent()
) {
val calls = mutableListOf<String>()
val mockMessage = mockMessage("!almostUnrestricted", replies = calls)
// with the banned role
every { mockMessage.messageAuthor.asUser() } returns mockk {
every { isPresent } returns true
every { get().getRoles(any()) } returns listOf(
Globals.server.getRolesByNameIgnoreCase("testrole")[0]
)
}
Kagebot.processMessage(mockMessage)
// without the role
every { mockMessage.messageAuthor.asUser() } returns mockk {
every { isPresent } returns true
every { get().getRoles(any()) } returns emptyList()
// without the role
every { mockMessage.messageAuthor.asUser() } returns mockk {
every { isPresent } returns true
every { get().getRoles(any()) } returns emptyList()
}
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf(config.localization.permissionDenied, "access granted")
}
}
"should refuse DM only message in server channel" {
withConfig(
"""
[[command]]
trigger = "!dm"
response = "access granted"
[command.permissions]
onlyDM = true
""".trimIndent()
) {
val calls = mutableListOf<String>()
Kagebot.processMessage(mockMessage("!dm", replies = calls))
calls shouldBe listOf(config.localization.permissionDenied)
}
Kagebot.processMessage(mockMessage)
calls shouldBe mutableListOf(config.localization.permissionDenied, "access granted")
}
/*
* This implicitly tests that the message author is not included in anonymous complaints
@ -94,10 +209,21 @@ class CommandTest : StringSpec({
*/
"should redirect" {
val calls = mutableListOf<EmbedBuilder>()
TestUtil.prepareServerConfig(calls)
val message = "this is a message"
Kagebot.processMessage(mockMessage("!anonRedirect $message"))
calls.size shouldBe 1
embedToString(calls[0]) shouldContain "\"$message\""
TestUtil.prepareTestEnvironment(calls)
withConfig(
"""
[[command]]
trigger = "!redirect"
response = "redirected"
[command.action.redirect]
target = "testchannel"
anonymous = true
""".trimIndent()
) {
val message = "this is a message"
Kagebot.processMessage(mockMessage("!redirect $message", replyEmbeds = calls))
calls.size shouldBe 1
embedToString(calls[0]) shouldContain "\"$message\""
}
}
})

View File

@ -1,17 +1,13 @@
package moe.kageru.kagebot
import io.kotlintest.shouldBe
import io.kotlintest.shouldNotBe
import io.kotlintest.specs.StringSpec
import moe.kageru.kagebot.config.RawConfig
class ConfigTest : StringSpec({
/*
"should properly parse default config" {
Config.config shouldNotBe null
Config.config.commands shouldNotBe null
}
*/
"should convert to raw config" {
RawConfig.config shouldNotBe null
TestUtil.prepareTestEnvironment()
"should properly parse test config" {
Globals.config shouldNotBe null
Globals.config.commands shouldBe emptyList()
}
})

View File

@ -31,7 +31,7 @@ class FeatureTest : StringSpec({
"should send welcome fallback if DMs are disabled" {
val dm = slot<String>()
val sentMessages = mutableListOf<EmbedBuilder>()
TestUtil.prepareServerConfig(sentMessages)
TestUtil.prepareTestEnvironment(sentMessages)
Kagebot.welcomeUser(
mockk {
every { user } returns mockk {

View File

@ -5,9 +5,11 @@ import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import moe.kageru.kagebot.config.Config
import moe.kageru.kagebot.config.RawConfig
import org.javacord.api.DiscordApi
import org.javacord.api.entity.channel.ServerTextChannel
import org.javacord.api.entity.message.embed.EmbedBuilder
import org.javacord.api.entity.server.Server
import org.javacord.api.entity.user.User
import org.javacord.api.event.message.MessageCreateEvent
import org.javacord.core.entity.message.embed.EmbedBuilderDelegateImpl
@ -18,40 +20,51 @@ object TestUtil {
content: String,
author: Long = 1,
replies: MutableList<String> = mutableListOf(),
isSelf: Boolean = false
replyEmbeds: MutableList<EmbedBuilder> = mutableListOf(),
isBot: Boolean = false
): MessageCreateEvent {
val message = mockk<MessageCreateEvent>()
every { message.messageContent } returns content
every { message.readableMessageContent } returns content
every { message.messageAuthor.id } returns author
every { message.channel.sendMessage(capture(replies)) } returns mockk()
every { message.messageAuthor.isYourself } returns isSelf
every { message.message.canYouDelete() } returns true
every { message.messageAuthor.isBotOwner } returns false
return message
return mockk {
every { messageContent } returns content
every { readableMessageContent } returns content
every { messageAuthor.id } returns author
every { channel.sendMessage(capture(replies)) } returns mockk()
every { channel.sendMessage(capture(replyEmbeds)) } returns mockk()
every { messageAuthor.isBotUser } returns isBot
every { message.canYouDelete() } returns true
every { messageAuthor.isBotOwner } returns false
every { isPrivateMessage } returns false
}
}
fun messageableAuthor(messages: MutableList<EmbedBuilder> = mutableListOf()): Optional<User> {
fun messageableAuthor(messages: MutableList<EmbedBuilder> = mutableListOf()): User {
return mockk {
every { getRoles(any()) } returns emptyList()
every { sendMessage(capture(messages)) } returns mockk()
}
}
fun prepareTestEnvironment(sentMessages: MutableList<EmbedBuilder> = mutableListOf()) {
val channel = mockk<Optional<ServerTextChannel>> {
every { isPresent } returns true
every { get() } returns mockk {
every { getRoles(any()) } returns emptyList()
every { sendMessage(capture(messages)) } returns mockk()
every { sendMessage(capture(sentMessages)) } returns mockk {
every { join() } returns mockk()
every { isCompletedExceptionally } returns false
}
}
}
}
fun prepareServerConfig(sentMessages: MutableList<EmbedBuilder> = mutableListOf()) {
val channelMock = mockk<ServerTextChannel> {
every { sendMessage(capture(sentMessages)) } returns mockk()
val api = mockk<DiscordApi> {
every { getServerById(any<String>()) } returns Optional.of(mockk {
every { icon.ifPresent(any()) } just Runs
every { getTextChannelById(any<String>()) } returns channel
every { getTextChannelsByName(any()) } returns listOf(channel.get())
every { getRolesByNameIgnoreCase("testrole") } returns listOf(mockk {
every { id } returns 1
})
})
}
val resultMock = mockk<Optional<ServerTextChannel>>()
every { resultMock.isPresent } returns true
every { resultMock.get() } returns channelMock
val server = mockk<Server>()
every { server.icon.ifPresent(any()) } just Runs
every { server.getTextChannelById(any<Long>()) } returns resultMock
//Config.server = server
Globals.api = api
Globals.config = Config(RawConfig.read("testconfig.toml"))
}
fun testMessageSuccess(content: String, result: String) {
@ -63,4 +76,10 @@ object TestUtil {
fun embedToString(embed: EmbedBuilder): String {
return (embed.delegate as EmbedBuilderDelegateImpl).toJsonNode().toString()
}
fun <R> withConfig(config: String, test: (() -> R)) {
val rawConfig = RawConfig.readFromString(config)
Globals.config.reloadCommands(rawConfig)
test()
}
}

View File

@ -0,0 +1,8 @@
[system]
serverId = "356414885292277771"
color = "#1793d0"
[localization]
permissionDenied = "no permissions"
redirectedMessage = "says"
messageDeleted = "message dongered"