Skip to content

Commit

Permalink
Merge pull request #2271 from tolgee/ivanmanzhosov/slack-integration
Browse files Browse the repository at this point in the history
chore: New tests for Slack integration
  • Loading branch information
huglx authored May 9, 2024
2 parents 272d03f + 7912277 commit 9d3caa6
Show file tree
Hide file tree
Showing 22 changed files with 495 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.slack.api.model.block.LayoutBlock
import io.swagger.v3.oas.annotations.tags.Tag
import io.tolgee.component.SlackRequestValidation
import io.tolgee.component.automations.processors.slackIntegration.*
import io.tolgee.configuration.tolgee.TolgeeProperties
import io.tolgee.dtos.request.slack.SlackCommandDto
import io.tolgee.dtos.response.SlackMessageDto
import io.tolgee.dtos.slackintegration.SlackConfigDto
Expand Down Expand Up @@ -42,6 +43,7 @@ class SlackSlashCommandController(
private val slackErrorProvider: SlackErrorProvider,
private val slackExceptionHandler: SlackExceptionHandler,
private val slackHelpBlocksProvider: SlackHelpBlocksProvider,
private val tolgeeProperties: TolgeeProperties,
) : Logging {
@Suppress("UastIncorrectHttpHeaderInspection")
@PostMapping
Expand All @@ -54,6 +56,8 @@ class SlackSlashCommandController(
return slackExceptionHandler.handle {
slackRequestValidation.validate(slackSignature, timestamp, body)

checkIfTokenIsPresent(payload.team_id)

val matchResult =
commandRegex.matchEntire(payload.text) ?: throw SlackErrorException(slackErrorProvider.getInvalidCommandError())

Expand Down Expand Up @@ -87,6 +91,16 @@ class SlackSlashCommandController(
}
}

private fun checkIfTokenIsPresent(teamId: String) {
if (tolgeeProperties.slack.token != null) {
return
}

organizationSlackWorkspaceService.findBySlackTeamId(
teamId,
) ?: throw SlackErrorException(slackErrorProvider.getWorkspaceNotFoundError())
}

private fun String?.toLongOrThrowInvalidCommand(): Long {
return this?.toLongOrNull() ?: throw SlackErrorException(slackErrorProvider.getInvalidCommandError())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.tolgee.api.v2.controllers.slack

import io.tolgee.component.automations.processors.slackIntegration.SlackUserLoginUrlProvider
import io.tolgee.development.testDataBuilder.data.SlackTestData
import io.tolgee.fixtures.andIsOk
import io.tolgee.service.slackIntegration.SlackUserConnectionService
Expand All @@ -13,36 +14,22 @@ class SlackLoginControllerTest : AuthorizedControllerTest() {
@Autowired
lateinit var slackUserConnectionService: SlackUserConnectionService

@Autowired
lateinit var slackUserLoginUrlProvider: SlackUserLoginUrlProvider

@BeforeAll
fun setUp() {
tolgeeProperties.slack.token = "token"
}

@Test
fun `user log in`() {
fun `user logs in`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
performAuthPost(
"/v2/slack/user-login",
mapOf(
"slackId" to testData.slackUserConnection.slackUserId,
"channelId" to "TEST",
"workspaceId" to testData.slackWorkspace.id,
),
).andIsOk
}

@Test
fun `user does not log in and creates new connection`() {
Assertions.assertThat(slackUserConnectionService.findBySlackId("TEST1")).isNull()
performAuthPost(
"/v2/slack/user-login",
mapOf(
"slackId" to "TEST1",
"channelId" to "TEST",
"workspaceId" to 1,
),
).andIsOk
slackUserLoginUrlProvider.encryptData("ChannelTest", "TEST1", testData.slackWorkspace.id).let {
performAuthPost("/v2/slack/user-login?data=$it", null).andIsOk
}

Assertions.assertThat(slackUserConnectionService.findBySlackId("TEST1")).isNotNull()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.tolgee.service.slack

import io.tolgee.AbstractSpringTest
import io.tolgee.development.testDataBuilder.data.SlackTestData
import io.tolgee.testing.assertions.Assertions
import io.tolgee.util.addMinutes
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test

class SavedSlackMessageServiceTest : AbstractSpringTest() {
@AfterEach
fun after() {
currentDateProvider.forcedDate = null
}

@Test
fun `deletes old messages`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
currentDateProvider.forcedDate = currentDateProvider.date.addMinutes(125)

savedSlackMessageService.deleteOldMessage()
Assertions.assertThat(savedSlackMessageService.findAll()).isEmpty()
}

@Test
fun `finds messages`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
val result = savedSlackMessageService.find(0L, testData.slackConfig.id)

Assertions.assertThat(result).hasSize(2)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.tolgee.service.slack

import io.tolgee.AbstractSpringTest
import io.tolgee.development.testDataBuilder.data.SlackTestData
import io.tolgee.dtos.slackintegration.SlackConfigDto
import io.tolgee.model.slackIntegration.EventName
import io.tolgee.testing.assertions.Assertions
import org.junit.jupiter.api.Test

class SlackConfigServiceTest : AbstractSpringTest() {
@Test
fun `deletes configs`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
slackConfigService.delete(testData.projectBuilder.self.id, testData.slackConfig.channelId)
Assertions.assertThat(slackConfigService.findAll()).isEmpty()
}

@Test
fun `creates new config`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
val slackConfigDto =
SlackConfigDto(
project = testData.projectBuilder.self,
channelId = "testChannel2",
userAccount = testData.user,
onEvent = EventName.ALL,
)
slackConfigService.createOrUpdate(slackConfigDto)
Assertions.assertThat(slackConfigService.findAll()).hasSize(2)
}
}
157 changes: 146 additions & 11 deletions backend/app/src/test/kotlin/io/tolgee/slack/SlackIntegrationTest.kt
Original file line number Diff line number Diff line change
@@ -1,40 +1,175 @@
package io.tolgee.slack

import com.slack.api.RequestConfigurator
import com.slack.api.Slack
import com.slack.api.methods.MethodsClient
import io.tolgee.AbstractSpringTest
import com.slack.api.methods.request.chat.ChatPostMessageRequest
import com.slack.api.methods.request.users.UsersLookupByEmailRequest
import com.slack.api.methods.response.chat.ChatPostMessageResponse
import com.slack.api.methods.response.users.UsersLookupByEmailResponse
import io.tolgee.ProjectAuthControllerTest
import io.tolgee.development.testDataBuilder.data.SlackTestData
import io.tolgee.dtos.slackintegration.SlackConfigDto
import io.tolgee.fixtures.andIsOk
import io.tolgee.fixtures.waitForNotThrowing
import io.tolgee.model.slackIntegration.EventName
import io.tolgee.service.slackIntegration.SavedSlackMessageService
import io.tolgee.testing.assert
import io.tolgee.testing.assertions.Assertions
import io.tolgee.util.Logging
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.mock.mockito.MockBean
import java.util.*

class SlackIntegrationTest : AbstractSpringTest(), Logging {
class SlackIntegrationTest : ProjectAuthControllerTest(), Logging {
@Autowired
@MockBean
lateinit var slackClient: Slack

lateinit var mockedSlackClient: MockedSlackClient

@Autowired
lateinit var slackMessageService: SavedSlackMessageService

@Test
fun `it mocks slack client`() {
val mockedSlackClient = mockSlackClient()
fun `sends message to correct channel after translation changed`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
mockedSlackClient = mockSlackClient()

val langTag = testData.projectBuilder.self.baseLanguage?.tag ?: ""
loginAsUser(testData.user.username)

Mockito.clearInvocations(mockedSlackClient.methodsClientMock)
waitForNotThrowing(timeout = 3000) {
modifyTranslationData(testData.projectBuilder.self.id, langTag)
val request = mockedSlackClient.chatPostMessageRequests.single()
request.channel.assert.isEqualTo(testData.slackConfig.channelId)
}
Assertions.assertThat(slackMessageService.findByKey(testData.key.id, testData.slackConfig.id)).hasSize(1)
}

@Test
fun `sends message to correct channel after key added`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
mockedSlackClient = mockSlackClient()

loginAsUser(testData.user.username)
waitForNotThrowing(timeout = 3000) {
addKeyToProject(testData.projectBuilder.self.id)
mockedSlackClient.chatPostMessageRequests.assert.hasSize(1)
val request = mockedSlackClient.chatPostMessageRequests.single()
request.channel.assert.isEqualTo(testData.slackConfig.channelId)
}
}

@Test
fun `Doesn't send a message if the subscription isn't global and modified language isn't in preferred languages`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
mockedSlackClient = mockSlackClient()

val updatedConfig =
SlackConfigDto(
project = testData.projectBuilder.self,
slackId = "testSlackId",
channelId = "testChannel",
userAccount = testData.user,
languageTag = "fr",
onEvent = EventName.ALL,
slackTeamId = "",
)
slackConfigService.delete(testData.slackConfig.project.id, "testChannel")
val config = slackConfigService.createOrUpdate(updatedConfig)

slackClient.methods("ahhs").chatPostMessage {
it.channel("channel")
it.text("text")
loginAsUser(testData.user.username)

modifyTranslationData(testData.projectBuilder.self.id, "cs")
mockedSlackClient.chatPostMessageRequests.assert.hasSize(0)
slackMessageService.findByKey(testData.key.id, config.id).forEach {
it.langTags.assert.doesNotContain("cs")
}
}

@Test
fun `Doesn't send a message if the event isn't in subscribed by user`() {
val testData = SlackTestData()
testDataService.saveTestData(testData.root)
mockedSlackClient = mockSlackClient()

mockedSlackClient.chatPostMessageRequests.assert.hasSize(1)
val request = mockedSlackClient.chatPostMessageRequests.single()
request.channel.assert.isEqualTo("channel")
request.text.assert.isEqualTo("text")
val updatedConfig =
SlackConfigDto(
project = testData.projectBuilder.self,
slackId = "testSlackId",
channelId = "testChannel",
userAccount = testData.user,
languageTag = "en",
onEvent = EventName.TRANSLATION_CHANGED,
slackTeamId = "",
)
slackConfigService.delete(testData.slackConfig.project.id, "testChannel")
val config = slackConfigService.createOrUpdate(updatedConfig)

loginAsUser(testData.user.username)

addKeyToProject(testData.projectBuilder.self.id)
mockedSlackClient.chatPostMessageRequests.assert.hasSize(0)
slackMessageService.findByKey(testData.key.id, config.id).forEach {
it.langTags.assert.doesNotContain("en")
}
}

fun mockSlackClient(): MockedSlackClient {
val methodsClientMock = mock<MethodsClient>()
whenever(slackClient.methods(any())).thenReturn(methodsClientMock)
val mockPostMessageResponse = mock<ChatPostMessageResponse>()
whenever(mockPostMessageResponse.isOk).thenReturn(true)
whenever(mockPostMessageResponse.ts).thenReturn("ts")

val mockUsersResponse = mock<UsersLookupByEmailResponse>()
whenever(mockUsersResponse.isOk).thenReturn(true)

whenever(
methodsClientMock.chatPostMessage(
any<RequestConfigurator<ChatPostMessageRequest.ChatPostMessageRequestBuilder>>(),
),
).thenReturn(mockPostMessageResponse)

whenever(
methodsClientMock.usersLookupByEmail(
any<RequestConfigurator<UsersLookupByEmailRequest.UsersLookupByEmailRequestBuilder>>(),
),
).thenReturn(mockUsersResponse)

return MockedSlackClient(methodsClientMock)
}

private fun modifyTranslationData(
projectId: Long,
landTag: String,
) {
performAuthPost(
"/v2/projects/$projectId/translations",
mapOf(
"key" to "testKey",
"translations" to mapOf(landTag to UUID.randomUUID().toString()),
),
).andIsOk
}

private fun addKeyToProject(projectId: Long) {
performAuthPost(
"/v2/projects/$projectId/translations",
mapOf(
"key" to "newKey",
"translations" to mapOf("en" to "Sample Translation"),
),
).andIsOk
}
}
3 changes: 3 additions & 0 deletions backend/app/src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ spring:
datasource:
maximum-pool-size: 100
tolgee:
slack:
token: fakeToken
signingSecret: fakeSecret
postgres-autostart:
enabled: true
container-name: tolgee_backend_tests_postgres_main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import com.slack.api.model.Attachment
import com.slack.api.model.block.LayoutBlock

data class SavedMessageDto(
val blocks: List<LayoutBlock>,
var blocks: List<LayoutBlock>,
val attachments: List<Attachment>,
val keyId: Long,
val langTag: Set<String>,
val createdKeyBlocks: Boolean = false,
val baseChanged: Boolean = false,
)
Loading

0 comments on commit 9d3caa6

Please sign in to comment.