Code cleanups, mostly runBlocking inlining in tests
This commit is contained in:
parent
8681fa3465
commit
4a78a894a4
10 changed files with 745 additions and 822 deletions
|
@ -10,7 +10,7 @@ import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories
|
||||||
@ConfigurationPropertiesScan
|
@ConfigurationPropertiesScan
|
||||||
@EntityScan("interview.chataccess.dbmodel")
|
@EntityScan("interview.chataccess.dbmodel")
|
||||||
@EnableR2dbcRepositories("interview.chataccess.dbmodel")
|
@EnableR2dbcRepositories("interview.chataccess.dbmodel")
|
||||||
open class ChatAccessApplication
|
class ChatAccessApplication
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
runApplication<ChatAccessApplication>(*args)
|
runApplication<ChatAccessApplication>(*args)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package interview.chataccess.controller
|
package interview.chataccess.controller
|
||||||
|
|
||||||
|
|
||||||
import interview.chataccess.controller.UtilsRestController.getPrincipal
|
import interview.chataccess.controller.UtilsRestController.getPrincipal
|
||||||
import interview.chataccess.controller.exceptions.ChatSessionNotFoundException
|
import interview.chataccess.controller.exceptions.ChatSessionNotFoundException
|
||||||
import interview.chataccess.controller.exceptions.GenericRestException
|
import interview.chataccess.controller.exceptions.GenericRestException
|
||||||
|
@ -10,6 +9,7 @@ import interview.chataccess.dto.*
|
||||||
import interview.chataccess.service.ChatService
|
import interview.chataccess.service.ChatService
|
||||||
import interview.chataccess.service.MessageService
|
import interview.chataccess.service.MessageService
|
||||||
import interview.chataccess.utils.logger
|
import interview.chataccess.utils.logger
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
@ -59,7 +59,8 @@ class MessageController(
|
||||||
suspend fun sendMessage(@RequestBody input: ChatMessageSendDTO): Flow<ChatMessagePartialReplyDTO> = coroutineScope {
|
suspend fun sendMessage(@RequestBody input: ChatMessageSendDTO): Flow<ChatMessagePartialReplyDTO> = coroutineScope {
|
||||||
val principal = getPrincipal()
|
val principal = getPrincipal()
|
||||||
|
|
||||||
val sId = async {
|
// Those two coroutines do not need to be LAZY, but it doesn't hurt.
|
||||||
|
val sId = async(start = CoroutineStart.LAZY) {
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(input.chatSessionId, principal)
|
.findChatByIdForUsername(input.chatSessionId, principal)
|
||||||
?: throw ChatSessionNotFoundException(
|
?: throw ChatSessionNotFoundException(
|
||||||
|
@ -74,7 +75,7 @@ class MessageController(
|
||||||
// both of the queries asynchronously for all the other cases.
|
// both of the queries asynchronously for all the other cases.
|
||||||
// We take advantage of the coroutines property that error on one of them will cancel
|
// We take advantage of the coroutines property that error on one of them will cancel
|
||||||
// the parent context (when not using supervisor).
|
// the parent context (when not using supervisor).
|
||||||
val pastMessages = async {
|
val pastMessages = async(start = CoroutineStart.LAZY) {
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(input.chatSessionId, principal)
|
.findMessagesInChatSessionForUsername(input.chatSessionId, principal)
|
||||||
.map { message ->
|
.map { message ->
|
||||||
|
|
|
@ -15,12 +15,12 @@ object UtilsRestController {
|
||||||
.awaitSingle()
|
.awaitSingle()
|
||||||
.authentication.principal
|
.authentication.principal
|
||||||
.let { authPrincipal ->
|
.let { authPrincipal ->
|
||||||
when {
|
when (authPrincipal) {
|
||||||
authPrincipal is User -> {
|
is User -> {
|
||||||
authPrincipal.username
|
authPrincipal.username
|
||||||
}
|
}
|
||||||
|
|
||||||
authPrincipal is Jwt -> {
|
is Jwt -> {
|
||||||
authPrincipal
|
authPrincipal
|
||||||
.claims["email"]
|
.claims["email"]
|
||||||
.toString()
|
.toString()
|
||||||
|
|
|
@ -13,23 +13,18 @@ open class BaseTests {
|
||||||
|
|
||||||
private var internalCounter: Int = 0
|
private var internalCounter: Int = 0
|
||||||
|
|
||||||
fun genChatSession(): ChatSession {
|
fun genChatSession() =
|
||||||
internalCounter++
|
ChatSession.from(
|
||||||
return ChatSession.from(
|
name = (++internalCounter).toString(),
|
||||||
name = internalCounter.toString(),
|
|
||||||
model = TESTS_DEFAULT_MODEL,
|
model = TESTS_DEFAULT_MODEL,
|
||||||
username = TESTS_DEFAULT_USERNAME
|
username = TESTS_DEFAULT_USERNAME
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
fun genChatMessage(chatSession: ChatSession): ChatMessage {
|
fun genChatMessage(chatSession: ChatSession) =
|
||||||
internalCounter++
|
ChatMessage.from(
|
||||||
return ChatMessage.from(
|
|
||||||
chatSession,
|
chatSession,
|
||||||
ChatRoles.USER,
|
ChatRoles.USER,
|
||||||
"Test message ${internalCounter}",
|
"Test message ${(++internalCounter)}",
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -34,6 +34,7 @@ class ApplicationStartupEventTests : BaseOllamaClientTest() {
|
||||||
.setBody("{\"status\":\"success\"}")
|
.setBody("{\"status\":\"success\"}")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationStartupEvent(appConfig, mockWebClient!!)
|
ApplicationStartupEvent(appConfig, mockWebClient!!)
|
||||||
.loadTestData()
|
.loadTestData()
|
||||||
}
|
}
|
||||||
|
@ -46,7 +47,7 @@ class ApplicationStartupEventTests : BaseOllamaClientTest() {
|
||||||
// The StartupEven will retry given number of times,
|
// The StartupEven will retry given number of times,
|
||||||
// we need to return error for all the retires.
|
// we need to return error for all the retires.
|
||||||
assertThrows<GenericRestException> {
|
assertThrows<GenericRestException> {
|
||||||
(0..ApplicationStartupEvent.MAX_RETRIES )
|
(0..ApplicationStartupEvent.MAX_RETRIES)
|
||||||
.forEach {
|
.forEach {
|
||||||
log.debug("Enqueued $it...")
|
log.debug("Enqueued $it...")
|
||||||
mockWebServer!!.enqueue(
|
mockWebServer!!.enqueue(
|
||||||
|
@ -57,7 +58,6 @@ class ApplicationStartupEventTests : BaseOllamaClientTest() {
|
||||||
|
|
||||||
ApplicationStartupEvent(appConfig, mockWebClient!!)
|
ApplicationStartupEvent(appConfig, mockWebClient!!)
|
||||||
.loadTestData()
|
.loadTestData()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,293 +50,273 @@ class ChatControllerTests : BaseControllerTests() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: fetch models list")
|
@DisplayName("ChatControllerTest: fetch models list")
|
||||||
fun getModelsList() {
|
fun getModelsList(): Unit = runBlocking {
|
||||||
runBlocking {
|
log.debug("Models in the test scenario: {}", appConfig.availableModels)
|
||||||
log.debug("Models in the test scenario: {}", appConfig.availableModels)
|
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.get()
|
.get()
|
||||||
.uri("/chat/models")
|
.uri("/chat/models")
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.is2xxSuccessful
|
.is2xxSuccessful
|
||||||
.expectBodyList(ChatSessionModelDTO::class.java)
|
.expectBodyList(ChatSessionModelDTO::class.java)
|
||||||
.hasSize(appConfig.availableModels.size)
|
.hasSize(appConfig.availableModels.size)
|
||||||
.contains(
|
.contains(
|
||||||
*(appConfig.availableModels
|
*(appConfig.availableModels
|
||||||
.map { m ->
|
.map { m ->
|
||||||
ChatSessionModelDTO(model = m)
|
ChatSessionModelDTO(model = m)
|
||||||
}.toTypedArray())
|
}.toTypedArray())
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: fetch chat sessions list")
|
@DisplayName("ChatControllerTest: fetch chat sessions list")
|
||||||
fun getSessions() {
|
fun getSessions(): Unit = runBlocking {
|
||||||
runBlocking {
|
(1..5).map { genChatSession() }
|
||||||
(1..5).map { genChatSession() }
|
.toTypedArray()
|
||||||
.toTypedArray()
|
.also { chatSessions ->
|
||||||
.also { chatSessions ->
|
given(
|
||||||
given(
|
chatService
|
||||||
chatService
|
.findChatSessionsForUsername(TESTS_DEFAULT_USERNAME)
|
||||||
.findChatSessionsForUsername(TESTS_DEFAULT_USERNAME)
|
).willReturn(Flux.just(*chatSessions).asFlow())
|
||||||
).willReturn(Flux.just(*chatSessions).asFlow())
|
}
|
||||||
}
|
.map { chatSession ->
|
||||||
.map { chatSession ->
|
ChatSessionDTO(
|
||||||
ChatSessionDTO(
|
id = chatSession.id,
|
||||||
id = chatSession.id,
|
name = chatSession.name,
|
||||||
name = chatSession.name,
|
model = chatSession.model,
|
||||||
model = chatSession.model,
|
createdDate = chatSession.createdDate
|
||||||
createdDate = chatSession.createdDate
|
)
|
||||||
)
|
}
|
||||||
}
|
.toTypedArray()
|
||||||
.toTypedArray()
|
.also { chatSessions ->
|
||||||
.also { chatSessions ->
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.get()
|
||||||
.get()
|
.uri("/chat/session")
|
||||||
.uri("/chat/session")
|
.accept(MediaType.TEXT_EVENT_STREAM)
|
||||||
.accept(MediaType.TEXT_EVENT_STREAM)
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is2xxSuccessful
|
||||||
.is2xxSuccessful
|
.expectBodyList(ChatSessionDTO::class.java)
|
||||||
.expectBodyList(ChatSessionDTO::class.java)
|
.hasSize(chatSessions.size)
|
||||||
.hasSize(chatSessions.size)
|
.contains(*chatSessions)
|
||||||
.contains(*chatSessions)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: fetch chat session details")
|
@DisplayName("ChatControllerTest: fetch chat session details")
|
||||||
fun getSessionDetails() {
|
fun getSessionDetails(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
given(
|
||||||
given(
|
chatService
|
||||||
chatService
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
).willReturn(chatSession)
|
||||||
).willReturn(chatSession)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.get()
|
||||||
.get()
|
.uri("/chat/session/${chatSession.id}")
|
||||||
.uri("/chat/session/${chatSession.id}")
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is2xxSuccessful
|
||||||
.is2xxSuccessful
|
.expectBody(ChatSessionDTO::class.java)
|
||||||
.expectBody(ChatSessionDTO::class.java)
|
.value { response ->
|
||||||
.value { response ->
|
response.id == chatSession.id
|
||||||
response.id == chatSession.id
|
&& response.name == chatSession.name
|
||||||
&& response.name == chatSession.name
|
&& response.model == chatSession.model
|
||||||
&& response.model == chatSession.model
|
&& response.createdDate == chatSession.createdDate
|
||||||
&& response.createdDate == chatSession.createdDate
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: fetch non-existent chat session details")
|
@DisplayName("ChatControllerTest: fetch non-existent chat session details")
|
||||||
fun getSessionDetailsNonExist() {
|
fun getSessionDetailsNonExist(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.apply {
|
||||||
.apply {
|
// We do not store it, i.e., not mock storing, just simulating,
|
||||||
// We do not store it, i.e., not mock storing, just simulating,
|
// like it would be deleted in the meantime.
|
||||||
// like it would be deleted in the meantime.
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
given(
|
||||||
given(
|
chatService
|
||||||
chatService
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
).willReturn(null)
|
||||||
).willReturn(null)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.get()
|
||||||
.get()
|
.uri("/chat/session/${chatSession.id}")
|
||||||
.uri("/chat/session/${chatSession.id}")
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is4xxClientError
|
||||||
.is4xxClientError
|
.expectBody(String::class.java)
|
||||||
.expectBody(String::class.java)
|
.isEqualTo(
|
||||||
.isEqualTo(
|
RestControllerAdvice.messageForSessionNotFound(chatSession.id!!)
|
||||||
RestControllerAdvice.messageForSessionNotFound(chatSession.id!!)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: delete non-existent chat session")
|
@DisplayName("ChatControllerTest: delete non-existent chat session")
|
||||||
fun deleteChatSessionNotExist() {
|
fun deleteChatSessionNotExist(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
given(
|
||||||
given(
|
chatService
|
||||||
chatService
|
.delete(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
.delete(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
).willReturn(false)
|
||||||
).willReturn(false)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.delete()
|
||||||
.delete()
|
.uri("/chat/session/${chatSession.id}")
|
||||||
.uri("/chat/session/${chatSession.id}")
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is4xxClientError
|
||||||
.is4xxClientError
|
.expectBody(String::class.java)
|
||||||
.expectBody(String::class.java)
|
.isEqualTo(
|
||||||
.isEqualTo(
|
RestControllerAdvice.messageForSessionNotFound(chatSession.id!!)
|
||||||
RestControllerAdvice.messageForSessionNotFound(chatSession.id!!)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: delete chat session")
|
@DisplayName("ChatControllerTest: delete chat session")
|
||||||
fun deleteChatSession() {
|
fun deleteChatSession(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
given(
|
||||||
given(
|
chatService
|
||||||
chatService
|
.delete(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
.delete(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
).willReturn(true)
|
||||||
).willReturn(true)
|
}
|
||||||
}
|
.also { chatSession ->
|
||||||
.also { chatSession ->
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.delete()
|
||||||
.delete()
|
.uri("/chat/session/${chatSession.id}")
|
||||||
.uri("/chat/session/${chatSession.id}")
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is2xxSuccessful
|
||||||
.is2xxSuccessful
|
.expectBody(String::class.java)
|
||||||
.expectBody(String::class.java)
|
.isEqualTo("Deleted")
|
||||||
.isEqualTo("Deleted")
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: create new chat session with wrong model")
|
@DisplayName("ChatControllerTest: create new chat session with wrong model")
|
||||||
fun createChatSessionWrongModel() {
|
fun createChatSessionWrongModel(): Unit = runBlocking {
|
||||||
runBlocking {
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.put()
|
||||||
.put()
|
.uri("/chat/session")
|
||||||
.uri("/chat/session")
|
.bodyValue(
|
||||||
.bodyValue(
|
ChatSessionCreateNewDTO(
|
||||||
ChatSessionCreateNewDTO(
|
model = TESTS_DEFAULT_WRONG_MODEL,
|
||||||
model = TESTS_DEFAULT_WRONG_MODEL,
|
name = "non-empty-name"
|
||||||
name = "non-empty-name"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
.exchange()
|
||||||
.is4xxClientError
|
.expectStatus()
|
||||||
.expectBody(String::class.java)
|
.is4xxClientError
|
||||||
.isEqualTo(
|
.expectBody(String::class.java)
|
||||||
RestControllerAdvice.messageForUnsupportedMode(TESTS_DEFAULT_WRONG_MODEL)
|
.isEqualTo(
|
||||||
)
|
RestControllerAdvice.messageForUnsupportedMode(TESTS_DEFAULT_WRONG_MODEL)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: create new chat session with empty name")
|
@DisplayName("ChatControllerTest: create new chat session with empty name")
|
||||||
fun createChatSessionEmptyName() {
|
fun createChatSessionEmptyName(): Unit = runBlocking {
|
||||||
runBlocking {
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.put()
|
||||||
.put()
|
.uri("/chat/session")
|
||||||
.uri("/chat/session")
|
.bodyValue(
|
||||||
.bodyValue(
|
ChatSessionCreateNewDTO(
|
||||||
ChatSessionCreateNewDTO(
|
model = TESTS_DEFAULT_MODEL,
|
||||||
model = TESTS_DEFAULT_MODEL,
|
name = "" // Empty by design for this test
|
||||||
name = "" // Empty by design for this test
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
.exchange()
|
||||||
.is4xxClientError
|
.expectStatus()
|
||||||
.expectBody(String::class.java)
|
.is4xxClientError
|
||||||
.isEqualTo(
|
.expectBody(String::class.java)
|
||||||
RestControllerAdvice.messageForBlankSessionName()
|
.isEqualTo(
|
||||||
)
|
RestControllerAdvice.messageForBlankSessionName()
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatControllerTest: create new chat session with wrong model")
|
@DisplayName("ChatControllerTest: create new chat session with wrong model")
|
||||||
fun createChatSession() {
|
fun createChatSession(): Unit = runBlocking {
|
||||||
runBlocking {
|
// This one looked hideous when written functionally, so semi-imperative it goes.
|
||||||
// This one looked hideous when written functionally, so semi-imperative it goes.
|
|
||||||
|
|
||||||
val chatSession = genChatSession()
|
val chatSession = genChatSession()
|
||||||
|
|
||||||
val savedChatSession = chatSession
|
val savedChatSession = chatSession
|
||||||
.copy()
|
.copy()
|
||||||
.apply {
|
.apply {
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.createdDate = Instant.now()
|
this.createdDate = Instant.now()
|
||||||
this.modifiedDate = Instant.now()
|
this.modifiedDate = Instant.now()
|
||||||
}
|
}
|
||||||
|
|
||||||
val chatMessage = ChatMessage.from(
|
val chatMessage = ChatMessage.from(
|
||||||
session = savedChatSession,
|
session = savedChatSession,
|
||||||
role = ChatRoles.BOT,
|
role = ChatRoles.BOT,
|
||||||
message = appConfig.defaultWelcomeMessage,
|
message = appConfig.defaultWelcomeMessage,
|
||||||
done = true
|
done = true
|
||||||
|
)
|
||||||
|
|
||||||
|
val savedChatMessage = chatMessage
|
||||||
|
.copy()
|
||||||
|
.apply {
|
||||||
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
|
this.createdDate = Instant.now()
|
||||||
|
this.modifiedDate = Instant.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
given(
|
||||||
|
chatService
|
||||||
|
.save(chatSession)
|
||||||
|
).willReturn(savedChatSession)
|
||||||
|
|
||||||
|
given(
|
||||||
|
messageService
|
||||||
|
.save(chatMessage)
|
||||||
|
).willReturn(savedChatMessage)
|
||||||
|
|
||||||
|
getWebTestClient()
|
||||||
|
.put()
|
||||||
|
.uri("/chat/session")
|
||||||
|
.bodyValue(
|
||||||
|
ChatSessionCreateNewDTO(
|
||||||
|
model = chatSession.model,
|
||||||
|
name = chatSession.name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
.exchange()
|
||||||
val savedChatMessage = chatMessage
|
.expectStatus()
|
||||||
.copy()
|
.is2xxSuccessful
|
||||||
.apply {
|
.expectBodyList(ChatSessionDTO::class.java)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
.contains(
|
||||||
this.createdDate = Instant.now()
|
ChatSessionDTO(
|
||||||
this.modifiedDate = Instant.now()
|
id = savedChatSession.id,
|
||||||
}
|
name = savedChatSession.name,
|
||||||
|
model = savedChatSession.model,
|
||||||
given(
|
createdDate = savedChatSession.createdDate,
|
||||||
chatService
|
|
||||||
.save(chatSession)
|
|
||||||
).willReturn(savedChatSession)
|
|
||||||
|
|
||||||
given(
|
|
||||||
messageService
|
|
||||||
.save(chatMessage)
|
|
||||||
).willReturn(savedChatMessage)
|
|
||||||
|
|
||||||
getWebTestClient()
|
|
||||||
.put()
|
|
||||||
.uri("/chat/session")
|
|
||||||
.bodyValue(
|
|
||||||
ChatSessionCreateNewDTO(
|
|
||||||
model = chatSession.model,
|
|
||||||
name = chatSession.name
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
|
||||||
.is2xxSuccessful
|
|
||||||
.expectBodyList(ChatSessionDTO::class.java)
|
|
||||||
.contains(
|
|
||||||
ChatSessionDTO(
|
|
||||||
id = savedChatSession.id,
|
|
||||||
name = savedChatSession.name,
|
|
||||||
model = savedChatSession.model,
|
|
||||||
createdDate = savedChatSession.createdDate,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,375 +47,362 @@ class MessageControllerTests : BaseOllamaClientTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: fetch previous non-existent messages")
|
@DisplayName("MessageControllerTest: fetch previous non-existent messages")
|
||||||
fun getPreviousMessagesNotExist() {
|
fun getPreviousMessagesNotExist(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSessionId = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
val chatSessionId = Random.nextInt(0, Int.MAX_VALUE)
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSessionId, TESTS_DEFAULT_USERNAME)
|
.findMessagesInChatSessionForUsername(chatSessionId, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(Mono.empty<ChatMessage>().asFlow())
|
).willReturn(Mono.empty<ChatMessage>().asFlow())
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.get()
|
.get()
|
||||||
.uri("/message/previous/${chatSessionId}")
|
.uri("/message/previous/${chatSessionId}")
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.is4xxClientError
|
.is4xxClientError
|
||||||
.expectBody(String::class.java)
|
.expectBody(String::class.java)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
RestControllerAdvice.messageForSessionNotFound(chatSessionId)
|
RestControllerAdvice.messageForSessionNotFound(chatSessionId)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: fetch previous non-existent messages")
|
@DisplayName("MessageControllerTest: fetch previous non-existent messages")
|
||||||
fun getPrevious() {
|
fun getPrevious(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val chatMessages = (1..5).map { genChatMessage(chatSession) }
|
val chatMessages = (1..5).map { genChatMessage(chatSession) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
.also { chatMessages ->
|
.also { chatMessages ->
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
||||||
).willReturn(Flux.just(*chatMessages).asFlow())
|
).willReturn(Flux.just(*chatMessages).asFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
val expectedReply = chatMessages
|
val expectedReply = chatMessages
|
||||||
.map { message ->
|
.map { message ->
|
||||||
ChatMessagePastReplyDTO(
|
ChatMessagePastReplyDTO(
|
||||||
content = message.message,
|
content = message.message,
|
||||||
role = message.role
|
role = message.role
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.reversed()
|
.reversed()
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.get()
|
.get()
|
||||||
.uri("/message/previous/${chatSession.id}")
|
.uri("/message/previous/${chatSession.id}")
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.is2xxSuccessful
|
.is2xxSuccessful
|
||||||
.expectBodyList(ChatMessagePastReplyDTO::class.java)
|
.expectBodyList(ChatMessagePastReplyDTO::class.java)
|
||||||
.hasSize(chatMessages.size)
|
.hasSize(chatMessages.size)
|
||||||
.contains(*expectedReply)
|
.contains(*expectedReply)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: send message to non-existent chat session")
|
@DisplayName("MessageControllerTest: send message to non-existent chat session")
|
||||||
fun sendMessageChatNotExist() {
|
fun sendMessageChatNotExist(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSessionId = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
val chatSessionId = Random.nextInt(0, Int.MAX_VALUE)
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSessionId, TESTS_DEFAULT_USERNAME)
|
.findChatByIdForUsername(chatSessionId, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(null)
|
).willReturn(null)
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.post()
|
.post()
|
||||||
.uri("/message/")
|
.uri("/message/")
|
||||||
.bodyValue(
|
.bodyValue(
|
||||||
ChatMessageSendDTO(
|
ChatMessageSendDTO(
|
||||||
chatSessionId = chatSessionId,
|
chatSessionId = chatSessionId,
|
||||||
message = "non empty message",
|
message = "non empty message",
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
.exchange()
|
||||||
.is4xxClientError
|
.expectStatus()
|
||||||
.expectBody(String::class.java)
|
.is4xxClientError
|
||||||
.isEqualTo(
|
.expectBody(String::class.java)
|
||||||
RestControllerAdvice.messageForSessionNotFound(chatSessionId)
|
.isEqualTo(
|
||||||
)
|
RestControllerAdvice.messageForSessionNotFound(chatSessionId)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: send chat message (non-streaming reply)")
|
@DisplayName("MessageControllerTest: send chat message (non-streaming reply)")
|
||||||
fun sendChatMessage() {
|
fun sendChatMessage(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(chatSession)
|
).willReturn(chatSession)
|
||||||
|
|
||||||
// Simulate past messages for the chat
|
// Simulate past messages for the chat
|
||||||
(1..5).map { genChatMessage(chatSession) }
|
(1..5).map { genChatMessage(chatSession) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
.also { chatMessages ->
|
.also { chatMessages ->
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
||||||
).willReturn(Flux.just(*chatMessages).asFlow())
|
).willReturn(Flux.just(*chatMessages).asFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
val expectedSavedMessages = arrayOf(
|
val expectedSavedMessages = arrayOf(
|
||||||
ChatMessage.from(
|
ChatMessage.from(
|
||||||
chatSession,
|
chatSession,
|
||||||
ChatRoles.USER,
|
ChatRoles.USER,
|
||||||
"Hello!",
|
"Hello!",
|
||||||
done = true
|
done = true
|
||||||
),
|
),
|
||||||
ChatMessage.from(
|
ChatMessage.from(
|
||||||
chatSession,
|
chatSession,
|
||||||
ChatRoles.BOT,
|
ChatRoles.BOT,
|
||||||
"Hello! How can I assist you today?",
|
"Hello! How can I assist you today?",
|
||||||
done = true
|
done = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
given(
|
||||||
|
messageService
|
||||||
|
.saveAll(*expectedSavedMessages)
|
||||||
|
).willReturn(Flux.just(*expectedSavedMessages).asFlow())
|
||||||
|
|
||||||
|
// Ollama llama3.2 generated response for "Hello!"
|
||||||
|
val mockResp = MockResponse()
|
||||||
|
mockResp.setResponseCode(200)
|
||||||
|
mockResp.addHeader("Content-Type", "application/json")
|
||||||
|
mockResp.setBody(
|
||||||
|
"{\"model\":\"llama3.2\"," +
|
||||||
|
"\"created_at\":\"2024-11-05T17:54:16.793334686Z\"," +
|
||||||
|
"\"message\":{" +
|
||||||
|
"\"role\":\"assistant\"," +
|
||||||
|
"\"content\":\"Hello! How can I assist you today?\"}," +
|
||||||
|
"\"done_reason\":\"stop\"," +
|
||||||
|
"\"done\":true," +
|
||||||
|
"\"total_duration\":698458587," +
|
||||||
|
"\"load_duration\":33044513," +
|
||||||
|
"\"prompt_eval_count\":27," +
|
||||||
|
"\"prompt_eval_duration\":182712000," +
|
||||||
|
"\"eval_count\":10,\"eval_duration\":440171000}"
|
||||||
|
)
|
||||||
|
mockWebServer!!.enqueue(mockResp)
|
||||||
|
|
||||||
|
val expectedReplies = arrayOf(
|
||||||
|
ChatMessagePartialReplyDTO(content = "Hello! How can I assist you today?", done = true),
|
||||||
|
)
|
||||||
|
getWebTestClient()
|
||||||
|
.post()
|
||||||
|
.uri("/message/")
|
||||||
|
.bodyValue(
|
||||||
|
ChatMessageSendDTO(
|
||||||
|
chatSessionId = chatSession.id!!,
|
||||||
|
message = "Hello!",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
given(
|
.exchange()
|
||||||
messageService
|
.expectStatus()
|
||||||
.saveAll(*expectedSavedMessages)
|
.is2xxSuccessful
|
||||||
).willReturn(Flux.just(*expectedSavedMessages).asFlow())
|
.expectBodyList(ChatMessagePartialReplyDTO::class.java)
|
||||||
|
.hasSize(1)
|
||||||
// Ollama llama3.2 generated response for "Hello!"
|
.contains(*expectedReplies)
|
||||||
val mockResp = MockResponse()
|
|
||||||
mockResp.setResponseCode(200)
|
|
||||||
mockResp.addHeader("Content-Type", "application/json")
|
|
||||||
mockResp.setBody(
|
|
||||||
"{\"model\":\"llama3.2\"," +
|
|
||||||
"\"created_at\":\"2024-11-05T17:54:16.793334686Z\"," +
|
|
||||||
"\"message\":{" +
|
|
||||||
"\"role\":\"assistant\"," +
|
|
||||||
"\"content\":\"Hello! How can I assist you today?\"}," +
|
|
||||||
"\"done_reason\":\"stop\"," +
|
|
||||||
"\"done\":true," +
|
|
||||||
"\"total_duration\":698458587," +
|
|
||||||
"\"load_duration\":33044513," +
|
|
||||||
"\"prompt_eval_count\":27," +
|
|
||||||
"\"prompt_eval_duration\":182712000," +
|
|
||||||
"\"eval_count\":10,\"eval_duration\":440171000}"
|
|
||||||
)
|
|
||||||
mockWebServer!!.enqueue(mockResp)
|
|
||||||
|
|
||||||
val expectedReplies = arrayOf(
|
|
||||||
ChatMessagePartialReplyDTO(content = "Hello! How can I assist you today?", done = true),
|
|
||||||
)
|
|
||||||
getWebTestClient()
|
|
||||||
.post()
|
|
||||||
.uri("/message/")
|
|
||||||
.bodyValue(
|
|
||||||
ChatMessageSendDTO(
|
|
||||||
chatSessionId = chatSession.id!!,
|
|
||||||
message = "Hello!",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus()
|
|
||||||
.is2xxSuccessful
|
|
||||||
.expectBodyList(ChatMessagePartialReplyDTO::class.java)
|
|
||||||
.hasSize(1)
|
|
||||||
.contains(*expectedReplies)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: send chat message (streaming reply)")
|
@DisplayName("MessageControllerTest: send chat message (streaming reply)")
|
||||||
fun sendChatMessageStreaming() {
|
fun sendChatMessageStreaming(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(chatSession)
|
).willReturn(chatSession)
|
||||||
|
|
||||||
// Simulate past messages for the chat
|
// Simulate past messages for the chat
|
||||||
(1..5).map { genChatMessage(chatSession) }
|
(1..5).map { genChatMessage(chatSession) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
.also { chatMessages ->
|
.also { chatMessages ->
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
||||||
).willReturn(Flux.just(*chatMessages).asFlow())
|
).willReturn(Flux.just(*chatMessages).asFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
val expectedSavedMessages = arrayOf(
|
val expectedSavedMessages = arrayOf(
|
||||||
ChatMessage.from(
|
ChatMessage.from(
|
||||||
chatSession,
|
chatSession,
|
||||||
ChatRoles.USER,
|
ChatRoles.USER,
|
||||||
"Hello!",
|
"Hello!",
|
||||||
done = true
|
done = true
|
||||||
),
|
),
|
||||||
ChatMessage.from(
|
ChatMessage.from(
|
||||||
chatSession,
|
chatSession,
|
||||||
ChatRoles.BOT,
|
ChatRoles.BOT,
|
||||||
"Hello! How can I assist you today?",
|
"Hello! How can I assist you today?",
|
||||||
done = true
|
done = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
given(
|
||||||
|
messageService
|
||||||
|
.saveAll(*expectedSavedMessages)
|
||||||
|
).willReturn(Flux.just(*expectedSavedMessages).asFlow())
|
||||||
|
|
||||||
|
// Ollama llama3.2 generated response for "Hello!" (streaming)
|
||||||
|
val mockResp = MockResponse()
|
||||||
|
mockResp.setResponseCode(200)
|
||||||
|
mockResp.addHeader("Content-Type", "application/x-ndjson")
|
||||||
|
mockResp.setBody(
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.800822113Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.851380825Z\",\"message\":{\"role\":\"assistant\",\"content\":\"!\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.905787498Z\",\"message\":{\"role\":\"assistant\",\"content\":\" How\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.968348153Z\",\"message\":{\"role\":\"assistant\",\"content\":\" can\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.024798921Z\",\"message\":{\"role\":\"assistant\",\"content\":\" I\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.077283283Z\",\"message\":{\"role\":\"assistant\",\"content\":\" assist\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.128354748Z\",\"message\":{\"role\":\"assistant\",\"content\":\" you\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.175314511Z\",\"message\":{\"role\":\"assistant\",\"content\":\" today\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.222567229Z\",\"message\":{\"role\":\"assistant\",\"content\":\"?\"},\"done\":false}\n" +
|
||||||
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.269542861Z\",\"message\":{\"role\":\"assistant\",\"content\":\"\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":6914935430,\"load_duration\":5889542121,\"prompt_eval_count\":27,\"prompt_eval_duration\":514096000,\"eval_count\":10,\"eval_duration\":468746000}\n"
|
||||||
|
)
|
||||||
|
mockWebServer!!.enqueue(mockResp)
|
||||||
|
|
||||||
|
val expectedReplies = arrayOf(
|
||||||
|
ChatMessagePartialReplyDTO(content = "Hello", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = "!", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " How", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " can", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " I", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " assist", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " you", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = " today", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = "?", done = false),
|
||||||
|
ChatMessagePartialReplyDTO(content = "", done = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
getWebTestClient()
|
||||||
|
.post()
|
||||||
|
.uri("/message/")
|
||||||
|
.bodyValue(
|
||||||
|
ChatMessageSendDTO(
|
||||||
|
chatSessionId = chatSession.id!!,
|
||||||
|
message = "Hello!",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
given(
|
.exchange()
|
||||||
messageService
|
.expectStatus()
|
||||||
.saveAll(*expectedSavedMessages)
|
.is2xxSuccessful
|
||||||
).willReturn(Flux.just(*expectedSavedMessages).asFlow())
|
.expectBodyList(ChatMessagePartialReplyDTO::class.java)
|
||||||
|
.hasSize(expectedReplies.size)
|
||||||
// Ollama llama3.2 generated response for "Hello!" (streaming)
|
.contains(*expectedReplies)
|
||||||
val mockResp = MockResponse()
|
|
||||||
mockResp.setResponseCode(200)
|
|
||||||
mockResp.addHeader("Content-Type", "application/x-ndjson")
|
|
||||||
mockResp.setBody(
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.800822113Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.851380825Z\",\"message\":{\"role\":\"assistant\",\"content\":\"!\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.905787498Z\",\"message\":{\"role\":\"assistant\",\"content\":\" How\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.968348153Z\",\"message\":{\"role\":\"assistant\",\"content\":\" can\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.024798921Z\",\"message\":{\"role\":\"assistant\",\"content\":\" I\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.077283283Z\",\"message\":{\"role\":\"assistant\",\"content\":\" assist\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.128354748Z\",\"message\":{\"role\":\"assistant\",\"content\":\" you\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.175314511Z\",\"message\":{\"role\":\"assistant\",\"content\":\" today\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.222567229Z\",\"message\":{\"role\":\"assistant\",\"content\":\"?\"},\"done\":false}\n" +
|
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.269542861Z\",\"message\":{\"role\":\"assistant\",\"content\":\"\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":6914935430,\"load_duration\":5889542121,\"prompt_eval_count\":27,\"prompt_eval_duration\":514096000,\"eval_count\":10,\"eval_duration\":468746000}\n"
|
|
||||||
)
|
|
||||||
mockWebServer!!.enqueue(mockResp)
|
|
||||||
|
|
||||||
val expectedReplies = arrayOf(
|
|
||||||
ChatMessagePartialReplyDTO(content = "Hello", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = "!", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " How", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " can", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " I", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " assist", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " you", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = " today", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = "?", done = false),
|
|
||||||
ChatMessagePartialReplyDTO(content = "", done = true),
|
|
||||||
)
|
|
||||||
|
|
||||||
getWebTestClient()
|
|
||||||
.post()
|
|
||||||
.uri("/message/")
|
|
||||||
.bodyValue(
|
|
||||||
ChatMessageSendDTO(
|
|
||||||
chatSessionId = chatSession.id!!,
|
|
||||||
message = "Hello!",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.exchange()
|
|
||||||
.expectStatus()
|
|
||||||
.is2xxSuccessful
|
|
||||||
.expectBodyList(ChatMessagePartialReplyDTO::class.java)
|
|
||||||
.hasSize(expectedReplies.size)
|
|
||||||
.contains(*expectedReplies)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: Ollama API returns error")
|
@DisplayName("MessageControllerTest: Ollama API returns error")
|
||||||
fun sendChatMessageButGot4xx() {
|
fun sendChatMessageButGot4xx(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(chatSession)
|
).willReturn(chatSession)
|
||||||
|
|
||||||
// Simulate past messages for the chat
|
// Simulate past messages for the chat
|
||||||
(1..5).map { genChatMessage(chatSession) }
|
(1..5).map { genChatMessage(chatSession) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
.also { chatMessages ->
|
.also { chatMessages ->
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
||||||
).willReturn(Flux.just(*chatMessages).asFlow())
|
).willReturn(Flux.just(*chatMessages).asFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
val mockResp = MockResponse()
|
val mockResp = MockResponse()
|
||||||
mockResp.setResponseCode(404)
|
mockResp.setResponseCode(404)
|
||||||
mockWebServer!!.enqueue(mockResp)
|
mockWebServer!!.enqueue(mockResp)
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.post()
|
.post()
|
||||||
.uri("/message/")
|
.uri("/message/")
|
||||||
.bodyValue(
|
.bodyValue(
|
||||||
ChatMessageSendDTO(
|
ChatMessageSendDTO(
|
||||||
chatSessionId = chatSession.id!!,
|
chatSessionId = chatSession.id!!,
|
||||||
message = "Hello!",
|
message = "Hello!",
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
.exchange()
|
||||||
.is5xxServerError
|
.expectStatus()
|
||||||
.expectBody(String::class.java)
|
.is5xxServerError
|
||||||
.value {
|
.expectBody(String::class.java)
|
||||||
it.startsWith(RestControllerAdvice.messagePrefixForGenericRestException())
|
.value {
|
||||||
}
|
it.startsWith(RestControllerAdvice.messagePrefixForGenericRestException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageControllerTest: send chat message (streaming reply with error in middle)")
|
@DisplayName("MessageControllerTest: send chat message (streaming reply with error in middle)")
|
||||||
fun sendChatMessageStreamingError() {
|
fun sendChatMessageStreamingError(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.apply {
|
||||||
.apply {
|
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
this.id = Random.nextInt(0, Int.MAX_VALUE)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
given(
|
given(
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
).willReturn(chatSession)
|
).willReturn(chatSession)
|
||||||
|
|
||||||
// Simulate past messages for the chat
|
// Simulate past messages for the chat
|
||||||
(1..3).map { genChatMessage(chatSession) }
|
(1..3).map { genChatMessage(chatSession) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
.also { chatMessages ->
|
.also { chatMessages ->
|
||||||
given(
|
given(
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, chatSession.username)
|
||||||
).willReturn(Flux.just(*chatMessages).asFlow())
|
).willReturn(Flux.just(*chatMessages).asFlow())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ollama llama3.2 generated response for "Hello!" (streaming)
|
// Ollama llama3.2 generated response for "Hello!" (streaming)
|
||||||
val mockResp = MockResponse()
|
val mockResp = MockResponse()
|
||||||
mockResp.setResponseCode(200)
|
mockResp.setResponseCode(200)
|
||||||
mockResp.addHeader("Content-Type", "application/x-ndjson")
|
mockResp.addHeader("Content-Type", "application/x-ndjson")
|
||||||
mockResp.setBody(
|
mockResp.setBody(
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.800822113Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\n" +
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.800822113Z\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\n" +
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.851380825Z\",\"message\":{\"role\":\"assistant\",\"content\":\"!\"},\"done\":false}\n" +
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.851380825Z\",\"message\":{\"role\":\"assistant\",\"content\":\"!\"},\"done\":false}\n" +
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.905787498Z\",\"message\":{\"role\":\"assistant\",\"content\":\" How\"},\"done\":false}\n" +
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.905787498Z\",\"message\":{\"role\":\"assistant\",\"content\":\" How\"},\"done\":false}\n" +
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.968348153Z\",\"message\":{\"role\":\"assistant\",\"content\":\" can\"},\"done\":false}\n" +
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:43.968348153Z\",\"message\":{\"role\":\"assistant\",\"content\":\" can\"},\"done\":false}\n" +
|
||||||
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.024798921Z\",\"message\":{\"role\":\"assistant\",\"content\":\" I\"},\"done\":false}\n" +
|
"{\"model\":\"llama3.2\",\"created_at\":\"2024-11-05T18:10:44.024798921Z\",\"message\":{\"role\":\"assistant\",\"content\":\" I\"},\"done\":false}\n" +
|
||||||
"malformed input in the middle"
|
"malformed input in the middle"
|
||||||
)
|
)
|
||||||
mockWebServer!!.enqueue(mockResp)
|
mockWebServer!!.enqueue(mockResp)
|
||||||
|
|
||||||
getWebTestClient()
|
getWebTestClient()
|
||||||
.post()
|
.post()
|
||||||
.uri("/message/")
|
.uri("/message/")
|
||||||
.bodyValue(
|
.bodyValue(
|
||||||
ChatMessageSendDTO(
|
ChatMessageSendDTO(
|
||||||
chatSessionId = chatSession.id!!,
|
chatSessionId = chatSession.id!!,
|
||||||
message = "Hello!",
|
message = "Hello!",
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.exchange()
|
)
|
||||||
.expectStatus()
|
.exchange()
|
||||||
.is5xxServerError
|
.expectStatus()
|
||||||
.expectBody(String::class.java)
|
.is5xxServerError
|
||||||
.value {
|
.expectBody(String::class.java)
|
||||||
it.startsWith(RestControllerAdvice.messagePrefixForGenericRestException())
|
.value {
|
||||||
}
|
it.startsWith(RestControllerAdvice.messagePrefixForGenericRestException())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.assertThrows
|
import org.junit.jupiter.api.assertThrows
|
||||||
import org.junit.jupiter.api.extension.ExtendWith
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
|
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
|
||||||
import org.springframework.context.annotation.Import
|
import org.springframework.context.annotation.Import
|
||||||
|
@ -27,7 +24,6 @@ import org.springframework.security.web.server.WebFilterChainProxy
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
|
|
||||||
@Import(TestApplicationConfig::class, SecurityConfig::class)
|
@Import(TestApplicationConfig::class, SecurityConfig::class)
|
||||||
@WebFluxTest(RestSecurityTests.Http200Controller::class)
|
@WebFluxTest(RestSecurityTests.Http200Controller::class)
|
||||||
|
@ -41,14 +37,8 @@ class RestSecurityTests : BaseControllerTests() {
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
class Http200Controller {
|
class Http200Controller {
|
||||||
|
|
||||||
val log = logger()
|
val log = logger()
|
||||||
|
|
||||||
@GetMapping("/")
|
|
||||||
suspend fun getOK(): Mono<String> {
|
|
||||||
return Mono.just("OK")
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/user")
|
@GetMapping("/user")
|
||||||
suspend fun getUsername(): String = getPrincipal().also { log.debug("Principal: ${it} ") }
|
suspend fun getUsername(): String = getPrincipal().also { log.debug("Principal: ${it} ") }
|
||||||
}
|
}
|
||||||
|
@ -71,70 +61,61 @@ class RestSecurityTests : BaseControllerTests() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Authentication required")
|
@DisplayName("Authentication required")
|
||||||
fun getModelsList() {
|
fun getModelsList(): Unit = runBlocking {
|
||||||
runBlocking {
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.get()
|
||||||
.get()
|
.uri("/")
|
||||||
.uri("/")
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is4xxClientError
|
||||||
.is4xxClientError
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("getPrincipal works for mock users")
|
@DisplayName("getPrincipal works for mock users")
|
||||||
@WithMockUser(username = TESTS_DEFAULT_USERNAME)
|
@WithMockUser(username = TESTS_DEFAULT_USERNAME)
|
||||||
fun getPrincipalForMockUser() {
|
fun getPrincipalForMockUser(): Unit = runBlocking {
|
||||||
// This one is just to make sure other tests with @WithMockUser do behave.
|
// This one is just to make sure other tests with @WithMockUser do behave.
|
||||||
runBlocking {
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.get()
|
||||||
.get()
|
.uri("/user")
|
||||||
.uri("/user")
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is2xxSuccessful
|
||||||
.is2xxSuccessful
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("getPrincipal works for mocked OAuth2")
|
@DisplayName("getPrincipal works for mocked OAuth2")
|
||||||
fun getPrincipalForMockOAuth2() {
|
fun getPrincipalForMockOAuth2(): Unit = runBlocking {
|
||||||
runBlocking {
|
getWebTestClient()
|
||||||
getWebTestClient()
|
.mutateWith(
|
||||||
.mutateWith(
|
SecurityMockServerConfigurers.mockJwt().jwt { jwt ->
|
||||||
SecurityMockServerConfigurers.mockJwt().jwt { jwt ->
|
jwt.claims { claims -> claims["email"] = TESTS_DEFAULT_USERNAME }
|
||||||
jwt.claims { claims -> claims["email"] = TESTS_DEFAULT_USERNAME }
|
}
|
||||||
}
|
)
|
||||||
)
|
.get()
|
||||||
.get()
|
.uri("/user")
|
||||||
.uri("/user")
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
.accept(MediaType.APPLICATION_JSON)
|
.exchange()
|
||||||
.exchange()
|
.expectStatus()
|
||||||
.expectStatus()
|
.is2xxSuccessful
|
||||||
.is2xxSuccessful
|
.expectBody(String::class.java)
|
||||||
.expectBody(String::class.java)
|
.isEqualTo(TESTS_DEFAULT_USERNAME)
|
||||||
.isEqualTo(TESTS_DEFAULT_USERNAME)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private val auth: Authentication? = null
|
private val auth: Authentication? = null
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("getPrincipal reacts to non-supported authentication context")
|
@DisplayName("getPrincipal reacts to non-supported authentication context")
|
||||||
fun getPrincipalWithUnsupportedContext() {
|
fun getPrincipalWithUnsupportedContext(): Unit = runBlocking {
|
||||||
runBlocking {
|
assertThrows<IllegalStateException> {
|
||||||
assertThrows<IllegalStateException> {
|
SecurityContextHolder
|
||||||
SecurityContextHolder
|
.getContext()
|
||||||
.getContext()
|
.authentication = auth
|
||||||
.authentication = auth
|
|
||||||
|
|
||||||
getPrincipal()
|
getPrincipal()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,133 +45,118 @@ class ChatServiceTests : BaseServiceTests() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatServiceTest: create chat session")
|
@DisplayName("ChatServiceTest: create chat session")
|
||||||
fun createChatSession() {
|
fun createChatSession(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
|
||||||
chatService
|
|
||||||
.save(chatSession)
|
|
||||||
.apply {
|
|
||||||
assertNotNull(id)
|
|
||||||
assertEquals(name, chatSession.name)
|
|
||||||
assertEquals(model, chatSession.model)
|
|
||||||
assertEquals(username, chatSession.username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("ChatServiceTest: delete chat session")
|
|
||||||
fun deleteChatSession() {
|
|
||||||
runBlocking {
|
|
||||||
chatService
|
|
||||||
.save(genChatSession())
|
|
||||||
.let { chatSession ->
|
|
||||||
assertNotNull(chatRepository.findById(chatSession.id!!))
|
|
||||||
|
|
||||||
chatService.delete(chatSession.id!!, chatSession.username)
|
|
||||||
|
|
||||||
assertNull(chatRepository.findById(chatSession.id!!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("ChatServiceTest: delete non-existent chat session")
|
|
||||||
fun deleteChatSessionNotExist() {
|
|
||||||
runBlocking {
|
|
||||||
val id = Random.nextInt(0, Int.MAX_VALUE)
|
|
||||||
assertFalse(
|
|
||||||
chatService.delete(id, TESTS_DEFAULT_USERNAME)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("ChatServiceTest: fetching chat sessions by id and username")
|
|
||||||
fun findOneByIdAndUsername() {
|
|
||||||
runBlocking {
|
|
||||||
chatRepository.saveAll(
|
|
||||||
(1..3).map { genChatSession() }
|
|
||||||
).onEach { chatSession ->
|
|
||||||
chatService
|
chatService
|
||||||
.findChatByIdForUsername(chatSession.id!!, chatSession.username)
|
.save(chatSession)
|
||||||
?.apply {
|
.apply {
|
||||||
assertNotNull(id)
|
assertNotNull(id)
|
||||||
assertNotNull(createdDate)
|
|
||||||
assertNotNull(modifiedDate)
|
|
||||||
assertNotNull(version)
|
|
||||||
assertEquals(name, chatSession.name)
|
assertEquals(name, chatSession.name)
|
||||||
assertEquals(model, chatSession.model)
|
assertEquals(model, chatSession.model)
|
||||||
assertEquals(username, chatSession.username)
|
assertEquals(username, chatSession.username)
|
||||||
}
|
}
|
||||||
?: fail("findChatByIdForUsername returned null when shouldn't")
|
}
|
||||||
}.lastOrNull()
|
}
|
||||||
}
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ChatServiceTest: delete chat session")
|
||||||
|
fun deleteChatSession(): Unit = runBlocking {
|
||||||
|
chatService
|
||||||
|
.save(genChatSession())
|
||||||
|
.let { chatSession ->
|
||||||
|
assertNotNull(chatRepository.findById(chatSession.id!!))
|
||||||
|
|
||||||
|
chatService.delete(chatSession.id!!, chatSession.username)
|
||||||
|
|
||||||
|
assertNull(chatRepository.findById(chatSession.id!!))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ChatServiceTest: delete non-existent chat session")
|
||||||
|
fun deleteChatSessionNotExist(): Unit = runBlocking {
|
||||||
|
val id = Random.nextInt(0, Int.MAX_VALUE)
|
||||||
|
assertFalse(
|
||||||
|
chatService.delete(id, TESTS_DEFAULT_USERNAME)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ChatServiceTest: fetching chat sessions by id and username")
|
||||||
|
fun findOneByIdAndUsername(): Unit = runBlocking {
|
||||||
|
chatRepository.saveAll(
|
||||||
|
(1..3).map { genChatSession() }
|
||||||
|
).onEach { chatSession ->
|
||||||
|
chatService
|
||||||
|
.findChatByIdForUsername(chatSession.id!!, chatSession.username)
|
||||||
|
?.apply {
|
||||||
|
assertNotNull(id)
|
||||||
|
assertNotNull(createdDate)
|
||||||
|
assertNotNull(modifiedDate)
|
||||||
|
assertNotNull(version)
|
||||||
|
assertEquals(name, chatSession.name)
|
||||||
|
assertEquals(model, chatSession.model)
|
||||||
|
assertEquals(username, chatSession.username)
|
||||||
|
}
|
||||||
|
?: fail("findChatByIdForUsername returned null when shouldn't")
|
||||||
|
}.lastOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatServiceTest: trying to fetch another user chat session")
|
@DisplayName("ChatServiceTest: trying to fetch another user chat session")
|
||||||
fun findOneByIdAndUsernameButDifferentUser() {
|
fun findOneByIdAndUsernameButDifferentUser(): Unit = runBlocking {
|
||||||
runBlocking {
|
chatService
|
||||||
chatService
|
.save(genChatSession())
|
||||||
.save(genChatSession())
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
assertNull(
|
||||||
assertNull(
|
chatService
|
||||||
chatService
|
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_ANOTHER_USERNAME)
|
||||||
.findChatByIdForUsername(chatSession.id!!, TESTS_DEFAULT_ANOTHER_USERNAME)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatServiceTest: trying to fetch non existing chat session")
|
@DisplayName("ChatServiceTest: trying to fetch non existing chat session")
|
||||||
fun findOneByIdAndUsernameButNonExisting() {
|
fun findOneByIdAndUsernameButNonExisting(): Unit = runBlocking {
|
||||||
runBlocking {
|
chatService
|
||||||
chatService
|
.save(genChatSession())
|
||||||
.save(genChatSession())
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
assertNull(
|
||||||
assertNull(
|
chatService
|
||||||
chatService
|
.findChatByIdForUsername(chatSession.id!! + 1, chatSession.username)
|
||||||
.findChatByIdForUsername(chatSession.id!! + 1, chatSession.username)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("ChatServiceTest: checking the order of queried chat sessions for username")
|
@DisplayName("ChatServiceTest: checking the order of queried chat sessions for username")
|
||||||
fun findAllByUsernameAndCheckOrder() {
|
fun findAllByUsernameAndCheckOrder(): Unit = runBlocking {
|
||||||
runBlocking {
|
// Order should be reversed from the order of inserting into database. Last first.
|
||||||
// Order should be reversed from the order of inserting into database. Last first.
|
(1..5).map { genChatSession() }
|
||||||
(1..5).map { genChatSession() }
|
.also { csList ->
|
||||||
.also { csList ->
|
chatRepository.saveAll(csList)
|
||||||
chatRepository.saveAll(csList)
|
.last()
|
||||||
.last()
|
}
|
||||||
}
|
.also { csList ->
|
||||||
.also { csList ->
|
chatService
|
||||||
chatService
|
.findChatSessionsForUsername(TESTS_DEFAULT_USERNAME)
|
||||||
.findChatSessionsForUsername(TESTS_DEFAULT_USERNAME)
|
.test {
|
||||||
.test {
|
csList
|
||||||
csList
|
.reversed()
|
||||||
.reversed()
|
.forEach { cs ->
|
||||||
.forEach { cs ->
|
with(this.awaitItem()) {
|
||||||
with(this.awaitItem()) {
|
assertNotNull(id)
|
||||||
assertNotNull(id)
|
assertNotNull(createdDate)
|
||||||
assertNotNull(createdDate)
|
assertNotNull(modifiedDate)
|
||||||
assertNotNull(modifiedDate)
|
assertNotNull(version)
|
||||||
assertNotNull(version)
|
assertEquals(cs.name, name)
|
||||||
assertEquals(cs.name, name)
|
assertEquals(cs.model, model)
|
||||||
assertEquals(cs.model, model)
|
assertEquals(cs.username, username)
|
||||||
assertEquals(cs.username, username)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
awaitComplete()
|
}
|
||||||
}
|
awaitComplete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -42,91 +42,85 @@ class MessageServiceTests : BaseServiceTests() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageServiceTest: create message")
|
@DisplayName("MessageServiceTest: create message")
|
||||||
fun createMessage() {
|
fun createMessage(): Unit = runBlocking {
|
||||||
runBlocking {
|
genChatSession()
|
||||||
genChatSession()
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
genChatMessage(
|
||||||
genChatMessage(
|
chatService
|
||||||
chatService
|
.save(chatSession)
|
||||||
.save(chatSession)
|
).let { msg ->
|
||||||
).let { msg ->
|
messageService.save(msg)
|
||||||
messageService.save(msg)
|
.apply {
|
||||||
.apply {
|
assertNotNull(id)
|
||||||
assertNotNull(id)
|
assertEquals(message, msg.message)
|
||||||
assertEquals(message, msg.message)
|
assertEquals(role, msg.role)
|
||||||
assertEquals(role, msg.role)
|
assertEquals(done, msg.done)
|
||||||
assertEquals(done, msg.done)
|
assertEquals(chatSessionId, msg.chatSessionId)
|
||||||
assertEquals(chatSessionId, msg.chatSessionId)
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageServiceTest: find all previous messages and verify order")
|
@DisplayName("MessageServiceTest: find all previous messages and verify order")
|
||||||
fun findAllByChatSessionIdAndUsername() {
|
fun findAllByChatSessionIdAndUsername(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
chatService
|
||||||
chatService
|
.save(chatSession)
|
||||||
.save(chatSession)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val messageList = (1..5).map {
|
val messageList = (1..5).map {
|
||||||
genChatMessage(
|
genChatMessage(
|
||||||
chatSession
|
chatSession
|
||||||
)
|
)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
messageService
|
messageService
|
||||||
.saveAll(*messageList)
|
.saveAll(*messageList)
|
||||||
.last()
|
.last()
|
||||||
|
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, TESTS_DEFAULT_USERNAME)
|
||||||
.test {
|
.test {
|
||||||
messageList
|
messageList
|
||||||
.forEach {
|
.forEach {
|
||||||
val item = awaitItem()
|
val item = awaitItem()
|
||||||
assertNotNull(item.id)
|
assertNotNull(item.id)
|
||||||
assertNotNull(item.createdDate)
|
assertNotNull(item.createdDate)
|
||||||
assertNotNull(item.modifiedDate)
|
assertNotNull(item.modifiedDate)
|
||||||
assertEquals(item.message, it.message)
|
assertEquals(item.message, it.message)
|
||||||
assertEquals(item.role, it.role)
|
assertEquals(item.role, it.role)
|
||||||
assertEquals(item.done, it.done)
|
assertEquals(item.done, it.done)
|
||||||
assertEquals(item.chatSessionId, it.chatSessionId)
|
assertEquals(item.chatSessionId, it.chatSessionId)
|
||||||
}
|
}
|
||||||
awaitComplete()
|
awaitComplete()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("MessageServiceTest: whether we can retrieve messages from another user's chat session")
|
@DisplayName("MessageServiceTest: whether we can retrieve messages from another user's chat session")
|
||||||
fun findAllForAnotherUser() {
|
fun findAllForAnotherUser(): Unit = runBlocking {
|
||||||
runBlocking {
|
val chatSession = genChatSession()
|
||||||
val chatSession = genChatSession()
|
.let { chatSession ->
|
||||||
.let { chatSession ->
|
chatService
|
||||||
chatService
|
.save(chatSession)
|
||||||
.save(chatSession)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val messageList = (1..5).map {
|
val messageList = (1..5).map {
|
||||||
genChatMessage(
|
genChatMessage(
|
||||||
chatSession
|
chatSession
|
||||||
)
|
)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
messageService
|
messageService
|
||||||
.saveAll(*messageList)
|
.saveAll(*messageList)
|
||||||
.last()
|
.last()
|
||||||
|
|
||||||
messageService
|
messageService
|
||||||
.findMessagesInChatSessionForUsername(chatSession.id!!, TESTS_DEFAULT_ANOTHER_USERNAME)
|
.findMessagesInChatSessionForUsername(chatSession.id!!, TESTS_DEFAULT_ANOTHER_USERNAME)
|
||||||
.test {
|
.test {
|
||||||
awaitComplete()
|
awaitComplete()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue