public async Task Can_create_OneToOne_relationship_from_principal_side() { // Arrange var existingTrack = _fakers.MusicTrack.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", data = new { type = "lyrics", relationships = new { track = new { data = new { type = "musicTracks", id = existingTrack.StringId } } } } } } }; const string route = "/operations"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAtomicAsync <AtomicOperationsDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Results.Should().HaveCount(1); responseDocument.Results[0].SingleData.Should().NotBeNull(); responseDocument.Results[0].SingleData.Type.Should().Be("lyrics"); responseDocument.Results[0].SingleData.Attributes.Should().NotBeEmpty(); responseDocument.Results[0].SingleData.Relationships.Should().NotBeEmpty(); var newLyricId = long.Parse(responseDocument.Results[0].SingleData.Id); await _testContext.RunOnDatabaseAsync(async dbContext => { var lyricInDatabase = await dbContext.Lyrics .Include(lyric => lyric.Track) .FirstWithIdAsync(newLyricId); lyricInDatabase.Track.Should().NotBeNull(); lyricInDatabase.Track.Id.Should().Be(existingTrack.Id); }); }
public async Task Cannot_add_to_HasOne_relationship() { // Arrange MusicTrack existingTrack = _fakers.MusicTrack.Generate(); RecordCompany existingCompany = _fakers.RecordCompany.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.AddRange(existingTrack, existingCompany); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", @ref = new { type = "musicTracks", id = existingTrack.StringId, relationship = "ownedBy" }, data = new { type = "recordCompanies", id = existingCompany.StringId } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, ErrorDocument responseDocument) = await _testContext.ExecutePostAtomicAsync <ErrorDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.UnprocessableEntity); responseDocument.Errors.Should().HaveCount(1); Error error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Failed to deserialize request body: Only to-many relationships can be targeted in 'add' operations."); error.Detail.Should().Be("Relationship 'ownedBy' must be a to-many relationship."); }
public async Task Can_select_fields_in_primary_resources() { // Arrange var store = _testContext.Factory.Services.GetRequiredService <ResourceCaptureStore>(); store.Clear(); var post = _fakers.BlogPost.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <BlogPost>(); dbContext.Posts.Add(post); await dbContext.SaveChangesAsync(); }); const string route = "/blogPosts?fields[blogPosts]=caption,author"; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(post.StringId); responseDocument.ManyData[0].Attributes.Should().HaveCount(1); responseDocument.ManyData[0].Attributes["caption"].Should().Be(post.Caption); responseDocument.ManyData[0].Relationships.Should().HaveCount(1); responseDocument.ManyData[0].Relationships["author"].Data.Should().BeNull(); responseDocument.ManyData[0].Relationships["author"].Links.Self.Should().NotBeNull(); responseDocument.ManyData[0].Relationships["author"].Links.Related.Should().NotBeNull(); var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(x => x is BlogPost).And.Subject.Single(); postCaptured.Caption.Should().Be(post.Caption); postCaptured.Url.Should().BeNull(); }
public async Task Can_clear_OneToOne_relationship_from_principal_side() { // Arrange var existingLyric = _fakers.Lyric.Generate(); existingLyric.Track = _fakers.MusicTrack.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <MusicTrack>(); dbContext.Lyrics.Add(existingLyric); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "update", @ref = new { type = "lyrics", id = existingLyric.StringId, relationship = "track" }, data = (object)null } } }; var route = "/operations"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAtomicAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { var lyricInDatabase = await dbContext.Lyrics .Include(lyric => lyric.Track) .FirstAsync(lyric => lyric.Id == existingLyric.Id); lyricInDatabase.Track.Should().BeNull(); var tracksInDatabase = await dbContext.MusicTracks.ToListAsync(); tracksInDatabase.Should().HaveCount(1); }); }
public async Task Can_include_in_primary_resources() { // Arrange var post = _fakers.BlogPost.Generate(); post.Author = _fakers.WebAccount.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync<BlogPost>(); dbContext.Posts.Add(post); await dbContext.SaveChangesAsync(); }); var route = "blogPosts?include=author"; // Act var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync<Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(post.StringId); responseDocument.ManyData[0].Attributes["caption"].Should().Be(post.Caption); responseDocument.Included.Should().HaveCount(1); responseDocument.Included[0].Type.Should().Be("webAccounts"); responseDocument.Included[0].Id.Should().Be(post.Author.StringId); responseDocument.Included[0].Attributes["displayName"].Should().Be(post.Author.DisplayName); }
public async Task Can_clear_HasMany_relationship() { // Arrange var existingTrack = _fakers.MusicTrack.Generate(); existingTrack.Performers = _fakers.Performer.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <Performer>(); dbContext.MusicTracks.Add(existingTrack); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "update", @ref = new { type = "musicTracks", id = existingTrack.StringId, relationship = "performers" }, data = new object[0] } } }; const string route = "/operations"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAtomicAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { var trackInDatabase = await dbContext.MusicTracks .Include(musicTrack => musicTrack.Performers) .FirstWithIdAsync(existingTrack.Id); trackInDatabase.Performers.Should().BeEmpty(); var performersInDatabase = await dbContext.Performers.ToListAsync(); performersInDatabase.Should().HaveCount(2); }); }
public async Task Can_paginate_in_primary_resources() { // Arrange List <BlogPost> posts = _fakers.BlogPost.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <BlogPost>(); dbContext.Posts.AddRange(posts); await dbContext.SaveChangesAsync(); }); const string route = "/blogPosts?page[number]=2&page[size]=1"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(posts[1].StringId); responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be(HostPrefix + route); responseDocument.Links.First.Should().Be(HostPrefix + "/blogPosts?page[size]=1"); responseDocument.Links.Last.Should().Be(responseDocument.Links.Self); responseDocument.Links.Prev.Should().Be(responseDocument.Links.First); responseDocument.Links.Next.Should().BeNull(); }
public async Task Can_create_HasMany_relationship() { // Arrange var existingUserAccounts = _fakers.UserAccount.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.AddRange(existingUserAccounts); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "workItems", relationships = new { subscribers = new { data = new[] { new { type = "userAccounts", id = existingUserAccounts[0].StringId }, new { type = "userAccounts", id = existingUserAccounts[1].StringId } } } } } }; var route = "/workItems"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Attributes.Should().NotBeEmpty(); responseDocument.SingleData.Relationships.Should().NotBeEmpty(); responseDocument.Included.Should().BeNull(); var newWorkItemId = int.Parse(responseDocument.SingleData.Id); await _testContext.RunOnDatabaseAsync(async dbContext => { var workItemInDatabase = await dbContext.WorkItems .Include(workItem => workItem.Subscribers) .FirstAsync(workItem => workItem.Id == newWorkItemId); workItemInDatabase.Subscribers.Should().HaveCount(2); workItemInDatabase.Subscribers.Should().ContainSingle(subscriber => subscriber.Id == existingUserAccounts[0].Id); workItemInDatabase.Subscribers.Should().ContainSingle(subscriber => subscriber.Id == existingUserAccounts[1].Id); }); }
public async Task Can_sort_in_primary_resources() { // Arrange var posts = _fakers.BlogPost.Generate(3); posts[0].Caption = "B"; posts[1].Caption = "A"; posts[2].Caption = "C"; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <BlogPost>(); dbContext.Posts.AddRange(posts); await dbContext.SaveChangesAsync(); }); const string route = "/blogPosts?sort=caption"; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(3); responseDocument.ManyData[0].Id.Should().Be(posts[1].StringId); responseDocument.ManyData[1].Id.Should().Be(posts[0].StringId); responseDocument.ManyData[2].Id.Should().Be(posts[2].StringId); }
public async Task Get_primary_resources_excludes_soft_deleted() { // Arrange List <Department> departments = _fakers.Department.Generate(2); departments[0].SoftDeletedAt = SoftDeletionTime; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <Department>(); dbContext.Departments.AddRange(departments); await dbContext.SaveChangesAsync(); }); const string route = "/departments"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(departments[1].StringId); }
public async Task Hides_text_in_update_resource_with_side_effects() { // Arrange var provider = _testContext.Factory.Services.GetRequiredService <LyricPermissionProvider>(); provider.CanViewText = false; provider.HitCount = 0; List <Lyric> existingLyrics = _fakers.Lyric.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Lyrics.AddRange(existingLyrics); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "update", data = new { type = "lyrics", id = existingLyrics[0].StringId, attributes = new { } } }, new { op = "update", data = new { type = "lyrics", id = existingLyrics[1].StringId, attributes = new { } } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, AtomicOperationsDocument responseDocument) = await _testContext.ExecutePostAtomicAsync <AtomicOperationsDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Results.Should().HaveCount(2); responseDocument.Results[0].SingleData.Attributes["format"].Should().Be(existingLyrics[0].Format); responseDocument.Results[0].SingleData.Attributes.Should().NotContainKey("text"); responseDocument.Results[1].SingleData.Attributes["format"].Should().Be(existingLyrics[1].Format); responseDocument.Results[1].SingleData.Attributes.Should().NotContainKey("text"); provider.HitCount.Should().Be(4); }
public async Task Can_get_resource_by_ID() { // Arrange var clock = (FrozenSystemClock)_testContext.Factory.Services.GetRequiredService <ISystemClock>(); clock.UtcNow = 27.January(2021); var certificate = _fakers.GiftCertificate.Generate(); certificate.IssueDate = 28.January(2020); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.GiftCertificates.Add(certificate); await dbContext.SaveChangesAsync(); }); var route = "/giftCertificates/" + certificate.StringId; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Id.Should().Be(certificate.StringId); responseDocument.SingleData.Attributes["issueDate"].Should().BeCloseTo(certificate.IssueDate); responseDocument.SingleData.Attributes["hasExpired"].Should().Be(false); }
public async Task Deleting_principal_side_of_required_OneToMany_relationship_triggers_cascading_delete() { // Arrange Order existingOrder = _fakers.Orders.Generate(); existingOrder.Customer = _fakers.Customers.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Orders.Add(existingOrder); await dbContext.SaveChangesAsync(); }); string route = $"/customers/{existingOrder.Customer.Id}"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteDeleteAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { Customer existingCustomerInDatabase = await dbContext.Customers.FirstWithIdOrDefaultAsync(existingOrder.Customer.Id); existingCustomerInDatabase.Should().BeNull(); Order existingOrderInDatabase = await dbContext.Orders.FirstWithIdOrDefaultAsync(existingOrder.Id); existingOrderInDatabase.Should().BeNull(); }); }
public async Task Can_filter_in_primary_resources() { // Arrange List <BlogPost> posts = _fakers.BlogPost.Generate(2); posts[0].Caption = "One"; posts[1].Caption = "Two"; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <BlogPost>(); dbContext.Posts.AddRange(posts); await dbContext.SaveChangesAsync(); }); const string route = "/blogPosts?filter=equals(caption,'Two')"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(posts[1].StringId); }
public async Task Can_clear_HasMany_relationship() { // Arrange WorkItem existingWorkItem = _fakers.WorkItem.Generate(); existingWorkItem.Subscribers = _fakers.UserAccount.Generate(2).ToHashSet(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new object[0] }; string route = $"/workItems/{existingWorkItem.StringId}/relationships/subscribers"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePatchAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { WorkItem workItemInDatabase = await dbContext.WorkItems.Include(workItem => workItem.Subscribers).FirstWithIdAsync(existingWorkItem.Id); workItemInDatabase.Subscribers.Should().BeEmpty(); }); }
public async Task Cannot_remove_from_HasOne_relationship() { // Arrange var existingWorkItem = _fakers.WorkItem.Generate(); existingWorkItem.Assignee = _fakers.UserAccount.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "userAccounts", id = existingWorkItem.Assignee.StringId } }; var route = $"/workItems/{existingWorkItem.StringId}/relationships/assignee"; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteDeleteAsync <ErrorDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Forbidden); responseDocument.Errors.Should().HaveCount(1); var error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.Forbidden); error.Title.Should().Be("Only to-many relationships can be updated through this endpoint."); error.Detail.Should().Be("Relationship 'assignee' must be a to-many relationship."); }
public async Task Includes_version_with_ext_on_operations_endpoint() { // Arrange const int newArtistId = 12345; string newArtistName = _fakers.Performer.Generate().ArtistName; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <Performer>(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", data = new { type = "performers", id = newArtistId, attributes = new { artistName = newArtistName } } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAtomicAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Should().BeJson(@"{ ""jsonapi"": { ""version"": ""1.1"", ""ext"": [ ""https://jsonapi.org/ext/atomic"" ] }, ""atomic:results"": [ { ""data"": { ""type"": ""performers"", ""id"": """ + newArtistId + @""", ""attributes"": { ""artistName"": """ + newArtistName + @""", ""bornAt"": ""0001-01-01T01:00:00+01:00"" }, ""links"": { ""self"": ""http://localhost/performers/" + newArtistId + @""" } } } ] }"); }
public async Task Can_clear_ManyToOne_relationship() { // Arrange var existingWorkItem = _fakers.WorkItem.Generate(); existingWorkItem.Assignee = _fakers.UserAccount.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "workItems", id = existingWorkItem.StringId, relationships = new { assignee = new { data = (object)null } } } }; var route = "/workItems/" + existingWorkItem.StringId; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePatchAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.SingleData.Should().NotBeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { var workItemInDatabase = await dbContext.WorkItems .Include(workItem => workItem.Assignee) .FirstWithIdAsync(existingWorkItem.Id); workItemInDatabase.Assignee.Should().BeNull(); }); }
public async Task Can_create_resource_with_inherited_attributes() { // Arrange var newMan = new Man { FamilyName = "Smith", IsRetired = true, HasBeard = true }; var requestBody = new { data = new { type = "men", attributes = new { familyName = newMan.FamilyName, isRetired = newMan.IsRetired, hasBeard = newMan.HasBeard } } }; const string route = "/men"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Type.Should().Be("men"); responseDocument.SingleData.Attributes["familyName"].Should().Be(newMan.FamilyName); responseDocument.SingleData.Attributes["isRetired"].Should().Be(newMan.IsRetired); responseDocument.SingleData.Attributes["hasBeard"].Should().Be(newMan.HasBeard); int newManId = int.Parse(responseDocument.SingleData.Id); await _testContext.RunOnDatabaseAsync(async dbContext => { Man manInDatabase = await dbContext.Men.FirstWithIdAsync(newManId); manInDatabase.FamilyName.Should().Be(newMan.FamilyName); manInDatabase.IsRetired.Should().Be(newMan.IsRetired); manInDatabase.HasBeard.Should().Be(newMan.HasBeard); }); }
public async Task Can_create_resource_with_annotated_relationships() { // Arrange var parentDirectory = new SystemDirectory { Name = "Shared", IsCaseSensitive = true }; var subdirectory = new SystemDirectory { Name = "Open Source", IsCaseSensitive = true }; var file = new SystemFile { FileName = "Main.cs", SizeInBytes = 100 }; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Directories.AddRange(parentDirectory, subdirectory); dbContext.Files.Add(file); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "systemDirectories", attributes = new { name = "Projects", isCaseSensitive = true }, relationships = new { subdirectories = new { data = new[] { new { type = "systemDirectories", id = subdirectory.StringId } } }, files = new { data = new[] { new { type = "systemFiles", id = file.StringId } } }, parent = new { data = new { type = "systemDirectories", id = parentDirectory.StringId } } } } }; const string route = "/systemDirectories"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecutePostAsync <Document>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); responseDocument.SingleData.Should().NotBeNull(); responseDocument.SingleData.Attributes["name"].Should().Be("Projects"); responseDocument.SingleData.Attributes["isCaseSensitive"].Should().Be(true); }
public async Task Can_create_resource_with_annotated_relationship() { // Arrange var existingTrack = _fakers.MusicTrack.Generate(); var newPlaylistName = _fakers.Playlist.Generate().Name; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", data = new { type = "playlists", attributes = new { name = newPlaylistName }, relationships = new { tracks = new { data = new[] { new { type = "musicTracks", id = existingTrack.StringId } } } } } } } }; var route = "/operations"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAtomicAsync <AtomicOperationsDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Results.Should().HaveCount(1); var newPlaylistId = long.Parse(responseDocument.Results[0].SingleData.Id); await _testContext.RunOnDatabaseAsync(async dbContext => { var playlistInDatabase = await dbContext.Playlists .Include(playlist => playlist.PlaylistMusicTracks) .ThenInclude(playlistMusicTrack => playlistMusicTrack.MusicTrack) .FirstAsync(playlist => playlist.Id == newPlaylistId); playlistInDatabase.PlaylistMusicTracks.Should().HaveCount(1); playlistInDatabase.PlaylistMusicTracks[0].MusicTrack.Id.Should().Be(existingTrack.Id); }); }
public async Task Can_get_primary_resources_with_include() { // Arrange var meetings = _fakers.Meeting.Generate(1); meetings[0].Attendees = _fakers.MeetingAttendee.Generate(1); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <Meeting>(); dbContext.Meetings.AddRange(meetings); await dbContext.SaveChangesAsync(); }); var route = "/meetings?include=attendees"; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteGetAsync <string>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Should().BeJson(@"{ ""links"": { ""self"": ""http://localhost/meetings?include=attendees"", ""first"": ""http://localhost/meetings?include=attendees"" }, ""data"": [ { ""type"": ""meetings"", ""id"": """ + meetings[0].StringId + @""", ""attributes"": { ""title"": """ + meetings[0].Title + @""", ""startTime"": """ + meetings[0].StartTime.ToString("O") + @""", ""duration"": """ + meetings[0].Duration + @""", ""location"": { ""lat"": " + meetings[0].Location.Latitude.ToString(CultureInfo.InvariantCulture) + @", ""lng"": " + meetings[0].Location.Longitude.ToString(CultureInfo.InvariantCulture) + @" } }, ""relationships"": { ""attendees"": { ""links"": { ""self"": ""http://localhost/meetings/" + meetings[0].StringId + @"/relationships/attendees"", ""related"": ""http://localhost/meetings/" + meetings[0].StringId + @"/attendees"" }, ""data"": [ { ""type"": ""meetingAttendees"", ""id"": """ + meetings[0].Attendees[0].StringId + @""" } ] } }, ""links"": { ""self"": ""http://localhost/meetings/" + meetings[0].StringId + @""" } } ], ""included"": [ { ""type"": ""meetingAttendees"", ""id"": """ + meetings[0].Attendees[0].StringId + @""", ""attributes"": { ""displayName"": """ + meetings[0].Attendees[0].DisplayName + @""" }, ""relationships"": { ""meeting"": { ""links"": { ""self"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @"/relationships/meeting"", ""related"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @"/meeting"" } } }, ""links"": { ""self"": ""http://localhost/meetingAttendees/" + meetings[0].Attendees[0].StringId + @""" } } ] }"); }
public async Task Can_update_resource_without_attributes_or_relationships() { // Arrange UserAccount existingUserAccount = _fakers.UserAccount.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingUserAccount); await dbContext.SaveChangesAsync(); }); var requestBody = new { data = new { type = "userAccounts", id = existingUserAccount.StringId, attributes = new { }, relationships = new { } } }; string route = "/userAccounts/" + existingUserAccount.StringId; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePatchAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NoContent); responseDocument.Should().BeEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { UserAccount userAccountInDatabase = await dbContext.UserAccounts.FirstWithIdAsync(existingUserAccount.Id); userAccountInDatabase.FirstName.Should().Be(existingUserAccount.FirstName); userAccountInDatabase.LastName.Should().Be(existingUserAccount.LastName); }); }
public async Task Returns_no_body_for_successful_HEAD_request() { // Arrange Meeting meeting = _fakers.Meeting.Generate(); await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Meetings.Add(meeting); await dbContext.SaveChangesAsync(); }); string route = "/meetings/" + meeting.StringId; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecuteHeadAsync <string>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Should().BeEmpty(); }
public async Task Can_rollback_on_error() { // Arrange var newArtistName = _fakers.Performer.Generate().ArtistName; var newBornAt = _fakers.Performer.Generate().BornAt; var newTitle = _fakers.MusicTrack.Generate().Title; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTablesAsync <Performer, MusicTrack>(); }); var requestBody = new { atomic__operations = new object[] { new { op = "add", data = new { type = "performers", attributes = new { artistName = newArtistName, bornAt = newBornAt } } }, new { op = "add", data = new { type = "musicTracks", attributes = new { title = newTitle }, relationships = new { performers = new { data = new[] { new { type = "performers", id = 99999999 } } } } } } } }; var route = "/operations"; // Act var(httpResponse, responseDocument) = await _testContext.ExecutePostAtomicAsync <ErrorDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.NotFound); responseDocument.Errors.Should().HaveCount(1); responseDocument.Errors[0].StatusCode.Should().Be(HttpStatusCode.NotFound); responseDocument.Errors[0].Title.Should().Be("A related resource does not exist."); responseDocument.Errors[0].Detail.Should().Be("Related resource of type 'performers' with ID '99999999' in relationship 'performers' does not exist."); responseDocument.Errors[0].Source.Pointer.Should().Be("/atomic:operations[1]"); await _testContext.RunOnDatabaseAsync(async dbContext => { var performersInDatabase = await dbContext.Performers.ToListAsync(); performersInDatabase.Should().BeEmpty(); var tracksInDatabase = await dbContext.MusicTracks.ToListAsync(); tracksInDatabase.Should().BeEmpty(); }); }
public async Task Can_filter_on_ID_in_primary_resources() { // Arrange var car = new Car { RegionId = 123, LicensePlate = "AA-BB-11" }; await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <Car>(); dbContext.Cars.Add(car); await dbContext.SaveChangesAsync(); }); const string route = "/cars?filter=any(id,'123:AA-BB-11','999:XX-YY-22')"; // Act var(httpResponse, responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(car.StringId); }
public async Task Can_create_user_with_password() { // Arrange User newUser = _fakers.User.Generate(); IRequestSerializer serializer = GetRequestSerializer <User>(user => new { user.Password, user.UserName }); string requestBody = serializer.Serialize(newUser); const string route = "/api/v1/users"; // Act (HttpResponseMessage httpResponse, string responseDocument) = await _testContext.ExecutePostAsync <string>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.Created); User responseUser = GetResponseDeserializer().DeserializeSingle <User>(responseDocument).Data; var document = JsonConvert.DeserializeObject <Document>(responseDocument); document.SingleData.Attributes.Should().NotContainKey("password"); document.SingleData.Attributes["userName"].Should().Be(newUser.UserName); await _testContext.RunOnDatabaseAsync(async dbContext => { User userInDatabase = await dbContext.Users.FirstWithIdAsync(responseUser.Id); userInDatabase.UserName.Should().Be(newUser.UserName); userInDatabase.Password.Should().Be(newUser.Password); }); }
public async Task Transforms_on_create_resource_with_side_effects() { // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService <ResourceDefinitionHitCounter>(); List <RecordCompany> newCompanies = _fakers.RecordCompany.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <RecordCompany>(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", data = new { type = "recordCompanies", attributes = new { name = newCompanies[0].Name, countryOfResidence = newCompanies[0].CountryOfResidence } } }, new { op = "add", data = new { type = "recordCompanies", attributes = new { name = newCompanies[1].Name, countryOfResidence = newCompanies[1].CountryOfResidence } } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, AtomicOperationsDocument responseDocument) = await _testContext.ExecutePostAtomicAsync <AtomicOperationsDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Results.Should().HaveCount(2); responseDocument.Results[0].SingleData.Attributes["name"].Should().Be(newCompanies[0].Name.ToUpperInvariant()); responseDocument.Results[0].SingleData.Attributes["countryOfResidence"].Should().Be(newCompanies[0].CountryOfResidence.ToUpperInvariant()); responseDocument.Results[1].SingleData.Attributes["name"].Should().Be(newCompanies[1].Name.ToUpperInvariant()); responseDocument.Results[1].SingleData.Attributes["countryOfResidence"].Should().Be(newCompanies[1].CountryOfResidence.ToUpperInvariant()); await _testContext.RunOnDatabaseAsync(async dbContext => { List <RecordCompany> companiesInDatabase = await dbContext.RecordCompanies.ToListAsync(); companiesInDatabase.Should().HaveCount(2); companiesInDatabase[0].Name.Should().Be(newCompanies[0].Name.ToUpperInvariant()); companiesInDatabase[0].CountryOfResidence.Should().Be(newCompanies[0].CountryOfResidence); companiesInDatabase[1].Name.Should().Be(newCompanies[1].Name.ToUpperInvariant()); companiesInDatabase[1].CountryOfResidence.Should().Be(newCompanies[1].CountryOfResidence); }); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[]
public async Task Can_create_HasMany_relationship() { // Arrange List <Performer> existingPerformers = _fakers.Performer.Generate(2); string newTitle = _fakers.MusicTrack.Generate().Title; await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.AddRange(existingPerformers); await dbContext.SaveChangesAsync(); }); var requestBody = new { atomic__operations = new[] { new { op = "add", data = new { type = "musicTracks", attributes = new { title = newTitle }, relationships = new { performers = new { data = new[] { new { type = "performers", id = existingPerformers[0].StringId }, new { type = "performers", id = existingPerformers[1].StringId } } } } } } } }; const string route = "/operations"; // Act (HttpResponseMessage httpResponse, AtomicOperationsDocument responseDocument) = await _testContext.ExecutePostAtomicAsync <AtomicOperationsDocument>(route, requestBody); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.Results.Should().HaveCount(1); responseDocument.Results[0].SingleData.Should().NotBeNull(); responseDocument.Results[0].SingleData.Type.Should().Be("musicTracks"); responseDocument.Results[0].SingleData.Attributes.Should().NotBeEmpty(); responseDocument.Results[0].SingleData.Relationships.Should().NotBeEmpty(); Guid newTrackId = Guid.Parse(responseDocument.Results[0].SingleData.Id); await _testContext.RunOnDatabaseAsync(async dbContext => { MusicTrack trackInDatabase = await dbContext.MusicTracks.Include(musicTrack => musicTrack.Performers).FirstWithIdAsync(newTrackId); trackInDatabase.Performers.Should().HaveCount(2); trackInDatabase.Performers.Should().ContainSingle(performer => performer.Id == existingPerformers[0].Id); trackInDatabase.Performers.Should().ContainSingle(performer => performer.Id == existingPerformers[1].Id); }); }
public async Task Can_filter_equality_in_primary_resources() { // Arrange List <BankAccount> accounts = _fakers.BankAccount.Generate(2); await _testContext.RunOnDatabaseAsync(async dbContext => { await dbContext.ClearTableAsync <BankAccount>(); dbContext.BankAccounts.AddRange(accounts); await dbContext.SaveChangesAsync(); }); string route = $"/bankAccounts?filter=equals(id,'{accounts[1].StringId}')"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync <Document>(route); // Assert httpResponse.Should().HaveStatusCode(HttpStatusCode.OK); responseDocument.ManyData.Should().HaveCount(1); responseDocument.ManyData[0].Id.Should().Be(accounts[1].StringId); }