1
1
using System . Net ;
2
2
using FluentAssertions ;
3
+ using JsonApiDotNetCore . Configuration ;
3
4
using JsonApiDotNetCore . Serialization . Objects ;
4
- using JsonApiDotNetCoreMongoDbExampleTests . TestBuildingBlocks ;
5
- using MongoDB . Driver ;
6
- using MongoDB . Driver . Linq ;
5
+ using JsonApiDotNetCoreMongoDbTests . TestBuildingBlocks ;
6
+ using Microsoft . Extensions . DependencyInjection ;
7
+ using MongoDB . Bson ;
7
8
using Xunit ;
8
9
9
- namespace JsonApiDotNetCoreMongoDbExampleTests . IntegrationTests . AtomicOperations . Creating ;
10
+ namespace JsonApiDotNetCoreMongoDbTests . IntegrationTests . AtomicOperations . Creating ;
10
11
11
12
[ Collection ( "AtomicOperationsFixture" ) ]
12
13
public sealed class AtomicCreateResourceTests
13
14
{
14
- private readonly IntegrationTestContext < TestableStartup > _testContext ;
15
+ private readonly IntegrationTestContext < TestableStartup , OperationsDbContext > _testContext ;
15
16
private readonly OperationsFakers _fakers = new ( ) ;
16
17
17
18
public AtomicCreateResourceTests ( AtomicOperationsFixture fixture )
18
19
{
19
20
_testContext = fixture . TestContext ;
20
21
21
- fixture . TestContext . ConfigureServicesAfterStartup ( services => services . AddControllersFromExampleProject ( ) ) ;
22
+ var options = ( JsonApiOptions ) fixture . TestContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
23
+ options . AllowClientGeneratedIds = false ;
22
24
}
23
25
24
26
[ Fact ]
25
27
public async Task Can_create_resource ( )
26
28
{
27
29
// Arrange
28
- string newArtistName = _fakers . Performer . Generate ( ) . ArtistName ;
30
+ string newArtistName = _fakers . Performer . Generate ( ) . ArtistName ! ;
29
31
DateTimeOffset newBornAt = _fakers . Performer . Generate ( ) . BornAt ;
30
32
31
- await _testContext . RunOnDatabaseAsync ( async db =>
32
- {
33
- await db . EnsureEmptyCollectionAsync < Performer > ( ) ;
34
- } ) ;
35
-
36
33
var requestBody = new
37
34
{
38
35
atomic__operations = new [ ]
@@ -56,27 +53,29 @@ await _testContext.RunOnDatabaseAsync(async db =>
56
53
const string route = "/operations" ;
57
54
58
55
// Act
59
- ( HttpResponseMessage httpResponse , AtomicOperationsDocument responseDocument ) =
60
- await _testContext . ExecutePostAtomicAsync < AtomicOperationsDocument > ( route , requestBody ) ;
56
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAtomicAsync < Document > ( route , requestBody ) ;
61
57
62
58
// Assert
63
59
httpResponse . Should ( ) . HaveStatusCode ( HttpStatusCode . OK ) ;
64
60
65
- responseDocument . Results . Should ( ) . HaveCount ( 1 ) ;
66
- responseDocument . Results [ 0 ] . SingleData . Should ( ) . NotBeNull ( ) ;
67
- responseDocument . Results [ 0 ] . SingleData . Type . Should ( ) . Be ( "performers" ) ;
68
- responseDocument . Results [ 0 ] . SingleData . Attributes [ "artistName" ] . Should ( ) . Be ( newArtistName ) ;
69
- responseDocument . Results [ 0 ] . SingleData . Attributes [ "bornAt" ] . Should ( ) . BeCloseTo ( newBornAt ) ;
70
- responseDocument . Results [ 0 ] . SingleData . Relationships . Should ( ) . BeNull ( ) ;
61
+ responseDocument . Results . ShouldHaveCount ( 1 ) ;
71
62
72
- string newPerformerId = responseDocument . Results [ 0 ] . SingleData . Id ;
63
+ responseDocument . Results [ 0 ] . Data . SingleValue . ShouldNotBeNull ( ) . With ( resource =>
64
+ {
65
+ resource . Type . Should ( ) . Be ( "performers" ) ;
66
+ resource . Attributes . ShouldContainKey ( "artistName" ) . With ( value => value . Should ( ) . Be ( newArtistName ) ) ;
67
+ resource . Attributes . ShouldContainKey ( "bornAt" ) . With ( value => value . Should ( ) . Be ( newBornAt ) ) ;
68
+ resource . Relationships . Should ( ) . BeNull ( ) ;
69
+ } ) ;
70
+
71
+ string newPerformerId = responseDocument . Results [ 0 ] . Data . SingleValue ! . Id . ShouldNotBeNull ( ) ;
73
72
74
- await _testContext . RunOnDatabaseAsync ( async db =>
73
+ await _testContext . RunOnDatabaseAsync ( async dbContext =>
75
74
{
76
- Performer performerInDatabase = await db . GetCollection < Performer > ( ) . AsQueryable ( ) . FirstWithIdAsync ( newPerformerId ) ;
75
+ Performer performerInDatabase = await dbContext . Performers . FirstWithIdAsync ( newPerformerId ) ;
77
76
78
77
performerInDatabase . ArtistName . Should ( ) . Be ( newArtistName ) ;
79
- performerInDatabase . BornAt . Should ( ) . BeCloseTo ( newBornAt , TimeSpan . FromMilliseconds ( 20 ) ) ;
78
+ performerInDatabase . BornAt . Should ( ) . Be ( newBornAt ) ;
80
79
} ) ;
81
80
}
82
81
@@ -88,11 +87,6 @@ public async Task Can_create_resources()
88
87
89
88
List < MusicTrack > newTracks = _fakers . MusicTrack . Generate ( elementCount ) ;
90
89
91
- await _testContext . RunOnDatabaseAsync ( async db =>
92
- {
93
- await db . EnsureEmptyCollectionAsync < MusicTrack > ( ) ;
94
- } ) ;
95
-
96
90
var operationElements = new List < object > ( elementCount ) ;
97
91
98
92
for ( int index = 0 ; index < elementCount ; index ++ )
@@ -122,35 +116,38 @@ await _testContext.RunOnDatabaseAsync(async db =>
122
116
const string route = "/operations" ;
123
117
124
118
// Act
125
- ( HttpResponseMessage httpResponse , AtomicOperationsDocument responseDocument ) =
126
- await _testContext . ExecutePostAtomicAsync < AtomicOperationsDocument > ( route , requestBody ) ;
119
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAtomicAsync < Document > ( route , requestBody ) ;
127
120
128
121
// Assert
129
122
httpResponse . Should ( ) . HaveStatusCode ( HttpStatusCode . OK ) ;
130
123
131
- responseDocument . Results . Should ( ) . HaveCount ( elementCount ) ;
124
+ responseDocument . Results . ShouldHaveCount ( elementCount ) ;
132
125
133
126
for ( int index = 0 ; index < elementCount ; index ++ )
134
127
{
135
- ResourceObject singleData = responseDocument . Results [ index ] . SingleData ;
136
-
137
- singleData . Should ( ) . NotBeNull ( ) ;
138
- singleData . Type . Should ( ) . Be ( "musicTracks" ) ;
139
- singleData . Attributes [ "title" ] . Should ( ) . Be ( newTracks [ index ] . Title ) ;
140
- singleData . Attributes [ "lengthInSeconds" ] . As < decimal ? > ( ) . Should ( ) . BeApproximately ( newTracks [ index ] . LengthInSeconds ) ;
141
- singleData . Attributes [ "genre" ] . Should ( ) . Be ( newTracks [ index ] . Genre ) ;
142
- singleData . Attributes [ "releasedAt" ] . Should ( ) . BeCloseTo ( newTracks [ index ] . ReleasedAt ) ;
143
- singleData . Relationships . Should ( ) . BeNull ( ) ;
128
+ responseDocument . Results [ index ] . Data . SingleValue . ShouldNotBeNull ( ) . With ( resource =>
129
+ {
130
+ resource . ShouldNotBeNull ( ) ;
131
+ resource . Type . Should ( ) . Be ( "musicTracks" ) ;
132
+ resource . Attributes . ShouldContainKey ( "title" ) . With ( value => value . Should ( ) . Be ( newTracks [ index ] . Title ) ) ;
133
+
134
+ resource . Attributes . ShouldContainKey ( "lengthInSeconds" )
135
+ . With ( value => value . As < decimal ? > ( ) . Should ( ) . BeApproximately ( newTracks [ index ] . LengthInSeconds ) ) ;
136
+
137
+ resource . Attributes . ShouldContainKey ( "genre" ) . With ( value => value . Should ( ) . Be ( newTracks [ index ] . Genre ) ) ;
138
+ resource . Attributes . ShouldContainKey ( "releasedAt" ) . With ( value => value . Should ( ) . Be ( newTracks [ index ] . ReleasedAt ) ) ;
139
+
140
+ resource . Relationships . Should ( ) . BeNull ( ) ;
141
+ } ) ;
144
142
}
145
143
146
- string [ ] newTrackIds = responseDocument . Results . Select ( result => result . SingleData . Id ) . ToArray ( ) ;
144
+ string [ ] newTrackIds = responseDocument . Results . Select ( result => result . Data . SingleValue ! . Id . ShouldNotBeNull ( ) ) . ToArray ( ) ;
147
145
148
- await _testContext . RunOnDatabaseAsync ( async db =>
146
+ await _testContext . RunOnDatabaseAsync ( async dbContext =>
149
147
{
150
- List < MusicTrack > tracksInDatabase = await db . GetCollection < MusicTrack > ( ) . AsQueryable ( ) . Where ( musicTrack => newTrackIds . Contains ( musicTrack . Id ) )
151
- . ToListAsync ( ) ;
148
+ List < MusicTrack > tracksInDatabase = await dbContext . MusicTracks . ToListWhereAsync ( musicTrack => newTrackIds . Contains ( musicTrack . Id ) ) ;
152
149
153
- tracksInDatabase . Should ( ) . HaveCount ( elementCount ) ;
150
+ tracksInDatabase . ShouldHaveCount ( elementCount ) ;
154
151
155
152
for ( int index = 0 ; index < elementCount ; index ++ )
156
153
{
@@ -159,7 +156,7 @@ await _testContext.RunOnDatabaseAsync(async db =>
159
156
trackInDatabase . Title . Should ( ) . Be ( newTracks [ index ] . Title ) ;
160
157
trackInDatabase . LengthInSeconds . Should ( ) . BeApproximately ( newTracks [ index ] . LengthInSeconds ) ;
161
158
trackInDatabase . Genre . Should ( ) . Be ( newTracks [ index ] . Genre ) ;
162
- trackInDatabase . ReleasedAt . Should ( ) . BeCloseTo ( newTracks [ index ] . ReleasedAt , TimeSpan . FromMilliseconds ( 20 ) ) ;
159
+ trackInDatabase . ReleasedAt . Should ( ) . Be ( newTracks [ index ] . ReleasedAt ) ;
163
160
}
164
161
} ) ;
165
162
}
@@ -168,11 +165,6 @@ await _testContext.RunOnDatabaseAsync(async db =>
168
165
public async Task Can_create_resource_without_attributes_or_relationships ( )
169
166
{
170
167
// Arrange
171
- await _testContext . RunOnDatabaseAsync ( async db =>
172
- {
173
- await db . EnsureEmptyCollectionAsync < Performer > ( ) ;
174
- } ) ;
175
-
176
168
var requestBody = new
177
169
{
178
170
atomic__operations = new [ ]
@@ -197,27 +189,75 @@ await _testContext.RunOnDatabaseAsync(async db =>
197
189
const string route = "/operations" ;
198
190
199
191
// Act
200
- ( HttpResponseMessage httpResponse , AtomicOperationsDocument responseDocument ) =
201
- await _testContext . ExecutePostAtomicAsync < AtomicOperationsDocument > ( route , requestBody ) ;
192
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAtomicAsync < Document > ( route , requestBody ) ;
202
193
203
194
// Assert
204
195
httpResponse . Should ( ) . HaveStatusCode ( HttpStatusCode . OK ) ;
205
196
206
- responseDocument . Results . Should ( ) . HaveCount ( 1 ) ;
207
- responseDocument . Results [ 0 ] . SingleData . Should ( ) . NotBeNull ( ) ;
208
- responseDocument . Results [ 0 ] . SingleData . Type . Should ( ) . Be ( "performers" ) ;
209
- responseDocument . Results [ 0 ] . SingleData . Attributes [ "artistName" ] . Should ( ) . BeNull ( ) ;
210
- responseDocument . Results [ 0 ] . SingleData . Attributes [ "bornAt" ] . Should ( ) . BeCloseTo ( default ( DateTimeOffset ) ) ;
211
- responseDocument . Results [ 0 ] . SingleData . Relationships . Should ( ) . BeNull ( ) ;
197
+ responseDocument . Results . ShouldHaveCount ( 1 ) ;
198
+
199
+ responseDocument . Results [ 0 ] . Data . SingleValue . ShouldNotBeNull ( ) . With ( resource =>
200
+ {
201
+ resource . Type . Should ( ) . Be ( "performers" ) ;
202
+ resource . Attributes . ShouldContainKey ( "artistName" ) . With ( value => value . Should ( ) . BeNull ( ) ) ;
203
+ resource . Attributes . ShouldContainKey ( "bornAt" ) . With ( value => value . Should ( ) . Be ( default ( DateTimeOffset ) ) ) ;
204
+ resource . Relationships . Should ( ) . BeNull ( ) ;
205
+ } ) ;
212
206
213
- string newPerformerId = responseDocument . Results [ 0 ] . SingleData . Id ;
207
+ string newPerformerId = responseDocument . Results [ 0 ] . Data . SingleValue ! . Id . ShouldNotBeNull ( ) ;
214
208
215
- await _testContext . RunOnDatabaseAsync ( async db =>
209
+ await _testContext . RunOnDatabaseAsync ( async dbContext =>
216
210
{
217
- Performer performerInDatabase = await db . GetCollection < Performer > ( ) . AsQueryable ( ) . FirstWithIdAsync ( newPerformerId ) ;
211
+ Performer performerInDatabase = await dbContext . Performers . FirstWithIdAsync ( newPerformerId ) ;
218
212
219
213
performerInDatabase . ArtistName . Should ( ) . BeNull ( ) ;
220
214
performerInDatabase . BornAt . Should ( ) . Be ( default ) ;
221
215
} ) ;
222
216
}
217
+
218
+ [ Fact ]
219
+ public async Task Cannot_create_resource_with_client_generated_ID ( )
220
+ {
221
+ // Arrange
222
+ MusicTrack newTrack = _fakers . MusicTrack . Generate ( ) ;
223
+ newTrack . Id = ObjectId . GenerateNewId ( ) . ToString ( ) ;
224
+
225
+ var requestBody = new
226
+ {
227
+ atomic__operations = new [ ]
228
+ {
229
+ new
230
+ {
231
+ op = "add" ,
232
+ data = new
233
+ {
234
+ type = "musicTracks" ,
235
+ id = newTrack . StringId ,
236
+ attributes = new
237
+ {
238
+ title = newTrack . Title
239
+ }
240
+ }
241
+ }
242
+ }
243
+ } ;
244
+
245
+ const string route = "/operations" ;
246
+
247
+ // Act
248
+ ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecutePostAtomicAsync < Document > ( route , requestBody ) ;
249
+
250
+ // Assert
251
+ httpResponse . Should ( ) . HaveStatusCode ( HttpStatusCode . Forbidden ) ;
252
+
253
+ responseDocument . Errors . ShouldHaveCount ( 1 ) ;
254
+
255
+ ErrorObject error = responseDocument . Errors [ 0 ] ;
256
+ error . StatusCode . Should ( ) . Be ( HttpStatusCode . Forbidden ) ;
257
+ error . Title . Should ( ) . Be ( "Failed to deserialize request body: The use of client-generated IDs is disabled." ) ;
258
+ error . Detail . Should ( ) . BeNull ( ) ;
259
+ error . Source . ShouldNotBeNull ( ) ;
260
+ error . Source . Pointer . Should ( ) . Be ( "/atomic:operations[0]/data/id" ) ;
261
+ error . Meta . ShouldContainKey ( "requestBody" ) . With ( value => value . ShouldNotBeNull ( ) . ToString ( ) . ShouldNotBeEmpty ( ) ) ;
262
+ }
223
263
}
0 commit comments