Improve query findFollowsByTarget & add tests
This commit is contained in:
@@ -74,21 +74,24 @@ sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requ
|
||||
target: Entity,
|
||||
bulkSize: Int = 300
|
||||
): Flow<FollowForView<IN>> = flow {
|
||||
var nextPage = 1
|
||||
do {
|
||||
val paginate = findFollowsByTarget(target, nextPage, bulkSize)
|
||||
paginate.result.forEach {
|
||||
var lastId: UUID? = null
|
||||
while (true) {
|
||||
val result = findFollowsByTarget(target, lastId, bulkSize)
|
||||
if (result.count() == 0) {
|
||||
break
|
||||
}
|
||||
result.forEach {
|
||||
emit(it)
|
||||
}
|
||||
nextPage = paginate.currentPage + 1
|
||||
} while (!paginate.isLastPage())
|
||||
lastId = result.last().id
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun findFollowsByTarget(
|
||||
target: Entity,
|
||||
page: Int = 1,
|
||||
lastId: UUID?,
|
||||
limit: Int = 300
|
||||
): Paginated<FollowForView<IN>>
|
||||
): List<FollowForView<IN>>
|
||||
}
|
||||
|
||||
class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRef, ArticleForView>(requester) {
|
||||
@@ -109,14 +112,14 @@ class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRe
|
||||
|
||||
override fun findFollowsByTarget(
|
||||
target: Entity,
|
||||
page: Int,
|
||||
lastId: UUID?,
|
||||
limit: Int
|
||||
): Paginated<FollowForView<ArticleRef>> {
|
||||
): List<FollowForView<ArticleRef>> {
|
||||
return requester
|
||||
.getFunction("find_follows_article_by_target")
|
||||
.select(
|
||||
page,
|
||||
limit,
|
||||
"start_id" to lastId,
|
||||
"limit" to limit,
|
||||
"target_id" to target.id
|
||||
)
|
||||
}
|
||||
@@ -140,9 +143,9 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository<Cons
|
||||
|
||||
override fun findFollowsByTarget(
|
||||
target: Entity,
|
||||
page: Int,
|
||||
lastId: UUID?,
|
||||
limit: Int
|
||||
): Paginated<FollowForView<ConstitutionRef>> {
|
||||
): List<FollowForView<ConstitutionRef>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
@@ -165,9 +168,9 @@ class FollowCitizenRepository(requester: Requester) : FollowRepository<CitizenRe
|
||||
|
||||
override fun findFollowsByTarget(
|
||||
target: Entity,
|
||||
page: Int,
|
||||
lastId: UUID?,
|
||||
limit: Int
|
||||
): Paginated<FollowForView<CitizenRef>> {
|
||||
): List<FollowForView<CitizenRef>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
create or replace function find_follows_article_by_target(
|
||||
_target_id uuid,
|
||||
"limit" int default 50,
|
||||
"offset" int default 0,
|
||||
out resource json,
|
||||
out total int
|
||||
_limit int default 50,
|
||||
_start_id uuid default null,
|
||||
out resource json
|
||||
) language plpgsql as
|
||||
$$
|
||||
declare
|
||||
_version_id uuid = (select version_id from article where id = _target_id);
|
||||
_start_at timestamp default '2000-01-01 00:00:00'::timestamp;
|
||||
_article_creator_id uuid = (select created_by_id from article where id = _target_id);
|
||||
begin
|
||||
select json_agg(t), (
|
||||
select count(f.id)
|
||||
from follow f
|
||||
join article a on f.target_id = a.id
|
||||
where a.version_id = _version_id)
|
||||
into resource, total
|
||||
if _start_id is not null then
|
||||
select created_at into _start_at from follow where id = _start_id;
|
||||
end if;
|
||||
|
||||
select json_agg(t)
|
||||
into resource
|
||||
from (
|
||||
select
|
||||
f.id,
|
||||
@@ -22,11 +23,17 @@ begin
|
||||
f.target_reference,
|
||||
json_build_object('id', f.target_id) as target,
|
||||
find_citizen_by_id_with_user(f.created_by_id) as created_by
|
||||
from follow_article as f
|
||||
join article a on f.target_id = a.id
|
||||
where a.version_id = _version_id
|
||||
from follow as f
|
||||
left join article a on f.target_reference = 'article'::regclass and f.target_id = a.id
|
||||
where (
|
||||
(f.target_reference = 'article'::regclass and a.version_id = _version_id)
|
||||
or
|
||||
(f.target_reference = 'citizen'::regclass and f.target_id = _article_creator_id)
|
||||
)
|
||||
and f.created_at >= _start_at
|
||||
and (_start_id is null or f.id != _start_id)
|
||||
order by f.created_at
|
||||
limit "limit" offset "offset"
|
||||
limit _limit
|
||||
) as t;
|
||||
end
|
||||
$$;
|
||||
|
||||
@@ -66,7 +66,7 @@ class NotificationConsumerTest {
|
||||
@KtorExperimentalAPI
|
||||
@ExperimentalCoroutinesApi
|
||||
@Test
|
||||
fun `can be send notification`() = runBlocking {
|
||||
fun `can be receive article update notification when follow article`() = runBlocking {
|
||||
val config: Configuration = Configuration("application-test.conf")
|
||||
/* Create mocks and spy's */
|
||||
val emailSender = mockk<NotificationEmailSender>() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package integration
|
||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||
import fr.dcproject.component.notification.ArticleUpdateNotificationMessage
|
||||
import fr.dcproject.component.notification.NotificationMessage
|
||||
import integration.steps.given.`And follow citizen`
|
||||
import integration.steps.given.`Given I have article update notification`
|
||||
import integration.steps.given.`Given I have article`
|
||||
import integration.steps.given.`Given I have citizen`
|
||||
@@ -50,4 +51,38 @@ class `Notification routes` : BaseTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `I can receive article update notification when follow the creator`() {
|
||||
withIntegrationApplication {
|
||||
`Given I have citizen`("Thomas", "Pesquet", id = "1a34191a-9cde-45ba-8ac1-230138a102d3")
|
||||
`Given I have article`(id = "a06cbfb7-3094-4d64-aaa1-7486c0c292f4", createdBy = Name(firstName = "Thomas", lastName = "Pesquet"))
|
||||
`Given I have citizen`("Alan", "Bean") {
|
||||
`And follow citizen`(Name("Thomas", "Pesquet"))
|
||||
}
|
||||
`Given I have article update notification`("a06cbfb7-3094-4d64-aaa1-7486c0c292f4")
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
handleWebSocketConversation(
|
||||
"/notifications",
|
||||
{
|
||||
`authenticated in url as`("Alan", "Bean")
|
||||
}
|
||||
) { incoming, outgoing ->
|
||||
incoming.receive().let {
|
||||
when (it) {
|
||||
is Frame.Text -> NotificationMessage.fromString<ArticleUpdateNotificationMessage>(it.readText()).let { notif ->
|
||||
assertEquals(
|
||||
"a06cbfb7-3094-4d64-aaa1-7486c0c292f4",
|
||||
notif.target.id.toString()
|
||||
)
|
||||
outgoing.send(it)
|
||||
}
|
||||
else -> error(it.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ fun Citizen.`And follow citizen`(
|
||||
) {
|
||||
createFollow(this, CitizenRef(citizen.toUUID()))
|
||||
}
|
||||
fun Citizen.`And follow citizen`(
|
||||
name: CitizenI.Name,
|
||||
) {
|
||||
val citizenRepository: CitizenRepository by lazy { GlobalContext.get().get() }
|
||||
val citizen = citizenRepository.findByName(name) ?: error("Citizen not exist")
|
||||
createFollow(this, CitizenRef(citizen.id))
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`Given I have follow on article`(
|
||||
firstName: String,
|
||||
@@ -41,6 +48,17 @@ fun TestApplicationEngine.`Given I have follow on article`(
|
||||
createFollow(citizen, ArticleRef(article.toUUID()))
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`Given I have follow on citizen`(
|
||||
firstName: String,
|
||||
lastName: String,
|
||||
target: CitizenI.Name,
|
||||
) {
|
||||
val citizenRepository: CitizenRepository by lazy { GlobalContext.get().get() }
|
||||
val citizen = citizenRepository.findByName(CitizenI.Name(firstName, lastName)) ?: error("Citizen not exist")
|
||||
val targetCitizen = citizenRepository.findByName(target) ?: error("Citizen not exist")
|
||||
createFollow(citizen, CitizenRef(targetCitizen.id))
|
||||
}
|
||||
|
||||
fun TestApplicationEngine.`Given I have follow on constitution`(
|
||||
firstName: String,
|
||||
lastName: String,
|
||||
|
||||
@@ -7,17 +7,19 @@ declare
|
||||
_version_id1 uuid = uuid_generate_v4();
|
||||
first_article_id uuid := fixture_article(_citizen_id := _citizen_id, _version_id := _version_id1);
|
||||
first_article_updated_id uuid;
|
||||
_follow_count int = 0;
|
||||
begin
|
||||
perform follow('citizen'::regclass, _citizen_id, _citizen_id2);
|
||||
assert (select count(*) = 1 from follow), 'follow must be inserted';
|
||||
assert (select following = true from find_follow(_citizen_id, _citizen_id2, 'citizen')), 'find_follow must return the following';
|
||||
|
||||
perform follow('citizen'::regclass, _citizen_id, _citizen_id2);
|
||||
assert (select count(*) = 1 from follow), 'follow must be inserted';
|
||||
assert (select count(*) = 1 from follow), 're follow must be do nothing';
|
||||
|
||||
perform unfollow('citizen'::regclass, _citizen_id, _citizen_id2);
|
||||
assert (select count(*) = 0 from follow), 'follow must be deleted after unfollow';
|
||||
|
||||
|
||||
perform follow('article'::regclass, first_article_id, _citizen_id);
|
||||
assert (select following = true from find_follow(first_article_id, _citizen_id, 'article')), 'find_follow must return the following';
|
||||
assert (select following = false from find_follow(first_article_id, _citizen_id2, 'article')), 'find_follow must not return the following if not followinf';
|
||||
@@ -29,11 +31,17 @@ begin
|
||||
assert (select following = true from find_follow(first_article_id, _citizen_id, 'article')), '(v1) find_follow must return the following';
|
||||
assert (select following = true from find_follow(first_article_updated_id, _citizen_id, 'article')), '(v2) find_follow must return the following';
|
||||
|
||||
assert (select f.total = 1 from find_follows_article_by_target(first_article_id) as f), 'find_follows_article_by_target must return 1 follow';
|
||||
assert (select (f.resource#>>'{0, created_by, id}')::uuid = _citizen_id from find_follows_article_by_target(first_article_id) as f), 'find_follows_article_by_target must return follows with creator';
|
||||
assert (select count(*) = 1 from follow), 'must be only 1 follow';
|
||||
perform follow('citizen'::regclass, _citizen_id, _citizen_id2);
|
||||
assert (select count(*) = 2 from follow), 'follow citizen must be inserted';
|
||||
|
||||
assert (select json_array_length(f.resource) = 2 from find_follows_article_by_target(first_article_id) as f), 'find_follows_article_by_target must return 2 follows';
|
||||
assert (select (f.resource#>>'{0, created_by, id}')::uuid = _citizen_id from find_follows_article_by_target(first_article_id) as f), 'find_follows_article_by_target must return follows with creator';
|
||||
assert (select (f.resource#>>'{1, created_by, id}')::uuid = _citizen_id2 from find_follows_article_by_target(first_article_id) as f), 'find_follows_article_by_target must return follows with creator';
|
||||
|
||||
_follow_count = (select count(*) from follow);
|
||||
perform unfollow('article'::regclass, first_article_id, _citizen_id);
|
||||
assert (select count(*) = 0 from follow), 'follow must be deleted after unfollow, event if article is on other version';
|
||||
assert (select count(*) = _follow_count-1 from follow), 'follow must be deleted after unfollow, event if article is on other version';
|
||||
|
||||
rollback;
|
||||
raise notice 'follow test pass';
|
||||
|
||||
Reference in New Issue
Block a user