Skip to content

Commit 20558da

Browse files
committed
fix(reactions): prevent setting reactions from creating duplicates due to Cartesian product
1 parent 8a70a64 commit 20558da

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

src/CrowdParlay.Social.Infrastructure.Persistence/Services/ReactionsRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ OPTIONAL MATCH (viewer)-[reaction:REACTED_TO]->(subject)
5050
5151
DELETE reaction
5252
53-
WITH viewer, subject
53+
WITH DISTINCT viewer, subject
5454
FOREACH (newReactionValue IN $reactions |
5555
CREATE (viewer)-[:REACTED_TO { Value: newReactionValue }]->(subject)
5656
)

tests/CrowdParlay.Social.IntegrationTests/Tests/ReactionsRepositoryTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,33 @@ public async Task SetReactions_OnlyWorksWithAllowedReactionSets()
4444
await reactWithNewInvalid.Should().ThrowAsync<ForbiddenException>();
4545
reactions.Should().BeEquivalentTo(thumbs);
4646
}
47+
48+
[Fact(DisplayName = "Setting reactions overwrites existing reactions")]
49+
public async Task SetReactions_OverwritesExistingReactions()
50+
{
51+
// Arrange
52+
await using var scope = _services.CreateAsyncScope();
53+
var authorsRepository = scope.ServiceProvider.GetRequiredService<IAuthorsRepository>();
54+
var discussionsRepository = scope.ServiceProvider.GetRequiredService<IDiscussionsRepository>();
55+
var reactionsRepository = scope.ServiceProvider.GetRequiredService<IReactionsRepository>();
56+
57+
var viewerId = Guid.NewGuid();
58+
await authorsRepository.EnsureCreatedAsync(viewerId);
59+
var discussionId = await discussionsRepository.CreateAsync(viewerId, "Title", "Description");
60+
61+
// Act
62+
await reactionsRepository.SetAsync(discussionId, viewerId, new HashSet<string> { "a" });
63+
await reactionsRepository.SetAsync(discussionId, viewerId, new HashSet<string> { "a", "b" });
64+
await reactionsRepository.SetAsync(discussionId, viewerId, new HashSet<string> { "a", "b", "c" });
65+
await reactionsRepository.SetAsync(discussionId, viewerId, new HashSet<string> { "b", "d" });
66+
67+
// Assert
68+
var discussion = await discussionsRepository.GetByIdAsync(discussionId, viewerId);
69+
discussion.ViewerReactions.Should().BeEquivalentTo(["b", "d"]);
70+
discussion.ReactionCounters.Should().BeEquivalentTo(new Dictionary<string, int>
71+
{
72+
{ "b", 1 },
73+
{ "d", 1 }
74+
});
75+
}
4776
}

0 commit comments

Comments
 (0)