11using System . Net ;
22using FluentAssertions ;
3+ using JsonApiDotNetCore . Configuration ;
34using JsonApiDotNetCore . Serialization . Objects ;
45using Microsoft . EntityFrameworkCore ;
6+ using Microsoft . Extensions . DependencyInjection ;
57using TestBuildingBlocks ;
68using Xunit ;
79
@@ -11,13 +13,45 @@ public sealed class IdObfuscationTests : IClassFixture<IntegrationTestContext<Te
1113{
1214 private readonly IntegrationTestContext < TestableStartup < ObfuscationDbContext > , ObfuscationDbContext > _testContext ;
1315 private readonly ObfuscationFakers _fakers = new ( ) ;
16+ private readonly HexadecimalCodec _codec = new ( ) ;
1417
1518 public IdObfuscationTests ( IntegrationTestContext < TestableStartup < ObfuscationDbContext > , ObfuscationDbContext > testContext )
1619 {
1720 _testContext = testContext ;
1821
1922 testContext . UseController < BankAccountsController > ( ) ;
2023 testContext . UseController < DebitCardsController > ( ) ;
24+
25+ var options = ( JsonApiOptions ) testContext . Factory . Services . GetRequiredService < IJsonApiOptions > ( ) ;
26+ options . UseRelativeLinks = true ;
27+ }
28+
29+ [ Fact ]
30+ public void Encodes_resource_ID ( )
31+ {
32+ // Arrange
33+ BankAccount account = _fakers . BankAccount . GenerateOne ( ) ;
34+ account . Id = 123 ;
35+
36+ // Act
37+ string ? stringId = _codec . Encode ( account . Id ) ;
38+
39+ // Assert
40+ stringId . Should ( ) . Be ( account . StringId ) ;
41+ }
42+
43+ [ Fact ]
44+ public void Decodes_resource_ID ( )
45+ {
46+ // Arrange
47+ BankAccount account = _fakers . BankAccount . GenerateOne ( ) ;
48+ account . Id = 123 ;
49+
50+ // Act
51+ int id = _codec . Decode ( account . StringId ) ;
52+
53+ // Assert
54+ id . Should ( ) . Be ( account . Id ) ;
2155 }
2256
2357 [ Fact ]
@@ -41,8 +75,16 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
4175 // Assert
4276 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
4377
44- responseDocument . Data . ManyValue . Should ( ) . HaveCount ( 1 ) ;
45- responseDocument . Data . ManyValue [ 0 ] . Id . Should ( ) . Be ( accounts [ 1 ] . StringId ) ;
78+ responseDocument . Links . Should ( ) . NotBeNull ( ) ;
79+ responseDocument . Links . Self . Should ( ) . Be ( route ) ;
80+ responseDocument . Links . First . Should ( ) . Be ( $ "/bankAccounts?filter=equals(id,%27{ accounts [ 1 ] . StringId } %27)") ;
81+
82+ responseDocument . Data . ManyValue . Should ( ) . ContainSingle ( ) . Which . With ( resource =>
83+ {
84+ resource . Id . Should ( ) . Be ( accounts [ 1 ] . StringId ) ;
85+ resource . Links . Should ( ) . NotBeNull ( ) ;
86+ resource . Links . Self . Should ( ) . Be ( $ "/bankAccounts/{ accounts [ 1 ] . StringId } ") ;
87+ } ) ;
4688 }
4789
4890 [ Fact ]
@@ -81,17 +123,15 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
81123 await dbContext . SaveChangesAsync ( ) ;
82124 } ) ;
83125
84- var codec = new HexadecimalCodec ( ) ;
85- string route = $ "/bankAccounts?filter=any(id,'{ accounts [ 1 ] . StringId } ','{ codec . Encode ( Unknown . TypedId . Int32 ) } ')";
126+ string route = $ "/bankAccounts?filter=any(id,'{ accounts [ 1 ] . StringId } ','{ _codec . Encode ( Unknown . TypedId . Int32 ) } ')";
86127
87128 // Act
88129 ( HttpResponseMessage httpResponse , Document responseDocument ) = await _testContext . ExecuteGetAsync < Document > ( route ) ;
89130
90131 // Assert
91132 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
92133
93- responseDocument . Data . ManyValue . Should ( ) . HaveCount ( 1 ) ;
94- responseDocument . Data . ManyValue [ 0 ] . Id . Should ( ) . Be ( accounts [ 1 ] . StringId ) ;
134+ responseDocument . Data . ManyValue . Should ( ) . ContainSingle ( ) . Which . Id . Should ( ) . Be ( accounts [ 1 ] . StringId ) ;
95135 }
96136
97137 [ Fact ]
@@ -135,8 +175,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
135175 // Assert
136176 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
137177
178+ responseDocument . Links . Should ( ) . NotBeNull ( ) ;
179+ responseDocument . Links . Self . Should ( ) . Be ( route ) ;
180+
138181 responseDocument . Data . SingleValue . Should ( ) . NotBeNull ( ) ;
139182 responseDocument . Data . SingleValue . Id . Should ( ) . Be ( card . StringId ) ;
183+ responseDocument . Data . SingleValue . Links . RefShould ( ) . NotBeNull ( ) . And . Subject . Self . Should ( ) . Be ( route ) ;
140184 }
141185
142186 [ Fact ]
@@ -160,9 +204,25 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
160204 // Assert
161205 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
162206
207+ responseDocument . Links . Should ( ) . NotBeNull ( ) ;
208+ responseDocument . Links . Self . Should ( ) . Be ( route ) ;
209+ responseDocument . Links . First . Should ( ) . Be ( route ) ;
210+
163211 responseDocument . Data . ManyValue . Should ( ) . HaveCount ( 2 ) ;
164- responseDocument . Data . ManyValue [ 0 ] . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
165- responseDocument . Data . ManyValue [ 1 ] . Id . Should ( ) . Be ( account . Cards [ 1 ] . StringId ) ;
212+
213+ responseDocument . Data . ManyValue [ 0 ] . With ( resource =>
214+ {
215+ resource . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
216+ resource . Links . Should ( ) . NotBeNull ( ) ;
217+ resource . Links . Self . Should ( ) . Be ( $ "/debitCards/{ account . Cards [ 0 ] . StringId } ") ;
218+ } ) ;
219+
220+ responseDocument . Data . ManyValue [ 1 ] . With ( resource =>
221+ {
222+ resource . Id . Should ( ) . Be ( account . Cards [ 1 ] . StringId ) ;
223+ resource . Links . Should ( ) . NotBeNull ( ) ;
224+ resource . Links . Self . Should ( ) . Be ( $ "/debitCards/{ account . Cards [ 1 ] . StringId } ") ;
225+ } ) ;
166226 }
167227
168228 [ Fact ]
@@ -186,13 +246,31 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
186246 // Assert
187247 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
188248
249+ responseDocument . Links . Should ( ) . NotBeNull ( ) ;
250+ responseDocument . Links . Self . Should ( ) . Be ( route ) ;
251+
189252 responseDocument . Data . SingleValue . Should ( ) . NotBeNull ( ) ;
190253 responseDocument . Data . SingleValue . Id . Should ( ) . Be ( account . StringId ) ;
191254
192- responseDocument . Included . Should ( ) . HaveCount ( 1 ) ;
193- responseDocument . Included [ 0 ] . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
194- responseDocument . Included [ 0 ] . Attributes . Should ( ) . HaveCount ( 1 ) ;
195- responseDocument . Included [ 0 ] . Relationships . Should ( ) . BeNull ( ) ;
255+ responseDocument . Data . SingleValue . Relationships . Should ( ) . ContainKey ( "cards" ) . WhoseValue . With ( value =>
256+ {
257+ value . Should ( ) . NotBeNull ( ) ;
258+ value . Data . ManyValue . Should ( ) . ContainSingle ( ) . Which . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
259+
260+ value . Links . Should ( ) . NotBeNull ( ) ;
261+ value . Links . Self . Should ( ) . Be ( $ "/bankAccounts/{ account . StringId } /relationships/cards") ;
262+ value . Links . Related . Should ( ) . Be ( $ "/bankAccounts/{ account . StringId } /cards") ;
263+ } ) ;
264+
265+ responseDocument . Included . Should ( ) . ContainSingle ( ) . Which . With ( resource =>
266+ {
267+ resource . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
268+ resource . Attributes . Should ( ) . HaveCount ( 1 ) ;
269+ resource . Relationships . Should ( ) . BeNull ( ) ;
270+
271+ resource . Links . Should ( ) . NotBeNull ( ) ;
272+ resource . Links . Self . Should ( ) . Be ( $ "/debitCards/{ account . Cards [ 0 ] . StringId } ") ;
273+ } ) ;
196274 }
197275
198276 [ Fact ]
@@ -216,8 +294,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
216294 // Assert
217295 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . OK ) ;
218296
219- responseDocument . Data . ManyValue . Should ( ) . HaveCount ( 1 ) ;
220- responseDocument . Data . ManyValue [ 0 ] . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
297+ responseDocument . Links . Should ( ) . NotBeNull ( ) ;
298+ responseDocument . Links . Self . Should ( ) . Be ( route ) ;
299+ responseDocument . Links . First . Should ( ) . Be ( route ) ;
300+
301+ responseDocument . Data . ManyValue . Should ( ) . ContainSingle ( ) . Which . Id . Should ( ) . Be ( account . Cards [ 0 ] . StringId ) ;
221302 }
222303
223304 [ Fact ]
@@ -266,11 +347,22 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
266347 httpResponse . ShouldHaveStatusCode ( HttpStatusCode . Created ) ;
267348
268349 responseDocument . Data . SingleValue . Should ( ) . NotBeNull ( ) ;
350+
351+ string newCardStringId = responseDocument . Data . SingleValue . Id . RefShould ( ) . NotBeNull ( ) . And . Subject ;
352+
353+ responseDocument . Data . SingleValue . Links . RefShould ( ) . NotBeNull ( ) . And . Subject . Self . Should ( ) . Be ( $ "/debitCards/{ newCardStringId } ") ;
269354 responseDocument . Data . SingleValue . Attributes . Should ( ) . ContainKey ( "ownerName" ) . WhoseValue . Should ( ) . Be ( newCard . OwnerName ) ;
270355 responseDocument . Data . SingleValue . Attributes . Should ( ) . ContainKey ( "pinCode" ) . WhoseValue . Should ( ) . Be ( newCard . PinCode ) ;
271356
272- var codec = new HexadecimalCodec ( ) ;
273- int newCardId = codec . Decode ( responseDocument . Data . SingleValue . Id ) ;
357+ responseDocument . Data . SingleValue . Relationships . Should ( ) . ContainKey ( "account" ) . WhoseValue . With ( value =>
358+ {
359+ value . Should ( ) . NotBeNull ( ) ;
360+ value . Links . Should ( ) . NotBeNull ( ) ;
361+ value . Links . Self . Should ( ) . Be ( $ "/debitCards/{ newCardStringId } /relationships/account") ;
362+ value . Links . Related . Should ( ) . Be ( $ "/debitCards/{ newCardStringId } /account") ;
363+ } ) ;
364+
365+ int newCardId = _codec . Decode ( responseDocument . Data . SingleValue . Id ) ;
274366
275367 await _testContext . RunOnDatabaseAsync ( async dbContext =>
276368 {
@@ -476,8 +568,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext =>
476568 public async Task Cannot_delete_unknown_resource ( )
477569 {
478570 // Arrange
479- var codec = new HexadecimalCodec ( ) ;
480- string ? stringId = codec . Encode ( Unknown . TypedId . Int32 ) ;
571+ string ? stringId = _codec . Encode ( Unknown . TypedId . Int32 ) ;
481572
482573 string route = $ "/bankAccounts/{ stringId } ";
483574
0 commit comments