-
Notifications
You must be signed in to change notification settings - Fork 349
Expand file tree
/
Copy pathUpdateEntityTests.cs
More file actions
1896 lines (1737 loc) · 87 KB
/
Copy pathUpdateEntityTests.cs
File metadata and controls
1896 lines (1737 loc) · 87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Azure.DataApiBuilder.Config.Converters;
namespace Cli.Tests
{
/// <summary>
/// Tests for Updating Entity.
/// </summary>
[TestClass]
public class UpdateEntityTests : VerifyBase
{
[TestInitialize]
public void TestInitialize()
{
ILoggerFactory loggerFactory = TestLoggerSupport.ProvisionLoggerFactory();
SetLoggerForCliConfigGenerator(loggerFactory.CreateLogger<ConfigGenerator>());
SetCliUtilsLogger(loggerFactory.CreateLogger<Utils>());
}
/// <summary>
/// Simple test to update an entity permission by adding a new action.
/// Initially it contained only "read" and "update". adding a new action "create"
/// </summary>
[TestMethod, Description("it should update the permission by adding a new action.")]
public Task TestUpdateEntityPermission()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "create" },
fieldsToInclude: new string[] { "id", "rating" },
fieldsToExclude: new string[] { "level" });
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"",""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an entity permission by creating a new role.
/// Initially the role "authenticated" was not present, so it will create a new role.
/// </summary>
[TestMethod, Description("it should update the permission by adding a new role.")]
public Task TestUpdateEntityPermissionByAddingNewRole()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "authenticated", "*" },
fieldsToInclude: new string[] { "id", "rating" },
fieldsToExclude: new string[] { "level" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"" : [""read"", ""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update the action which already exists in permissions.
/// Adding fields to Include/Exclude to update action.
/// </summary>
[TestMethod, Description("Should update the action which already exists in permissions.")]
public Task TestUpdateEntityPermissionWithExistingAction()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "update" },
fieldsToInclude: new string[] { "id", "rating" },
fieldsToExclude: new string[] { "level" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [ ""read"", ""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an entity permission which has action as WILDCARD.
/// It will update only "read" and "delete".
/// </summary>
[TestMethod, Description("it should update the permission which has action as WILDCARD.")]
public Task TestUpdateEntityPermissionHavingWildcardAction()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "read,delete" },
fieldsToInclude: new string[] { "id", "type", "quantity" },
fieldsToExclude: new string[] { }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
{
""action"": ""*"",
""fields"": {
""include"": [""id"", ""rating""],
""exclude"": [""level""]
}
}
]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an entity permission with new action as WILDCARD.
/// It will apply the update as WILDCARD.
/// </summary>
[TestMethod, Description("it should update the permission with \"*\".")]
public Task TestUpdateEntityPermissionWithWildcardAction()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "*" },
fieldsToInclude: new string[] { "id", "rating" },
fieldsToExclude: new string[] { "level" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"", ""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an entity by adding a new relationship.
/// </summary>
[TestMethod, Description("it should add a new relationship")]
public Task TestUpdateEntityByAddingNewRelationship()
{
UpdateOptions options = GenerateBaseUpdateOptions(
entity: "SecondEntity",
relationship: "r2",
cardinality: "many",
targetEntity: "FirstEntity"
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""FirstEntity"": {
""source"": ""Table1"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""create"",
""read""
]
}
],
""relationships"": {
""r1"": {
""cardinality"": ""one"",
""target.entity"": ""SecondEntity""
}
}
},
""SecondEntity"": {
""source"": ""Table2"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""create"",
""read""
]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an existing relationship.
/// It will add source.fields, target.fields, linking.object, linking.source.fields, linking.target.fields
/// </summary>
[TestMethod, Description("it should update an existing relationship")]
public Task TestUpdateEntityByModifyingRelationship()
{
UpdateOptions options = GenerateBaseUpdateOptions(
entity: "SecondEntity",
relationship: "r2",
cardinality: "many",
targetEntity: "FirstEntity",
linkingObject: "entity_link",
linkingSourceFields: new string[] { "eid1" },
linkingTargetFields: new string[] { "eid2", "fid2" },
relationshipFields: new string[] { "e1", "e2,t2" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""FirstEntity"": {
""source"": ""Table1"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""create"",
""read""
]
}
],
""relationships"": {
""r1"": {
""cardinality"": ""one"",
""target.entity"": ""SecondEntity""
}
}
},
""SecondEntity"": {
""source"": ""Table2"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""create"",
""read""
]
}
],
""relationships"": {
""r2"": {
""cardinality"": ""many"",
""target.entity"": ""FirstEntity""
}
}
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Simple test to update an entity cache.
/// </summary>
[TestMethod, Description("It should update the cache into true.")]
public Task TestUpdateEntityCaching()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
cacheEnabled: "true",
cacheTtlSeconds: "1"
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""cache"": {
""enabled"": false,
""ttlseconds"": 0
}
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Test to check creation of a new relationship
/// </summary>
[TestMethod]
public void TestCreateNewRelationship()
{
UpdateOptions options = GenerateBaseUpdateOptions(
cardinality: "many",
targetEntity: "FirstEntity",
linkingObject: "entity_link",
linkingSourceFields: new string[] { "eid1" },
linkingTargetFields: new string[] { "eid2", "fid2" },
relationshipFields: new string[] { "e1", "e2,t2" }
);
EntityRelationship? relationship = CreateNewRelationshipWithUpdateOptions(options);
Assert.IsNotNull(relationship);
Assert.AreEqual(Cardinality.Many, relationship.Cardinality);
Assert.AreEqual("entity_link", relationship.LinkingObject);
Assert.AreEqual("FirstEntity", relationship.TargetEntity);
CollectionAssert.AreEqual(new string[] { "e1" }, relationship.SourceFields);
CollectionAssert.AreEqual(new string[] { "e2", "t2" }, relationship.TargetFields);
CollectionAssert.AreEqual(new string[] { "eid1" }, relationship.LinkingSourceFields);
CollectionAssert.AreEqual(new string[] { "eid2", "fid2" }, relationship.LinkingTargetFields);
}
/// <summary>
/// Test to check creation of a relationship with multiple linking fields
/// </summary>
[TestMethod]
public void TestCreateNewRelationshipWithMultipleLinkingFields()
{
UpdateOptions options = GenerateBaseUpdateOptions(
cardinality: "many",
targetEntity: "FirstEntity",
linkingObject: "entity_link",
linkingSourceFields: new string[] { "eid1", "fid1" },
linkingTargetFields: new string[] { "eid2", "fid2" },
relationshipFields: new string[] { "e1", "e2,t2" }
);
EntityRelationship? relationship = CreateNewRelationshipWithUpdateOptions(options);
Assert.IsNotNull(relationship);
Assert.AreEqual(Cardinality.Many, relationship.Cardinality);
Assert.AreEqual("entity_link", relationship.LinkingObject);
Assert.AreEqual("FirstEntity", relationship.TargetEntity);
CollectionAssert.AreEqual(new string[] { "e1" }, relationship.SourceFields);
CollectionAssert.AreEqual(new string[] { "e2", "t2" }, relationship.TargetFields);
CollectionAssert.AreEqual(new string[] { "eid1", "fid1" }, relationship.LinkingSourceFields);
CollectionAssert.AreEqual(new string[] { "eid2", "fid2" }, relationship.LinkingTargetFields);
}
/// <summary>
/// Test to check creation of a relationship with multiple relationship fields
/// </summary>
[TestMethod]
public void TestCreateNewRelationshipWithMultipleRelationshipFields()
{
UpdateOptions options = GenerateBaseUpdateOptions(
cardinality: "many",
targetEntity: "FirstEntity",
linkingObject: "entity_link",
linkingSourceFields: new string[] { "eid1" },
linkingTargetFields: new string[] { "eid2", "fid2" },
relationshipFields: new string[] { "e1,t1", "e2,t2" }
);
EntityRelationship? relationship = CreateNewRelationshipWithUpdateOptions(options);
Assert.IsNotNull(relationship);
Assert.AreEqual(Cardinality.Many, relationship.Cardinality);
Assert.AreEqual("entity_link", relationship.LinkingObject);
Assert.AreEqual("FirstEntity", relationship.TargetEntity);
CollectionAssert.AreEqual(new string[] { "e1", "t1" }, relationship.SourceFields);
CollectionAssert.AreEqual(new string[] { "e2", "t2" }, relationship.TargetFields);
CollectionAssert.AreEqual(new string[] { "eid1" }, relationship.LinkingSourceFields);
CollectionAssert.AreEqual(new string[] { "eid2", "fid2" }, relationship.LinkingTargetFields);
}
/// <summary>
/// Update Entity with new Policy and Field properties
/// </summary>
[DataTestMethod]
[DataRow(new string[] { "*" }, new string[] { "level", "rating" }, "@claims.name eq 'dab'", "@claims.id eq @item.id", "PolicyAndFields", DisplayName = "Check adding new Policy and Fields to Action")]
[DataRow(new string[] { }, new string[] { }, "@claims.name eq 'dab'", "@claims.id eq @item.id", "Policy", DisplayName = "Check adding new Policy to Action")]
[DataRow(new string[] { "*" }, new string[] { "level", "rating" }, null, null, "Fields", DisplayName = "Check adding new fieldsToInclude and FieldsToExclude to Action")]
public Task TestUpdateEntityWithPolicyAndFieldProperties(IEnumerable<string>? fieldsToInclude,
IEnumerable<string>? fieldsToExclude,
string? policyRequest,
string? policyDatabase,
string check)
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "delete" },
fieldsToInclude: fieldsToInclude,
fieldsToExclude: fieldsToExclude,
policyRequest: policyRequest,
policyDatabase: policyDatabase);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY);
VerifySettings settings = new();
settings.UseHashedParameters(fieldsToInclude, fieldsToExclude, policyRequest, policyDatabase);
return ExecuteVerifyTest(initialConfig, options, settings);
}
/// <summary>
/// Simple test to verify success on updating a source from string to source object for valid fields.
/// </summary>
[DataTestMethod]
[DataRow("s001.book", null, new string[] { "anonymous", "*" }, null, null, "UpdateSourceName", DisplayName = "Updating sourceName with no change in parameters or keyfields.")]
[DataRow(null, "view", null, null, new string[] { "col1", "col2" }, "ConvertToView", DisplayName = "Source KeyFields with View")]
[DataRow(null, "table", null, null, new string[] { "id", "name" }, "ConvertToTable", DisplayName = "Source KeyFields with Table")]
[DataRow(null, null, null, null, new string[] { "id", "name" }, "ConvertToDefaultType", DisplayName = "Source KeyFields with SourceType not provided")]
public Task TestUpdateSourceStringToDatabaseSourceObject(
string? source,
string? sourceType,
string[]? permissions,
IEnumerable<string>? parameters,
IEnumerable<string>? keyFields,
string task)
{
UpdateOptions options = GenerateBaseUpdateOptions(
permissions: permissions,
source: source,
sourceType: sourceType,
sourceParameters: parameters,
sourceKeyFields: keyFields);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, BASIC_ENTITY_WITH_ANONYMOUS_ROLE);
VerifySettings settings = new();
settings.UseHashedParameters(source, sourceType, permissions, parameters, keyFields);
return ExecuteVerifyTest(initialConfig, options, settings);
}
[TestMethod]
public Task UpdateDatabaseSourceName()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "newSourceName",
permissions: new string[] { "anonymous", "execute" },
entity: "MyEntity");
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY_WITH_STORED_PROCEDURE);
return ExecuteVerifyTest(initialConfig, options);
}
[TestMethod]
public Task UpdateDatabaseSourceParameters()
{
UpdateOptions options = GenerateBaseUpdateOptions(
permissions: new string[] { "anonymous", "execute" },
sourceParameters: new string[] { "param1:dab", "param2:false" }
);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY_WITH_STORED_PROCEDURE);
return ExecuteVerifyTest(initialConfig, options);
}
[TestMethod]
public Task UpdateDatabaseSourceKeyFields()
{
UpdateOptions options = GenerateBaseUpdateOptions(
permissions: new string[] { "anonymous", "read" },
sourceKeyFields: new string[] { "col1", "col2" });
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY_WITH_SOURCE_AS_TABLE);
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Converts one source object type to another.
/// Also testing automatic update for parameter and keyfields to null in case
/// of table/view, and stored-procedure respectively.
/// Updating Table with all supported CRUD action to Stored-Procedure should fail.
/// </summary>
[DataTestMethod]
[DataRow(SINGLE_ENTITY_WITH_ONLY_READ_PERMISSION, "stored-procedure", new string[] { "param1:123", "param2:hello", "param3:true" },
null, SINGLE_ENTITY_WITH_STORED_PROCEDURE, new string[] { "anonymous", "execute" }, false, true,
DisplayName = "PASS - Convert table to stored-procedure with valid parameters.")]
[DataRow(SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, "stored-procedure", null, new string[] { "col1", "col2" },
SINGLE_ENTITY_WITH_STORED_PROCEDURE, new string[] { "anonymous", "execute" }, false, false,
DisplayName = "FAIL - Convert table to stored-procedure with invalid KeyFields.")]
[DataRow(SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, "stored-procedure", null, null, SINGLE_ENTITY_WITH_STORED_PROCEDURE, null,
true, true, DisplayName = "PASS - Convert table with wildcard CRUD operation to stored-procedure.")]
[DataRow(SINGLE_ENTITY_WITH_STORED_PROCEDURE, "table", null, new string[] { "id", "name" },
SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, new string[] { "anonymous", "*" }, false, true,
DisplayName = "PASS - Convert stored-procedure to table with valid KeyFields.")]
[DataRow(SINGLE_ENTITY_WITH_STORED_PROCEDURE, "view", null, new string[] { "col1", "col2" },
SINGLE_ENTITY_WITH_SOURCE_AS_VIEW, new string[] { "anonymous", "*" }, false, true,
DisplayName = "PASS - Convert stored-procedure to view with valid KeyFields.")]
[DataRow(SINGLE_ENTITY_WITH_STORED_PROCEDURE, "table", new string[] { "param1:kind", "param2:true" },
null, SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, null, false, false,
DisplayName = "FAIL - Convert stored-procedure to table with parameters is not allowed.")]
[DataRow(SINGLE_ENTITY_WITH_STORED_PROCEDURE, "table", null, null, SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, null,
true, true, DisplayName = "PASS - Convert stored-procedure to table with no parameters or KeyFields.")]
[DataRow(SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, "view", null, new string[] { "col1", "col2" },
SINGLE_ENTITY_WITH_SOURCE_AS_VIEW, null, false, true,
DisplayName = "PASS - Convert table to view with KeyFields.")]
[DataRow(SINGLE_ENTITY_WITH_SOURCE_AS_TABLE, "view", new string[] { "param1:kind", "param2:true" }, null,
SINGLE_ENTITY_WITH_SOURCE_AS_VIEW, null, false, false,
DisplayName = "FAIL - Convert table to view with parameters is not allowed.")]
public Task TestConversionOfSourceObject(
string initialSourceObjectEntity,
string sourceType,
IEnumerable<string>? parameters,
string[]? keyFields,
string updatedSourceObjectEntity,
string[]? permissions,
bool expectNoKeyFieldsAndParameters,
bool expectSuccess)
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "s001.book",
permissions: permissions,
sourceType: sourceType,
sourceParameters: parameters,
sourceKeyFields: keyFields);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, initialSourceObjectEntity);
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.AreEqual(expectSuccess, TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig updatedConfig));
if (expectSuccess)
{
Assert.AreNotSame(runtimeConfig, updatedConfig);
VerifySettings settings = new();
settings.UseHashedParameters(sourceType, parameters, keyFields, permissions, expectNoKeyFieldsAndParameters);
return Verify(updatedConfig, settings);
}
return Task.CompletedTask;
}
/// <summary>
/// Update Policy for an action
/// </summary>
[TestMethod]
public Task TestUpdatePolicy()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "delete" },
policyRequest: "@claims.name eq 'api_builder'",
policyDatabase: "@claims.name eq @item.name"
);
string? initialConfig = AddPropertiesToJson(INITIAL_CONFIG, ENTITY_CONFIG_WITH_POLCIY_AND_ACTION_FIELDS);
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Test to verify updating permissions for stored-procedure.
/// Checks:
/// 1. Updating a stored-procedure with WILDCARD/CRUD action should fail.
/// 2. Adding a new role/Updating an existing role with execute action should succeeed.
/// </summary>
[DataTestMethod]
[DataRow("anonymous", "*", true, DisplayName = "PASS: Stored-Procedure updated with wildcard operation")]
[DataRow("anonymous", "execute", true, DisplayName = "PASS: Stored-Procedure updated with execute operation")]
[DataRow("anonymous", "create,read", false, DisplayName = "FAIL: Stored-Procedure updated with CRUD action.")]
[DataRow("authenticated", "*", true, DisplayName = "PASS: Stored-Procedure updated with wildcard operation for an existing role.")]
[DataRow("authenticated", "execute", true, DisplayName = "PASS: Stored-Procedure updated with execute operation for an existing role.")]
[DataRow("authenticated", "create,read", false, DisplayName = "FAIL: Stored-Procedure updated with CRUD action for an existing role.")]
public void TestUpdatePermissionsForStoredProcedure(
string role,
string operations,
bool isSuccess
)
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "my_sp",
permissions: new string[] { role, operations },
sourceType: "stored-procedure");
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY_WITH_STORED_PROCEDURE);
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.AreEqual(isSuccess, TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Test to Update Entity with New mappings
/// </summary>
[TestMethod]
public Task TestUpdateEntityWithMappings()
{
UpdateOptions options = GenerateBaseUpdateOptions(map: new string[] { "id:Identity", "name:Company Name" });
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"",""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Test to Update stored procedure action. Stored procedures support only execute action.
/// An attempt to update to another action should be unsuccessful.
/// </summary>
[TestMethod]
public void TestUpdateActionOfStoredProcedureRole()
{
UpdateOptions options = GenerateBaseUpdateOptions(
permissions: new string[] { "authenticated", "create" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": {
""object"": ""MySp"",
""type"": ""stored-procedure""
},
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""execute""
]
},
{
""role"": ""authenticated"",
""actions"": [
""execute""
]
}
]
}
}
}";
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Test to Update Entity with New mappings containing special unicode characters
/// </summary>
[TestMethod]
public Task TestUpdateEntityWithSpecialCharacterInMappings()
{
UpdateOptions options = GenerateBaseUpdateOptions(
map: new string[] { "Macaroni:Mac & Cheese", "region:United State's Region", "russian:русский", "chinese:中文" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"",""update""]
}
]
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Test to Update existing mappings of an entity
/// </summary>
[TestMethod]
public Task TestUpdateExistingMappings()
{
UpdateOptions options = GenerateBaseUpdateOptions(
map: new string[] { "name:Company Name", "addr:Company Address", "number:Contact Details" }
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"",""update""]
}
],
""mappings"": {
""id"": ""Identity"",
""name"": ""Company Name""
}
}
}
}";
return ExecuteVerifyTest(initialConfig, options);
}
/// <summary>
/// Test to validate various updates to various combinations of
/// REST path, REST methods, GraphQL Type and GraphQL Operation are working as intended.
/// </summary>
/// <param name="restMethods">List of REST Methods that are configured for the entity</param>
/// <param name="graphQLOperation">GraphQL Operation configured for the entity</param>
/// <param name="restRoute">REST Path configured for the entity</param>
/// <param name="graphQLType">GraphQL Type configured for the entity</param>
/// <param name="testType">Scenario that is tested. It is also used to construct the expected JSON.</param>
[DataTestMethod]
[DataRow(null, null, "true", null, "RestEnabled", DisplayName = "Entity Update - REST enabled without any methods explicitly configured")]
[DataRow(null, null, "book", null, "CustomRestPath", DisplayName = "Entity Update - Custom REST path defined without any methods explicitly configured")]
[DataRow(new string[] { "Get", "Post", "Patch" }, null, null, null, "RestMethods", DisplayName = "Entity Update - REST methods defined without REST Path explicitly configured")]
[DataRow(new string[] { "Get", "Post", "Patch" }, null, "true", null, "RestEnabledWithMethods", DisplayName = "Entity Update - REST enabled along with some methods")]
[DataRow(new string[] { "Get", "Post", "Patch" }, null, "book", null, "CustomRestPathWithMethods", DisplayName = "Entity Update - Custom REST path defined along with some methods")]
[DataRow(null, null, null, "true", "GQLEnabled", DisplayName = "Entity Update - GraphQL enabled without any operation explicitly configured")]
[DataRow(null, null, null, "book", "GQLCustomType", DisplayName = "Entity Update - Custom GraphQL Type defined without any operation explicitly configured")]
[DataRow(null, null, null, "book:books", "GQLSingularPluralCustomType", DisplayName = "Entity Update - SingularPlural GraphQL Type enabled without any operation explicitly configured")]
[DataRow(null, "Query", null, "true", "GQLEnabledWithCustomOperation", DisplayName = "Entity Update - GraphQL enabled with Query operation")]
[DataRow(null, "Query", null, "book", "GQLCustomTypeAndOperation", DisplayName = "Entity Update - Custom GraphQL Type defined along with Query operation")]
[DataRow(null, "Query", null, "book:books", "GQLSingularPluralTypeAndOperation", DisplayName = "Entity Update - SingularPlural GraphQL Type defined along with Query operation")]
[DataRow(null, null, "true", "true", "RestAndGQLEnabled", DisplayName = "Entity Update - Both REST and GraphQL enabled without any methods and operations configured explicitly")]
[DataRow(null, null, "false", "false", "RestAndGQLDisabled", DisplayName = "Entity Update - Both REST and GraphQL disabled without any methods and operations configured explicitly")]
[DataRow(new string[] { "Get" }, "Query", "true", "true", "CustomRestMethodAndGqlOperation", DisplayName = "Entity Update - Both REST and GraphQL enabled with custom REST methods and GraphQL operations")]
[DataRow(new string[] { "Post", "Patch", "Put" }, "Query", "book", "book:books", "CustomRestAndGraphQLAll", DisplayName = "Entity Update - Configuration with REST Path, Methods and GraphQL Type, Operation")]
public Task TestUpdateRestAndGraphQLSettingsForStoredProcedures(
IEnumerable<string>? restMethods,
string? graphQLOperation,
string? restRoute,
string? graphQLType,
string testType)
{
UpdateOptions options = GenerateBaseUpdateOptions(
restRoute: restRoute,
graphQLType: graphQLType,
restMethodsForStoredProcedure: restMethods,
graphQLOperationForStoredProcedure: graphQLOperation
);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SP_DEFAULT_REST_METHODS_GRAPHQL_OPERATION);
VerifySettings settings = new();
settings.UseHashedParameters(restMethods, graphQLOperation, restRoute, graphQLType, testType);
return ExecuteVerifyTest(initialConfig, options, settings);
}
/// <summary>
/// Validates that updating an entity with conflicting options such as disabling an entity
/// for GraphQL but specifying GraphQL Operations results in a failure. Likewise for REST Path and
/// Methods.
/// </summary>
/// <param name="restMethods"></param>
/// <param name="graphQLOperation"></param>
/// <param name="restRoute"></param>
/// <param name="graphQLType"></param>
[DataTestMethod]
[DataRow(null, "Mutation", "true", "false", DisplayName = "Conflicting configurations during update - GraphQL operation specified but entity is disabled for GraphQL")]
[DataRow(new string[] { "Get" }, null, "false", "true", DisplayName = "Conflicting configurations during update - REST methods specified but entity is disabled for REST")]
public void TestUpdateStoredProcedureWithConflictingRestGraphQLOptions(
IEnumerable<string>? restMethods,
string? graphQLOperation,
string restRoute,
string graphQLType
)
{
UpdateOptions options = GenerateBaseUpdateOptions(
restRoute: restRoute,
graphQLType: graphQLType,
restMethodsForStoredProcedure: restMethods,
graphQLOperationForStoredProcedure: graphQLOperation);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SP_DEFAULT_REST_METHODS_GRAPHQL_OPERATION);
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Simple test to update an entity permission with new action containing WILDCARD and other crud operation.
/// Example "*,read,create"
/// Update including WILDCARD along with other crud operation is not allowed
/// </summary>
[TestMethod, Description("update action should fail because of invalid action combination.")]
public void TestUpdateEntityPermissionWithWildcardAndOtherCRUDAction()
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { "anonymous", "*,create,read" },
fieldsToInclude: new string[] { "id", "rating" },
fieldsToExclude: new string[] { "level" });
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""read"",
""update""
]
}
]
}
}
}";
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Simple test to verify failure on updating source of an entity with invalid fields.
/// </summary>
[DataTestMethod]
[DataRow(null, new string[] { "param1:value1" }, new string[] { "col1", "col2" }, "anonymous", "*", DisplayName = "Both KeyFields and Parameters provided for source")]
[DataRow("stored-procedure", null, new string[] { "col1", "col2" }, "anonymous", "create", DisplayName = "KeyFields incorrectly used with stored procedure")]
[DataRow("stored-procedure", new string[] { "param1:value1,param1:223" }, null, "anonymous", "read", DisplayName = "Parameters with duplicate keys for stored procedure")]
[DataRow("stored-procedure", new string[] { "param1:value1,param2:223" }, null, "anonymous", "create,read", DisplayName = "Stored procedure with more than 1 CRUD operation")]
[DataRow("stored-procedure", new string[] { "param1:value1,param2:223" }, null, "anonymous", "*", DisplayName = "Stored procedure with wildcard CRUD operation")]
[DataRow("view", new string[] { "param1:value1" }, null, "anonymous", "*", DisplayName = "Source Parameters incorrectly used with View")]
[DataRow("view", null, null, "anonymous", "*", DisplayName = "Mandatory KeyFields for views not provided.")]
[DataRow("view", new string[] { "param1:value1" }, new string[] { "col1", "col2" }, "anonymous", "*", DisplayName = "Source Parameters and keyfields incorrectly used with View.")]
[DataRow("table", new string[] { "param1:value1" }, null, "anonymous", "*", DisplayName = "Source Parameters incorrectly used with Table")]
[DataRow("table", new string[] { "param1:value1" }, new string[] { "col1", "col2" }, "anonymous", "*", DisplayName = "Source Parameters and keyfields incorrectly used with Table.")]
[DataRow("table-view", new string[] { "param1:value1" }, null, "anonymous", "*", DisplayName = "Invalid Source Type")]
public void TestUpdateSourceObjectWithInvalidFields(
string? sourceType,
IEnumerable<string>? parameters,
IEnumerable<string>? keyFields,
string role,
string operations)
{
UpdateOptions options = GenerateBaseUpdateOptions(
source: "MyTable",
permissions: new string[] { role, operations },
sourceType: sourceType,
sourceParameters: parameters,
sourceKeyFields: keyFields);
string initialConfig = AddPropertiesToJson(INITIAL_CONFIG, SINGLE_ENTITY_WITH_STORED_PROCEDURE);
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Test to check failure on invalid permission string
/// </summary>
[TestMethod]
public void TestParsingFromInvalidPermissionString()
{
string? role, actions;
IEnumerable<string> permissions = new string[] { "anonymous,create" }; //wrong format
bool isSuccess = TryGetRoleAndOperationFromPermission(permissions, out role, out actions);
Assert.IsFalse(isSuccess);
Assert.IsNull(role);
Assert.IsNull(actions);
}
/// <summary>
/// Test to check creation of a new relationship with Invalid Mapping fields
/// </summary>
[TestMethod]
public void TestCreateNewRelationshipWithInvalidRelationshipFields()
{
UpdateOptions options = GenerateBaseUpdateOptions(
cardinality: "many",
targetEntity: "FirstEntity",
linkingObject: "entity_link",
linkingSourceFields: new string[] { "eid1" },
linkingTargetFields: new string[] { "eid2", "fid2" },
relationshipFields: new string[] { "e1,e2,t2" } // Invalid value. Correct format uses ':' to separate source and target fields
);
EntityRelationship? relationship = CreateNewRelationshipWithUpdateOptions(options);
Assert.IsNull(relationship);
}
/// <summary>
/// Test to Update Entity with Invalid mappings
/// </summary>
[DataTestMethod]
[DataRow("id:identity:id,name:Company Name", DisplayName = "Invalid format for mappings value, required: 2, provided: 3.")]
[DataRow("id:identity:id,name:", DisplayName = "Invalid format for mappings value, required: 2, provided: 1.")]
public void TestUpdateEntityWithInvalidMappings(string mappings)
{
UpdateOptions options = GenerateBaseUpdateOptions(
map: mappings.Split(',')
);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [
""read"",
""update""
]
}
]
}
}
}";
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Test to validate that Permissions is mandatory when using options --fields.include or --fields.exclude
/// </summary>
[DataTestMethod]
[DataRow(new string[] { }, new string[] { "field" }, new string[] { }, DisplayName = "Invalid command with fieldsToInclude but no permissions")]
[DataRow(new string[] { }, new string[] { }, new string[] { "field1,field2" }, DisplayName = "Invalid command with fieldsToExclude but no permissions")]
public void TestUpdateEntityWithInvalidPermissionAndFields(
IEnumerable<string> Permissions,
IEnumerable<string> FieldsToInclude,
IEnumerable<string> FieldsToExclude)
{
UpdateOptions options = GenerateBaseUpdateOptions(
permissions: Permissions,
fieldsToInclude: FieldsToInclude,
fieldsToExclude: FieldsToExclude);
string initialConfig = GetInitialConfigString() + "," + @"
""entities"": {
""MyEntity"": {
""source"": ""MyTable"",
""permissions"": [
{
""role"": ""anonymous"",
""actions"": [""read"",""update""]
}
],
""mappings"": {
""id"": ""Identity"",
""name"": ""Company Name""
}
}
}
}";
RuntimeConfigLoader.TryParseConfig(initialConfig, out RuntimeConfig? runtimeConfig);
Assert.IsFalse(TryUpdateExistingEntity(options, runtimeConfig!, out RuntimeConfig _));
}
/// <summary>
/// Test to verify Invalid inputs to create a relationship
/// </summary>
[DataTestMethod]
[DataRow("CosmosDB_NoSQL", "one", "MyEntity", DisplayName = "CosmosDb does not support relationships")]
[DataRow("mssql", null, "MyEntity", DisplayName = "Cardinality should not be null")]
[DataRow("mssql", "manyx", "MyEntity", DisplayName = "Cardinality should be one/many")]
[DataRow("mssql", "one", null, DisplayName = "Target entity should not be null")]