cleanup and refactoring of notification
close rabbit and redis connexion on application close Refactoring of Configuration class fix notification id increment Add builder for NotificationPush Add close to notificationPush to remove listener Clean tags of tests purge queue before functional tests
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import com.rabbitmq.client.Channel
|
||||
import com.rabbitmq.client.ConnectionFactory
|
||||
import fr.dcproject.application.Configuration
|
||||
import fr.dcproject.application.Env.CUCUMBER
|
||||
import fr.dcproject.application.module
|
||||
@@ -10,6 +12,8 @@ import io.cucumber.java8.Scenario
|
||||
import io.cucumber.junit.Cucumber
|
||||
import io.cucumber.junit.CucumberOptions
|
||||
import io.ktor.server.testing.withTestApplication
|
||||
import io.lettuce.core.RedisClient
|
||||
import io.lettuce.core.api.sync.RedisCommands
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import org.junit.runner.RunWith
|
||||
import org.koin.test.KoinTest
|
||||
@@ -24,6 +28,12 @@ var unitialized: Boolean = false
|
||||
@CucumberOptions(plugin = ["pretty"], strict = true)
|
||||
class CucumberTest : En, KoinTest {
|
||||
private val logger: Logger? by LoggerDelegate()
|
||||
val config = Configuration("application-test.conf")
|
||||
val redis: RedisCommands<String, String> = RedisClient.create(config.redis).connect().sync()
|
||||
val rabbit: Channel = ConnectionFactory()
|
||||
.apply { setUri(config.rabbitmq) }
|
||||
.newConnection()
|
||||
.createChannel()
|
||||
|
||||
@InternalCoroutinesApi
|
||||
val ktorContext = KtorServerContext {
|
||||
@@ -47,6 +57,14 @@ class CucumberTest : En, KoinTest {
|
||||
After { _: Scenario ->
|
||||
//language=PostgreSQL
|
||||
get<Connection>().sendQuery("rollback;", listOf())
|
||||
|
||||
redis.flushall()
|
||||
/* Purge rabbit notification queues */
|
||||
rabbit.run {
|
||||
queuePurge("push")
|
||||
queuePurge("email")
|
||||
}
|
||||
|
||||
ktorContext.stop()
|
||||
}
|
||||
}
|
||||
@@ -75,7 +93,7 @@ class CucumberTest : En, KoinTest {
|
||||
private fun getFixturesRequester(): Requester {
|
||||
return Requester.RequesterFactory(
|
||||
connection = get(),
|
||||
queriesDirectory = Configuration.Sql.fixtureFiles
|
||||
queriesDirectory = config.sql.fixtureFiles
|
||||
).createRequester()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import io.ktor.server.testing.withTestApplication
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.koin.test.AutoCloseKoinTest
|
||||
@@ -20,10 +21,11 @@ import org.koin.test.get
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Tags(Tag("functional"))
|
||||
class MailerTest : KoinTest, AutoCloseKoinTest() {
|
||||
@InternalCoroutinesApi
|
||||
@Test
|
||||
@Tag("online, functional")
|
||||
@Tags(Tag("online"))
|
||||
fun `can be send an email`() {
|
||||
withTestApplication({ module(TEST) }) {
|
||||
get<Mailer>().sendEmail {
|
||||
|
||||
@@ -7,10 +7,10 @@ import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.component.follow.FollowArticleRepository
|
||||
import fr.dcproject.component.follow.FollowSimple
|
||||
import fr.dcproject.messages.NotificationEmailSender
|
||||
import fr.dcproject.notification.ArticleUpdateNotification
|
||||
import fr.dcproject.notification.NotificationConsumer
|
||||
import fr.dcproject.notification.publisher.Publisher
|
||||
import fr.dcproject.messages.NotificationEmailSender
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import io.lettuce.core.RedisClient
|
||||
@@ -22,31 +22,53 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.InternalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
|
||||
@Tags(Tag("functional"))
|
||||
class NotificationConsumerTest {
|
||||
companion object {
|
||||
@BeforeAll
|
||||
@JvmStatic
|
||||
fun before() {
|
||||
val config: Configuration = Configuration("application-test.conf")
|
||||
RedisClient.create(config.redis).connect().sync().flushall()
|
||||
|
||||
/* Purge rabbit notification queues */
|
||||
ConnectionFactory()
|
||||
.apply { setUri(config.rabbitmq) }
|
||||
.run {
|
||||
newConnection().createChannel().apply {
|
||||
queuePurge("push")
|
||||
queuePurge("email")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@InternalCoroutinesApi
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
@ExperimentalCoroutinesApi
|
||||
@Test
|
||||
@Tag("functional")
|
||||
fun `can be send notification`() = runBlocking {
|
||||
val config: Configuration = Configuration("application-test.conf")
|
||||
/* Create mocks and spy's */
|
||||
val emailSender = mockk<NotificationEmailSender>() {
|
||||
every { sendEmail(any()) } returns Unit
|
||||
}
|
||||
|
||||
/* Init Spy on redis client */
|
||||
val redisClient = spyk<RedisClient>(RedisClient.create(Configuration.redis))
|
||||
val redisClient = spyk<RedisClient>(RedisClient.create(config.redis))
|
||||
val asyncCommand = spyk(redisClient.connect().async())
|
||||
every { redisClient.connect().async() } returns asyncCommand
|
||||
|
||||
val rabbitFactory: ConnectionFactory = spyk {
|
||||
ConnectionFactory().apply { setUri(Configuration.rabbitmq) }
|
||||
ConnectionFactory().apply { setUri(config.rabbitmq) }
|
||||
}
|
||||
val followArticleRepo = mockk<FollowArticleRepository> {
|
||||
every { findFollowsByTarget(any()) } returns flow {
|
||||
@@ -57,21 +79,15 @@ class NotificationConsumerTest {
|
||||
}
|
||||
}
|
||||
|
||||
/* Purge rabbit notification queues */
|
||||
rabbitFactory.newConnection().createChannel().apply {
|
||||
queuePurge("push")
|
||||
queuePurge("email")
|
||||
}
|
||||
|
||||
/* Config consumer */
|
||||
NotificationConsumer(
|
||||
val consumer = NotificationConsumer(
|
||||
rabbitFactory = rabbitFactory,
|
||||
redisClient = redisClient,
|
||||
followArticleRepo = followArticleRepo,
|
||||
followConstitutionRepo = mockk(),
|
||||
notificationEmailSender = emailSender,
|
||||
exchangeName = "notification_test",
|
||||
).config()
|
||||
).apply { start() }
|
||||
verify { rabbitFactory.newConnection() }
|
||||
|
||||
/* Push message */
|
||||
@@ -93,5 +109,7 @@ class NotificationConsumerTest {
|
||||
verify(timeout = 1000) { followArticleRepo.findFollowsByTarget(any()) }
|
||||
verify(timeout = 1000) { emailSender.sendEmail(any()) }
|
||||
verify(timeout = 1000) { asyncCommand.zadd(any<String>(), any<Double>(), any<String>()) }
|
||||
|
||||
// consumer.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,54 @@
|
||||
package functional
|
||||
|
||||
import com.rabbitmq.client.ConnectionFactory
|
||||
import fr.dcproject.application.Configuration
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.citizen.CitizenRef
|
||||
import fr.dcproject.notification.ArticleUpdateNotification
|
||||
import fr.dcproject.notification.Notification
|
||||
import io.lettuce.core.Limit
|
||||
import io.lettuce.core.RedisClient
|
||||
import io.mockk.every
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import notification.NotificationsPush
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.koin.test.AutoCloseKoinTest
|
||||
import org.koin.test.KoinTest
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@Tags(Tag("functional"))
|
||||
internal class NotificationsPushTest {
|
||||
companion object {
|
||||
@BeforeAll
|
||||
@JvmStatic
|
||||
fun before() {
|
||||
val config: Configuration = Configuration("application-test.conf")
|
||||
RedisClient.create(config.redis).connect().sync().flushall()
|
||||
|
||||
/* Purge rabbit notification queues */
|
||||
ConnectionFactory()
|
||||
.apply { setUri(config.rabbitmq) }
|
||||
.newConnection().createChannel().apply {
|
||||
queuePurge("push")
|
||||
queuePurge("email")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Tag("functional")
|
||||
fun `Notification from redis is well catch and return`() = runBlocking {
|
||||
val config: Configuration = Configuration("application-test.conf")
|
||||
/* Redis client for test */
|
||||
val redisClientTest = RedisClient.create(Configuration.redis)
|
||||
val redisClientTest = RedisClient.create(config.redis)
|
||||
|
||||
/* Init Spy on redis client */
|
||||
val redisClient = spyk<RedisClient>(RedisClient.create(Configuration.redis))
|
||||
val redisClient = spyk<RedisClient>(RedisClient.create(config.redis))
|
||||
val asyncCommand = spyk(redisClient.connect().async())
|
||||
every { redisClient.connect().async() } returns asyncCommand
|
||||
|
||||
@@ -44,36 +63,41 @@ internal class NotificationsPushTest {
|
||||
)
|
||||
/* Init two notification, one called before subscription, and the other after */
|
||||
val notifBeforeSubscribe = ArticleUpdateNotification(article)
|
||||
runBlocking {
|
||||
delay(100)
|
||||
}
|
||||
val notifAfterSubscribe = ArticleUpdateNotification(article)
|
||||
|
||||
/* init event for emulate incomint message from websocket */
|
||||
val event = MutableSharedFlow<Notification>()
|
||||
val incomingFlow = event.asSharedFlow()
|
||||
|
||||
spyk(object { var counter = 0}).run { /* Counter for count the callback of notification */
|
||||
spyk(object { var counter = 0 }).run { /* Counter for count the callback of notification */
|
||||
/* Sent notification */
|
||||
redisClientTest.connect().sync().run {
|
||||
zadd(
|
||||
redisClientTest.connect().run {
|
||||
sync().zadd(
|
||||
"notification:${citizen.id}",
|
||||
notifBeforeSubscribe.id,
|
||||
notifBeforeSubscribe.toString()
|
||||
)
|
||||
close()
|
||||
}
|
||||
|
||||
/* Init NotificationPush system, and set assertion in callback */
|
||||
NotificationsPush(redisClient, citizen, incomingFlow) {
|
||||
val notificationPush = NotificationsPush.Builder(redisClient).build(citizen, incomingFlow) {
|
||||
counter++
|
||||
if (counter == 1) it.id `should be equal to` notifBeforeSubscribe.id
|
||||
else it.id `should be equal to` notifAfterSubscribe.id
|
||||
}
|
||||
|
||||
/* Sent the notification */
|
||||
redisClientTest.connect().sync().run {
|
||||
zadd(
|
||||
redisClientTest.connect().run {
|
||||
sync().zadd(
|
||||
"notification:${citizen.id}",
|
||||
notifAfterSubscribe.id,
|
||||
notifAfterSubscribe.toString()
|
||||
)
|
||||
close()
|
||||
}
|
||||
|
||||
/* Verify if the callback is called 2 times */
|
||||
@@ -83,7 +107,8 @@ internal class NotificationsPushTest {
|
||||
/* Emit an event to delete notification */
|
||||
event.emit(notifAfterSubscribe)
|
||||
/* Verify the "mark as read" is called */
|
||||
verify(timeout = 300) { asyncCommand.zremrangebyscore(any(), any()) }
|
||||
verify(timeout = 500) { asyncCommand.zremrangebyscore(any(), any()) }
|
||||
notificationPush.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package functional
|
||||
|
||||
import fr.dcproject.utils.readResource
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Tags(Tag("functional"))
|
||||
class ResourcesKtTest {
|
||||
@Test
|
||||
fun readResource() {
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ktor.server.testing.withTestApplication
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.amshove.kluent.`should be equal to`
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
||||
@@ -19,7 +20,7 @@ import java.util.UUID
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
@TestInstance(PER_CLASS)
|
||||
@Tag("functional")
|
||||
@Tags(Tag("functional"))
|
||||
class ViewTest {
|
||||
@Test
|
||||
fun `test View Article`() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import io.mockk.mockk
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -23,7 +24,7 @@ import fr.dcproject.component.article.ArticleRepository as ArticleRepo
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class ArticleAccessControlTest {
|
||||
private val tesla = CitizenCart(
|
||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||
|
||||
@@ -10,6 +10,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -17,7 +18,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class CitizenAccessControlTest {
|
||||
private val tesla = CitizenBasic(
|
||||
user = User(
|
||||
|
||||
@@ -15,6 +15,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -23,7 +24,7 @@ import java.util.UUID
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class CommentAccessControlTest {
|
||||
private val tesla = Citizen(
|
||||
user = User(
|
||||
|
||||
@@ -14,6 +14,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -22,7 +23,7 @@ import java.util.UUID
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class FollowAccessControlTest {
|
||||
private val tesla = CitizenBasic(
|
||||
user = User(
|
||||
|
||||
@@ -14,6 +14,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -22,7 +23,7 @@ import java.util.UUID
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class OpinionAccessControlTest {
|
||||
private val tesla = CitizenBasic(
|
||||
user = User(
|
||||
|
||||
@@ -12,6 +12,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -20,7 +21,7 @@ import java.util.UUID
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class OpinionChoiceAccessControlTest {
|
||||
private val tesla = CitizenBasic(
|
||||
id = UUID.fromString("e6efc288-4283-4729-a268-6debb18de1a0"),
|
||||
|
||||
@@ -14,6 +14,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -23,7 +24,7 @@ import fr.dcproject.component.vote.entity.Vote as VoteEntity
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class VoteAccessControlTest {
|
||||
private val tesla = Citizen(
|
||||
id = UUID.fromString("a1e35c99-9d33-4fb4-9201-58d7071243bb"),
|
||||
|
||||
@@ -12,6 +12,7 @@ import fr.dcproject.security.AccessDecision.GRANTED
|
||||
import org.amshove.kluent.`should be`
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Tags
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.parallel.Execution
|
||||
@@ -21,7 +22,7 @@ import fr.dcproject.component.workgroup.Workgroup as WorkgroupEntity
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Execution(CONCURRENT)
|
||||
@Tag("security")
|
||||
@Tags(Tag("security"), Tag("unit"))
|
||||
internal class WorkgroupAccessControlTest {
|
||||
private val tesla = CitizenBasic(
|
||||
user = User(
|
||||
|
||||
Reference in New Issue
Block a user