public async Task TestCommentCreate() { var newCrowdaction = new NewCrowdactionInternal("test" + Guid.NewGuid(), 100, "test", "test", "test", null, DateTime.UtcNow, DateTime.UtcNow.AddDays(1), null, null, null, null, new[] { Category.Community }, Array.Empty <string>(), CrowdactionDisplayPriority.Bottom, CrowdactionStatus.Running, 0, null); Crowdaction crowdaction = await crowdactionService.CreateCrowdactionInternal(newCrowdaction, CancellationToken.None); Assert.NotNull(crowdaction); ApplicationUser user = await context.Users.FirstAsync(); ClaimsPrincipal userPrincipal = await signInManager.CreateUserPrincipalAsync(user); CrowdactionComment firstComment = await crowdactionService.CreateComment("test test", crowdaction.Id, userPrincipal, CancellationToken.None); Assert.NotNull(firstComment); await Assert.ThrowsAnyAsync <Exception>(() => crowdactionService.CreateComment("test test <script />", crowdaction.Id, userPrincipal, CancellationToken.None)); await Assert.ThrowsAnyAsync <Exception>(() => crowdactionService.CreateComment("test test <a href=\"javascript:alert('hello')\" />", crowdaction.Id, userPrincipal, CancellationToken.None)); await crowdactionService.DeleteComment(firstComment.Id, CancellationToken.None); CrowdactionComment retrievedComment = await context.CrowdactionComments.FirstOrDefaultAsync(c => c.Id == firstComment.Id); Assert.Null(retrievedComment); CrowdactionComment sanitizedComment = await crowdactionService.CreateComment("test test <p><a href=\"www.google.com\" /></p>", crowdaction.Id, userPrincipal, CancellationToken.None); Assert.Equal("test test <p><a href=\"https://www.google.com\" rel=\"nofollow ugc\"></a></p>", sanitizedComment.Comment); await crowdactionService.DeleteComment(sanitizedComment.Id, CancellationToken.None); }
public async Task TestCrowdactionUpdate() { var user = await context.Users.FirstAsync(); var newCrowdaction = new NewCrowdactionInternal("test" + Guid.NewGuid(), 100, "test", "test", "test", null, DateTime.UtcNow, DateTime.UtcNow.AddDays(1), null, null, null, null, new[] { Category.Community }, Array.Empty <string>(), CrowdactionDisplayPriority.Bottom, CrowdactionStatus.Running, 0, user.Id); Crowdaction crowdaction = await crowdactionService.CreateCrowdactionInternal(newCrowdaction, CancellationToken.None); Assert.NotNull(crowdaction); Crowdaction currentCrowdaction = await context.Crowdactions.Include(c => c.Owner).FirstAsync(c => c.OwnerId != null); var owner = await signInManager.CreateUserPrincipalAsync(currentCrowdaction.Owner ?? throw new InvalidOperationException("Owner is null")); var r = new Random(); var updatedCrowdaction = new UpdatedCrowdaction() { Name = Guid.NewGuid().ToString(), BannerImageFileId = currentCrowdaction.BannerImageFileId, Categories = new[] { Category.Community, Category.Environment }, CreatorComments = currentCrowdaction.CreatorComments, Description = currentCrowdaction.Description, OwnerId = currentCrowdaction.OwnerId, DescriptionVideoLink = "https://www.youtube-nocookie.com/embed/xY0XTysJUDY", DescriptiveImageFileId = currentCrowdaction.DescriptiveImageFileId, DisplayPriority = CrowdactionDisplayPriority.Top, End = DateTime.Now.AddDays(30), Start = DateTime.Now.AddDays(10), Goal = Guid.NewGuid().ToString(), Tags = new string[3] { $"a{r.Next(1000)}", $"b{r.Next(1000)}", $"c{r.Next(1000)}" }, Id = currentCrowdaction.Id, NumberCrowdactionEmailsSent = 3, Proposal = currentCrowdaction.Proposal, Status = CrowdactionStatus.Running, Target = 33 }; var newCrowdactionResult = await crowdactionService.UpdateCrowdaction(updatedCrowdaction, CancellationToken.None); Assert.True(newCrowdactionResult.Succeeded); int?newCrowdactionId = newCrowdactionResult.Crowdaction?.Id; Assert.NotNull(newCrowdactionId); Crowdaction retrievedCrowdaction = await context.Crowdactions.Include(c => c.Tags).ThenInclude(t => t.Tag).FirstOrDefaultAsync(c => c.Id == newCrowdactionId); Assert.NotNull(retrievedCrowdaction); Assert.Equal(updatedCrowdaction.Name, retrievedCrowdaction.Name); Assert.True(Enumerable.SequenceEqual(updatedCrowdaction.Tags.OrderBy(t => t), retrievedCrowdaction.Tags.Select(t => t.Tag.Name).OrderBy(t => t))); await crowdactionService.DeleteCrowdaction(newCrowdactionId ?? -1, CancellationToken.None); retrievedCrowdaction = await context.Crowdactions.Include(c => c.Tags).ThenInclude(t => t.Tag).FirstOrDefaultAsync(c => c.Id == newCrowdactionId); Assert.Equal(CrowdactionStatus.Deleted, retrievedCrowdaction.Status); }
public async Task TestCrowdactionList() { var newCrowdaction = new NewCrowdactionInternal("test" + Guid.NewGuid(), 100, "test", "test", "test", null, DateTime.UtcNow, DateTime.UtcNow.AddDays(1), null, null, null, null, new[] { Category.Community }, Array.Empty <string>(), CrowdactionDisplayPriority.Bottom, CrowdactionStatus.Running, 0, null); Crowdaction createdCrowdaction = await crowdactionService.CreateCrowdactionInternal(newCrowdaction, CancellationToken.None); Assert.NotNull(createdCrowdaction); const string QueryCrowdactions = @" query { crowdactions { id name categories { category } descriptiveImage { filepath } } }"; HttpResponseMessage response = await PerformGraphQlQuery(QueryCrowdactions, null); string content = await response.Content.ReadAsStringAsync(); Assert.True(response.IsSuccessStatusCode, content); JsonDocument result = JsonDocument.Parse(content); Assert.Throws <KeyNotFoundException>(() => result.RootElement.GetProperty("errors")); JsonElement.ArrayEnumerator crowdactions = result.RootElement.GetProperty("data").GetProperty("crowdactions").EnumerateArray(); Assert.True(crowdactions.Any(), content); string crowdactionId = crowdactions.First().GetProperty("id").GetString(); const string QueryCrowdaction = @" query($crowdactionId : ID!) { crowdaction(id: $crowdactionId) { id name descriptiveImage { filepath } } }"; dynamic variables = new { crowdactionId }; response = await PerformGraphQlQuery(QueryCrowdaction, variables); content = await response.Content.ReadAsStringAsync(); Assert.True(response.IsSuccessStatusCode, content); result = JsonDocument.Parse(content); Assert.Throws <KeyNotFoundException>(() => result.RootElement.GetProperty("errors")); JsonElement crowdaction = result.RootElement.GetProperty("data").GetProperty("crowdaction"); Assert.Equal(crowdactionId.ToString(CultureInfo.InvariantCulture), crowdaction.GetProperty("id").GetString()); }
public async Task TestCrowdactionSearch() { Random r = new Random(); Category searchCategory = (Category)r.Next(7); for (int i = 0; i < r.Next(10, 30); i++) { var newCrowdaction = new NewCrowdactionInternal("test" + Guid.NewGuid(), 100, "test", "test", "test", null, DateTime.UtcNow.AddDays(r.Next(-20, 20)), DateTime.UtcNow.AddDays(r.Next(21, 50)), null, null, null, null, new[] { searchCategory }, Array.Empty <string>(), CrowdactionDisplayPriority.Bottom, (CrowdactionStatus)r.Next(3), 0, null); Crowdaction crowdaction = await crowdactionService.CreateCrowdactionInternal(newCrowdaction, CancellationToken.None); } Assert.True(await crowdactionService.SearchCrowdactions(null, null).AnyAsync()); Assert.True(await crowdactionService.SearchCrowdactions(searchCategory, null).Include(c => c.Categories).AllAsync(c => c.Categories.Any(pc => pc.Category == searchCategory))); Assert.True(await crowdactionService.SearchCrowdactions(null, SearchCrowdactionStatus.Closed).AllAsync(c => c.End < DateTime.UtcNow)); Assert.True(await crowdactionService.SearchCrowdactions(searchCategory, SearchCrowdactionStatus.Closed).AllAsync(c => c.End < DateTime.UtcNow)); Assert.True(await crowdactionService.SearchCrowdactions(null, SearchCrowdactionStatus.ComingSoon).AllAsync(c => c.Start > DateTime.UtcNow && c.Status == CrowdactionStatus.Running)); Assert.True(await crowdactionService.SearchCrowdactions(searchCategory, SearchCrowdactionStatus.ComingSoon).AllAsync(c => c.Start > DateTime.UtcNow && c.Status == CrowdactionStatus.Running)); Assert.True(await crowdactionService.SearchCrowdactions(null, SearchCrowdactionStatus.Open).AllAsync(c => c.Start <= DateTime.UtcNow && c.End >= DateTime.UtcNow && c.Status == CrowdactionStatus.Running)); Assert.True(await crowdactionService.SearchCrowdactions(searchCategory, SearchCrowdactionStatus.Open).AllAsync(c => c.Start <= DateTime.UtcNow && c.End >= DateTime.UtcNow && c.Status == CrowdactionStatus.Running)); }
public async Task <Crowdaction> CreateCrowdactionInternal(NewCrowdactionInternal newCrowdaction, CancellationToken token) { if (await context.Crowdactions.AnyAsync(c => c.Name == newCrowdaction.Name, token).ConfigureAwait(false)) { throw new InvalidOperationException($"A crowdaction with this name already exists: {newCrowdaction.Name}"); } logger.LogInformation("Creating crowdaction: {0}", newCrowdaction.Name); var tagMap = new Dictionary <string, int>(); List <string> tags = newCrowdaction.Tags.Distinct().ToList(); if (tags.Any()) { var missingTags = tags.Except( await context.Tags .Where(t => tags.Contains(t.Name)) .Select(t => t.Name) .ToListAsync(token).ConfigureAwait(false)) .Select(t => new Tag(t)); if (missingTags.Any()) { context.Tags.AddRange(missingTags); await context.SaveChangesAsync(token).ConfigureAwait(false); } tagMap = await context.Tags .Where(t => tags.Contains(t.Name)) .ToDictionaryAsync(t => t.Name, t => t.Id, token).ConfigureAwait(false); } List <CrowdactionTag> crowdactionTags = tags.Select(t => new CrowdactionTag(tagId: tagMap[t])) .ToList(); var crowdaction = new Crowdaction( name: newCrowdaction.Name, status: newCrowdaction.Status, ownerId: newCrowdaction.OwnerId, target: newCrowdaction.Target, start: newCrowdaction.Start, end: newCrowdaction.End.Date.AddHours(23).AddMinutes(59).AddSeconds(59), description: newCrowdaction.Description, goal: newCrowdaction.Goal, proposal: newCrowdaction.Proposal, creatorComments: newCrowdaction.CreatorComments, descriptionVideoLink: newCrowdaction.DescriptionVideoLink?.Replace("www.youtube.com", "www.youtube-nocookie.com", StringComparison.Ordinal), displayPriority: newCrowdaction.DisplayPriority, anonymousUserParticipants: newCrowdaction.AnonymousUserParticipants, bannerImageFileId: newCrowdaction.BannerImageFileId, cardImageFileId: newCrowdaction.CardImageFileId, descriptiveImageFileId: newCrowdaction.DescriptiveImageFileId, categories: newCrowdaction.Categories.Select(c => new CrowdactionCategory((c))).ToList(), tags: crowdactionTags); context.Crowdactions.Add(crowdaction); await context.SaveChangesAsync(token).ConfigureAwait(false); await RefreshParticipantCount(token).ConfigureAwait(false); if (!crowdaction.IsClosed) { crowdaction.FinishJobId = jobClient.Schedule(() => CrowdactionEndProcess(crowdaction.Id, CancellationToken.None), crowdaction.End); await context.SaveChangesAsync(token).ConfigureAwait(false); } return(crowdaction); }
private async Task SeedRandomCrowdactions(IEnumerable <ApplicationUser> users, CancellationToken cancellationToken) { Random r = new Random(); DateTime now = DateTime.UtcNow; string?[] videoLinks = new[] { "https://www.youtube-nocookie.com/embed/aLzM_L5fjCQ", "https://www.youtube-nocookie.com/embed/Zvugem-tKyI", "https://www.youtube-nocookie.com/embed/xY0XTysJUDY", "https://www.youtube-nocookie.com/embed/2yfPLxQQG-k", null }; List <(Uri bannerImageUrl, Task <byte[]> bannerImageBytes)> bannerImages = new[] { "https://collaction-production.s3.eu-central-1.amazonaws.com/57136ed4-b7f6-4dd2-a822-9341e2e60d1e.png", "https://collaction-production.s3.eu-central-1.amazonaws.com/765bc57b-748e-4bb8-a27e-08db6b99ea3e.png", "https://collaction-production.s3.eu-central-1.amazonaws.com/e06bbc2d-02f7-4a9b-a744-6923d5b21f51.png", }.Select(b => new Uri(b)).Select(b => (b, DownloadFile(b, cancellationToken))).ToList(); List <(Uri descriptiveImageUrl, Task <byte[]> descriptiveImageBytes)> descriptiveImages = new[] { "https://collaction-production.s3.eu-central-1.amazonaws.com/107104bc-deeb-4f48-b3a5-f25585bebf89.png", "https://collaction-production.s3.eu-central-1.amazonaws.com/365f2dc9-1784-45ea-9cc7-d5f0ef1a480c.png", "https://collaction-production.s3.eu-central-1.amazonaws.com/6e6c12b1-eaae-4811-aa1c-c169d10f1a59.png", }.Select(b => new Uri(b)).Select(b => (b, DownloadFile(b, cancellationToken))).ToList(); await Task.WhenAll(descriptiveImages.Select(d => d.descriptiveImageBytes).Concat(bannerImages.Select(b => b.bannerImageBytes))).ConfigureAwait(false); List <string> tags = Enumerable.Range(0, seedOptions.NumberSeededTags) .Select(r => Faker.Internet.DomainWord()) .Distinct() .ToList(); var crowdactionNames = Enumerable.Range(0, seedOptions.NumberSeededCrowdactions) .Select(i => Faker.Company.Name()) .Distinct() .ToList(); List <string> userIds = await context.Users.Select(u => u.Id).ToListAsync(cancellationToken).ConfigureAwait(false); List <Crowdaction> crowdactions = new List <Crowdaction>(crowdactionNames.Count); // Generate random crowdactions foreach (string crowdactionName in crowdactionNames) { DateTime start = now.Date.AddDays(r.Next(-10, 20)); const int MaxTagsForCrowdaction = 4; IEnumerable <string> crowdactionTags = Enumerable.Range(0, r.Next(MaxTagsForCrowdaction + 1)) .Select(i => r.Next(tags.Count)) .Distinct() .Select(i => tags[i]) .ToList(); int numberCategories = Enum.GetValues(typeof(Category)).Length; List <Category> categories = new[] { (Category)r.Next(numberCategories), (Category)r.Next(numberCategories) }.Distinct().ToList(); (Uri descriptiveImageUrl, Task <byte[]> descriptiveImageBytes) = descriptiveImages[r.Next(descriptiveImages.Count)]; ImageFile?descriptiveImage = r.Next(3) == 0 ? null : await imageService.UploadImage(ToFormFile(descriptiveImageBytes.Result, descriptiveImageUrl), Faker.Company.BS(), MaxImageBannerDimensionPixels, cancellationToken).ConfigureAwait(false); (Uri bannerImageUrl, Task <byte[]> bannerImageBytes) = bannerImages[r.Next(bannerImages.Count)]; ImageFile?bannerImage = r.Next(3) == 0 ? null : await imageService.UploadImage(ToFormFile(bannerImageBytes.Result, bannerImageUrl), Faker.Company.BS(), MaxImageBannerDimensionPixels, cancellationToken).ConfigureAwait(false); ImageFile?cardImage = bannerImage == null ? null : await imageService.UploadImage(ToFormFile(bannerImageBytes.Result, bannerImageUrl), Faker.Company.BS(), MaxImageCardDimensionPixels, cancellationToken).ConfigureAwait(false); ApplicationUser owner = users.ElementAt(users.Count() - 1); int numberStatusses = Enum.GetValues(typeof(CrowdactionStatus)).Length; CrowdactionStatus status = (CrowdactionStatus)r.Next(numberStatusses); NewCrowdactionInternal newCrowdaction = new NewCrowdactionInternal( name: crowdactionName, description: $"<p>{string.Join("</p><p>", Faker.Lorem.Paragraphs(r.Next(3) + 1))}</p>", start: start, end: start.AddDays(r.Next(10, 40)).AddHours(23).AddMinutes(59).AddSeconds(59), categories: categories, tags: crowdactionTags, bannerImageFileId: bannerImage?.Id, descriptiveImageFileId: descriptiveImage?.Id, cardImageFileId: cardImage?.Id, creatorComments: r.Next(4) == 0 ? null : $"<p>{string.Join("</p><p>", Faker.Lorem.Paragraphs(r.Next(3) + 1))}</p>", goal: Faker.Company.CatchPhrase(), proposal: Faker.Company.BS(), target: r.Next(1, 10000), descriptionVideoLink: videoLinks.ElementAt(r.Next(videoLinks.Length)), displayPriority: (CrowdactionDisplayPriority)r.Next(3), status: (CrowdactionStatus)r.Next(3), ownerId: owner.Id, anonymousUserParticipants: r.Next(1, 8000)); Crowdaction crowdaction = await crowdactionService.CreateCrowdactionInternal(newCrowdaction, cancellationToken).ConfigureAwait(false); context.CrowdactionParticipants.AddRange( userIds.Where(userId => r.Next(2) == 0) .Select(userId => new CrowdactionParticipant(userId, crowdaction.Id, r.Next(2) == 1, now, Guid.NewGuid()))); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); IEnumerable <CrowdactionComment> comments = Enumerable.Range(-24 * seedOptions.NumberDaysSeededForComments, 24 * seedOptions.NumberDaysSeededForComments) .Where(i => r.NextDouble() <= seedOptions.ProbabilityCommentSeededPerHour) .Select(i => { DateTime commentedAt = DateTime.Now.AddHours(i).AddMinutes(r.Next(-30, 30)).AddSeconds(r.Next(-30, 30)); string comment = $"<p>{string.Join("</p><p>", Faker.Lorem.Paragraphs(r.Next(2) + 1))}</p>"; string anonymousName = Faker.Name.First(); (string?userId, string?anonymousUser) = r.Next(3) == 0 ? ((string?)null, anonymousName.Substring(0, Math.Min(20, anonymousName.Length))) : (userIds[r.Next(userIds.Count)], null); var status = (CrowdactionCommentStatus)r.Next(3); return(new CrowdactionComment(comment, userId, anonymousUser, crowdaction.Id, commentedAt, status)); }); foreach (var comment in comments) // Insert one-by-one to preserve insertion order { context.CrowdactionComments.Add(comment); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } } }