
mock.BotBusMocked.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2017/2021 e-voyageurs technologies
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.tock.bot.test.mock
import ai.tock.bot.connector.ConnectorMessage
import ai.tock.bot.connector.messenger.messengerConnectorType
import ai.tock.bot.connector.messenger.model.MessengerConnectorMessage
import ai.tock.bot.connector.messenger.withMessenger
import ai.tock.bot.connector.twitter.model.TwitterConnectorMessage
import ai.tock.bot.connector.twitter.twitterConnectorType
import ai.tock.bot.connector.twitter.withTwitter
import ai.tock.bot.definition.BotDefinition
import ai.tock.bot.definition.IntentAware
import ai.tock.bot.definition.StoryDefinitionBase
import ai.tock.bot.definition.StoryHandlerBase
import ai.tock.bot.definition.StoryHandlerDefinition
import ai.tock.bot.definition.StoryStep
import ai.tock.bot.engine.BotBus
import ai.tock.bot.engine.action.Action
import ai.tock.bot.engine.action.ActionQuote
import ai.tock.bot.engine.action.ActionReply
import ai.tock.bot.engine.action.ActionVisibility
import ai.tock.bot.engine.action.SendChoice
import ai.tock.bot.engine.dialog.EntityValue
import ai.tock.bot.engine.dialog.EventState
import ai.tock.bot.engine.message.Message
import ai.tock.bot.engine.message.MessagesList
import ai.tock.bot.engine.user.PlayerId
import ai.tock.bot.engine.user.UserPreferences
import ai.tock.bot.engine.user.UserState
import ai.tock.bot.engine.user.UserTimeline
import ai.tock.nlp.api.client.model.Entity
import ai.tock.nlp.entity.Value
import ai.tock.shared.defaultLocale
import ai.tock.translator.I18nContext
import ai.tock.translator.Translator
import ai.tock.translator.UserInterfaceType.textAndVoiceAssistant
import ai.tock.translator.UserInterfaceType.textChat
import ai.tock.translator.raw
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import java.time.Instant
private val endCalled = ThreadLocal()
/**
* Test a [StoryDefinition] with a mocked (mockk) [BotBus].
*/
fun StoryDefinitionBase.test(bus: BotBus) {
endCalled.remove()
val handler = storyHandler as? StoryHandlerBase<*>
handler?.checkPreconditions()?.invoke(bus)
if (endCalled.get() != true) {
(storyHandler as? StoryHandlerBase<*>)?.newHandlerDefinition(bus)?.handle()
?: error("story handler is not a StoryHandlerBase")
}
}
/**
* Default mockk BotBus configuration.
*/
fun provideMockedBusCommon(bus: BotBus = mockk()): BotBus {
every { bus.step } returns null
every { bus.currentAnswerIndex } returns 0
every { bus.choice(any()) } returns null
every { bus.isIntent(any()) } returns false
every { bus.withMessage(any()) }.answers {
if (bus.targetConnectorType == (args[0] as ConnectorMessage).connectorType) {
@Suppress("UNCHECKED_CAST")
(args[0] as (() -> ConnectorMessage)).invoke()
}
bus
}
// withMessage
every { bus.withMessage(any(), any()) }.answers {
if (bus.targetConnectorType == args[0]) {
@Suppress("UNCHECKED_CAST")
(args[1] as (() -> ConnectorMessage)).invoke()
}
bus
}
// send
every { bus.send(any(), any()) } returns bus
every { bus.send(any(), any()) } returns bus
every { bus.send(any()) } returns bus
every { bus.send(any(), any()) } returns bus
every { bus.send(any(), any(), *anyVararg()) } returns bus
every { bus.send(any(), *anyVararg()) } returns bus
every { bus.send(any(), any()) }.answers {
@Suppress("UNCHECKED_CAST")
(args[1] as BotBus.() -> Any?).invoke(bus)
bus
}
// end
fun BotBus.endCall(): BotBus = apply { endCalled.set(true) }
every { bus.end(any(), any()) } answers { bus.endCall() }
every { bus.end(any(), any()) } answers { bus.endCall() }
every { bus.end(any(), any()) } answers { bus.endCall() }
every { bus.end(any()) } answers { bus.endCall() }
every { bus.end(any(), any()) } answers { bus.endCall() }
every { bus.end(any(), any(), *anyVararg()) } answers { bus.endCall() }
every { bus.end(any(), *anyVararg()) } answers { bus.endCall() }
every { bus.end(any(), any()) }.answers {
@Suppress("UNCHECKED_CAST")
(args[1] as BotBus.() -> Any?).invoke(bus)
bus.endCall()
}
every {
bus.entityValue(
any(),
any<(EntityValue) -> Value?>()
)
} returns null
every { bus.changeEntityValue(any(), any()) } returns Unit
every {
bus.entityText(
any()
)
} returns null
every {
bus.entityValueDetails(
any()
)
} returns null
every {
bus.entityValueDetails(
any()
)
} returns null
every { bus.changeEntityText(any(), any()) } returns Unit
val playerId = PlayerId("user")
every { bus.userId } returns playerId
val botId = PlayerId("bot")
every { bus.botId } returns botId
every { bus.applicationId } returns "appId"
val userTimeline: UserTimeline = mockk()
val userState = UserState(Instant.now())
every { bus.userTimeline } returns userTimeline
every { userTimeline.userState } returns userState
every { userTimeline.playerId } returns playerId
every { bus.userLocale } returns defaultLocale
every { bus.userPreferences } returns UserPreferences()
every { bus.userInterfaceType } returns textAndVoiceAssistant
val botDefinition: BotDefinition = mockk()
every { bus.botDefinition } returns botDefinition
every { botDefinition.defaultDelay(any()) } returns 0
every { bus.resetDialogState() } returns Unit
every { bus.translate(any()) } answers { (args[0] as CharSequence).raw }
every { bus.translate(any(), *anyVararg()) } answers {
Translator.formatMessage(
args[0].toString(),
I18nContext(defaultLocale, textChat, null),
args.subList(1, args.size)
).raw
}
every { bus.defaultDelay(any()) } returns 0
mockkObject(SendChoice.Companion)
every {
SendChoice.encodeChoiceId(bus, any(), any>(), any())
} answers {
@Suppress("UNCHECKED_CAST")
SendChoice.encodeChoiceId(
(args[1] as IntentAware).wrappedIntent(),
args[2] as? StoryStep,
(args[3] as? Map) ?: emptyMap(),
null,
null,
bus.applicationId
)
}
every {
SendChoice.encodeChoiceId(bus, any(), any(), any())
} answers {
@Suppress("UNCHECKED_CAST")
SendChoice.encodeChoiceId(
(args[1] as IntentAware).wrappedIntent(),
args[2] as? String,
(args[3] as? Map) ?: emptyMap(),
null,
null,
bus.applicationId
)
}
val state = mockk(relaxed = true)
val action = mockk(relaxed = true)
every { action.state } returns state
every { bus.action } returns action
every { bus.hasActionEntity(any()) } returns false
every { bus.entities } returns emptyMap()
return bus
}
/**
* Execute test with a bus mocked with default BotBus configuration.
*/
fun mockBus(bus: BotBus = mockk(), test: (BotBus) -> Any?) {
try {
provideMockedBusCommon(bus)
test(bus)
} finally {
clearAllMocks()
}
}
/**
* Execute test with a bus mocked with classic messenger extensions.
*/
fun mockMessenger(bus: BotBus = mockk(), test: (BotBus) -> Any?) {
try {
provideMockedMessengerBus(bus)
test(bus)
} finally {
clearAllMocks()
}
}
/**
* Mock classic messenger extensions.
*/
fun provideMockedMessengerBus(bus: BotBus = mockk()): BotBus {
provideMockedBusCommon(bus)
mockkStatic("ai.tock.bot.connector.messenger.MessengerBuildersKt")
every { bus.targetConnectorType } returns messengerConnectorType
every { bus.withMessenger(any()) }.answers {
if (bus.targetConnectorType == messengerConnectorType) {
@Suppress("UNCHECKED_CAST")
(args[1] as (() -> MessengerConnectorMessage)).invoke()
}
bus
}
return bus
}
/**
* Execute test with a bus mocked with classic twitter extensions.
*/
fun mockTwitter(bus: BotBus, test: (BotBus) -> Any?) {
try {
provideMockedTwitterBus(bus)
test(bus)
} finally {
clearAllMocks()
}
}
/**
* Mock classic twitter extensions.
*/
fun provideMockedTwitterBus(bus: BotBus): BotBus {
provideMockedBusCommon(bus)
mockkStatic("ai.tock.bot.connector.twitter.TwitterBuildersKt")
every { bus.targetConnectorType } returns twitterConnectorType
every { bus.action.metadata.visibility } returns ActionVisibility.UNKNOWN
every { bus.action.metadata.quoteMessage } returns ActionQuote.UNKNOWN
every { bus.action.metadata.replyMessage } returns ActionReply.UNKNOWN
every { bus.withTwitter(any()) }.answers {
if (bus.targetConnectorType == twitterConnectorType) {
@Suppress("UNCHECKED_CAST")
(args[1] as (() -> TwitterConnectorMessage)).invoke()
}
bus
}
return bus
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy