Skip to content

Commit afddbe4

Browse files
committed
add tests for bulk operations
1 parent ff09ae7 commit afddbe4

File tree

6 files changed

+156
-25
lines changed

6 files changed

+156
-25
lines changed

Build.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ CheckLastExitCode
3232
dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
3333
CheckLastExitCode
3434

35+
dotnet test ./test/OperationsExampleTests/OperationsExampleTests.csproj
36+
CheckLastExitCode
37+
3538
dotnet build .\src\JsonApiDotNetCore -c Release
3639
CheckLastExitCode
3740

@@ -57,4 +60,4 @@ Else {
5760
Write-Output "RUNNING dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision"
5861
dotnet pack .\src\JsonApiDotNetCore -c Release -o .\artifacts --version-suffix=alpha1-$revision
5962
CheckLastExitCode
60-
}
63+
}

JsonApiDotnetCore.sln

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ EndProject
1515
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}"
1616
ProjectSection(SolutionItems) = preProject
1717
.gitignore = .gitignore
18+
.travis.yml = .travis.yml
19+
appveyor.yml = appveyor.yml
20+
Build.ps1 = Build.ps1
21+
build.sh = build.sh
1822
Directory.Build.props = Directory.Build.props
1923
README.md = README.md
2024
EndProjectSection

build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ dotnet restore
77

88
dotnet test ./test/UnitTests/UnitTests.csproj
99
dotnet test ./test/JsonApiDotNetCoreExampleTests/JsonApiDotNetCoreExampleTests.csproj
10-
dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
10+
dotnet test ./test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj
11+
dotnet test ./test/OperationsExampleTests/OperationsExampleTests.csproj

src/JsonApiDotNetCore/Controllers/JsonApiOperationsController.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,50 @@
55

66
namespace JsonApiDotNetCore.Controllers
77
{
8+
/// <summary>
9+
/// A controller to be used for bulk operations as defined in the json:api 1.1 specification
10+
/// </summary>
811
public class JsonApiOperationsController : Controller
912
{
1013
private readonly IOperationsProcessor _operationsProcessor;
1114

12-
public JsonApiOperationsController(
13-
IOperationsProcessor operationsProcessor)
15+
/// <param name="operationsProcessor">
16+
/// The processor to handle bulk operations.
17+
/// </param>
18+
public JsonApiOperationsController(IOperationsProcessor operationsProcessor)
1419
{
1520
_operationsProcessor = operationsProcessor;
1621
}
1722

23+
/// <summary>
24+
/// Bulk endpoint for json:api operations
25+
/// </summary>
26+
/// <param name="doc">
27+
/// A json:api operations request document
28+
/// </param>
29+
/// <example>
30+
/// <code>
31+
/// PATCH /api/bulk HTTP/1.1
32+
/// Content-Type: application/vnd.api+json
33+
///
34+
/// {
35+
/// "operations": [{
36+
/// "op": "add",
37+
/// "ref": {
38+
/// "type": "authors"
39+
/// },
40+
/// "data": {
41+
/// "type": "authors",
42+
/// "attributes": {
43+
/// "name": "jaredcnance"
44+
/// }
45+
/// }
46+
/// }]
47+
/// }
48+
/// </code>
49+
/// </example>
1850
[HttpPatch]
19-
public async Task<IActionResult> PatchAsync([FromBody] OperationsDocument doc)
51+
public virtual async Task<IActionResult> PatchAsync([FromBody] OperationsDocument doc)
2052
{
2153
if (doc == null) return new StatusCodeResult(422);
2254

test/OperationsExampleTests/Add/AddTests.cs

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Net;
44
using System.Threading.Tasks;
55
using Bogus;
6-
using JsonApiDotNetCore.Extensions;
76
using JsonApiDotNetCore.Models.Operations;
87
using Microsoft.EntityFrameworkCore;
98
using OperationsExample.Data;
@@ -24,20 +23,20 @@ public AddTests(Fixture fixture)
2423
}
2524

2625
[Fact]
27-
public async Task Can_Create_Article()
26+
public async Task Can_Create_Author()
2827
{
2928
// arrange
3029
var context = _fixture.GetService<AppDbContext>();
31-
var article = ArticleFactory.Get();
30+
var author = AuthorFactory.Get();
3231
var content = new
3332
{
3433
operations = new[] {
3534
new {
3635
op = "add",
3736
data = new {
38-
type = "articles",
37+
type = "authors",
3938
attributes = new {
40-
name = article.Name
39+
name = author.Name
4140
}
4241
}
4342
}
@@ -48,21 +47,21 @@ public async Task Can_Create_Article()
4847
var result = await _fixture.PatchAsync<OperationsDocument>("api/bulk", content);
4948

5049
// assert
51-
Assert.NotNull(result);
50+
Assert.NotNull(result.response);
5251
Assert.Equal(HttpStatusCode.OK, result.response.StatusCode);
5352

54-
var id = (string)result.data.Operations.Single().DataObject.Id;
55-
var lastArticle = await context.Articles.SingleAsync(a => a.StringId == id);
56-
Assert.Equal(article.Name, lastArticle.Name);
53+
var id = result.data.Operations.Single().DataObject.Id;
54+
var lastAuthor = await context.Authors.SingleAsync(a => a.StringId == id);
55+
Assert.Equal(author.Name, lastAuthor.Name);
5756
}
5857

5958
[Fact]
60-
public async Task Can_Create_Articles()
59+
public async Task Can_Create_Authors()
6160
{
6261
// arrange
6362
var expectedCount = _faker.Random.Int(1, 10);
6463
var context = _fixture.GetService<AppDbContext>();
65-
var articles = ArticleFactory.Get(expectedCount);
64+
var authors = AuthorFactory.Get(expectedCount);
6665
var content = new
6766
{
6867
operations = new List<object>()
@@ -76,30 +75,97 @@ public async Task Can_Create_Articles()
7675
op = "add",
7776
data = new
7877
{
79-
type = "articles",
78+
type = "authors",
8079
attributes = new
8180
{
82-
name = articles[i].Name
81+
name = authors[i].Name
8382
}
8483
}
8584
}
8685
);
8786
}
8887

8988
// act
90-
var result = await _fixture.PatchAsync<OperationsDocument>("api/bulk", content);
89+
var (response, data) = await _fixture.PatchAsync<OperationsDocument>("api/bulk", content);
9190

9291
// assert
93-
Assert.NotNull(result);
94-
Assert.Equal(HttpStatusCode.OK, result.response.StatusCode);
95-
Assert.Equal(expectedCount, result.data.Operations.Count);
92+
Assert.NotNull(response);
93+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
94+
Assert.Equal(expectedCount, data.Operations.Count);
9695

9796
for (int i = 0; i < expectedCount; i++)
9897
{
99-
var data = result.data.Operations[i].DataObject;
100-
var article = context.Articles.Single(a => a.StringId == data.Id.ToString());
101-
Assert.Equal(articles[i].Name, article.Name);
98+
var dataObject = data.Operations[i].DataObject;
99+
var author = context.Authors.Single(a => a.StringId == dataObject.ToString());
100+
Assert.Equal(authors[i].Name, author.Name);
102101
}
102+
}
103+
104+
[Fact]
105+
public async Task Can_Create_Author_With_Article()
106+
{
107+
// arrange
108+
var context = _fixture.GetService<AppDbContext>();
109+
var author = AuthorFactory.Get();
110+
var article = ArticleFactory.Get();
111+
const string authorLocalId = "author-1";
112+
113+
var content = new
114+
{
115+
operations = new object[] {
116+
new {
117+
op = "add",
118+
data = new {
119+
lid = authorLocalId,
120+
type = "authors",
121+
attributes = new {
122+
name = author.Name
123+
},
124+
}
125+
},
126+
new {
127+
op = "add",
128+
data = new {
129+
type = "articles",
130+
attributes = new {
131+
name = article.Name
132+
},
133+
relationships = new {
134+
author = new {
135+
data = new {
136+
type = "authors",
137+
lid = authorLocalId
138+
}
139+
}
140+
}
141+
}
142+
}
143+
}
144+
};
145+
146+
// act
147+
var (response, data) = await _fixture.PatchAsync<OperationsDocument>("api/bulk", content);
148+
149+
// assert
150+
Assert.NotNull(response);
151+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
152+
Assert.Equal(2, data.Operations.Count);
153+
154+
var authorOperationResult = data.Operations[0];
155+
var id = authorOperationResult.DataObject.Id;
156+
var lastAuthor = await context.Authors
157+
.Include(a => a.Articles)
158+
.SingleAsync(a => a.StringId == id);
159+
var articleOperationResult = data.Operations[1];
160+
161+
// author validation
162+
Assert.Equal(authorLocalId, authorOperationResult.DataObject.LocalId);
163+
Assert.Equal(author.Name, lastAuthor.Name);
164+
165+
// article validation
166+
Assert.Equal(1, lastAuthor.Articles.Count);
167+
Assert.Equal(article.Name, lastAuthor.Articles[0].Name);
168+
Assert.Equal(articleOperationResult.DataObject.Id, lastAuthor.Articles[0].StringId);
103169
}
104170
}
105171
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections.Generic;
2+
using Bogus;
3+
using OperationsExample.Models;
4+
5+
namespace OperationsExampleTests.Factories
6+
{
7+
public static class AuthorFactory
8+
{
9+
public static Author Get()
10+
{
11+
var faker = new Faker<Author>();
12+
faker.RuleFor(m => m.Name, f => f.Person.UserName);
13+
return faker.Generate();
14+
}
15+
16+
public static List<Author> Get(int count)
17+
{
18+
var authors = new List<Author>();
19+
for (int i = 0; i < count; i++)
20+
authors.Add(Get());
21+
22+
return authors;
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)