-
Notifications
You must be signed in to change notification settings - Fork 332
Expand file tree
/
Copy pathUtilsTests.cs
More file actions
319 lines (285 loc) · 18 KB
/
UtilsTests.cs
File metadata and controls
319 lines (285 loc) · 18 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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Cli.Tests;
[TestClass]
public class UtilsTests
{
[TestInitialize]
public void TestInitialize()
{
ILoggerFactory loggerFactory = TestLoggerSupport.ProvisionLoggerFactory();
SetLoggerForCliConfigGenerator(loggerFactory.CreateLogger<ConfigGenerator>());
SetCliUtilsLogger(loggerFactory.CreateLogger<Utils>());
}
[TestMethod]
public void ConstructRestOptionsForCosmosDbNoSQLIgnoresOtherParamsAndDisables()
{
EntityRestOptions options = ConstructRestOptions(restRoute: "true", supportedHttpVerbs: null, isCosmosDbNoSql: true);
Assert.IsFalse(options.Enabled);
}
[TestMethod]
public void ConstructRestOptionsWithNullEnablesRest()
{
EntityRestOptions options = ConstructRestOptions(restRoute: null, supportedHttpVerbs: null, isCosmosDbNoSql: false);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructRestOptionsWithTrueEnablesRest()
{
EntityRestOptions options = ConstructRestOptions(restRoute: "true", supportedHttpVerbs: null, isCosmosDbNoSql: false);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructRestOptionsWithFalseDisablesRest()
{
EntityRestOptions options = ConstructRestOptions(restRoute: "false", supportedHttpVerbs: null, isCosmosDbNoSql: false);
Assert.IsFalse(options.Enabled);
}
[TestMethod]
public void ConstructRestOptionsWithCustomPathSetsPath()
{
EntityRestOptions options = ConstructRestOptions(restRoute: "customPath", supportedHttpVerbs: null, isCosmosDbNoSql: false);
Assert.AreEqual("/customPath", options.Path);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructRestOptionsWithCustomPathAndMethodsSetsPathAndMethods()
{
EntityRestOptions options = ConstructRestOptions("customPath", new[] { SupportedHttpVerb.Get, SupportedHttpVerb.Post }, false);
Assert.AreEqual("/customPath", options.Path);
Assert.IsTrue(options.Enabled);
Assert.IsNotNull(options.Methods);
Assert.AreEqual(2, options.Methods.Length);
Assert.IsTrue(options.Methods.Contains(SupportedHttpVerb.Get));
Assert.IsTrue(options.Methods.Contains(SupportedHttpVerb.Post));
}
[TestMethod]
public void ConstructGraphQLOptionsWithNullWillEnable()
{
EntityGraphQLOptions options = ConstructGraphQLTypeDetails(null, null);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructGraphQLOptionsWithTrueWillEnable()
{
EntityGraphQLOptions options = ConstructGraphQLTypeDetails("true", null);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructGraphQLOptionsWithFalseWillDisable()
{
EntityGraphQLOptions options = ConstructGraphQLTypeDetails("false", null);
Assert.IsFalse(options.Enabled);
}
[TestMethod]
public void ConstructGraphQLOptionsWithSingularWillSetSingularAndDefaultPlural()
{
EntityGraphQLOptions options = ConstructGraphQLTypeDetails("singular", null);
Assert.AreEqual("singular", options.Singular);
Assert.AreEqual("", options.Plural);
Assert.IsTrue(options.Enabled);
}
[TestMethod]
public void ConstructGraphQLOptionsWithSingularAndPluralWillSetSingularAndPlural()
{
EntityGraphQLOptions options = ConstructGraphQLTypeDetails("singular:plural", null);
Assert.AreEqual("singular", options.Singular);
Assert.AreEqual("plural", options.Plural);
Assert.IsTrue(options.Enabled);
}
/// <summary>
/// Test to check the precedence logic for config file in CLI
/// </summary>
[DataTestMethod]
[DataRow("", "my-config.json", "my-config.json", DisplayName = "user provided the config file and environment variable was not set.")]
[DataRow("Test", "my-config.json", "my-config.json", DisplayName = "user provided the config file and environment variable was set.")]
[DataRow("Test", null, $"{CONFIGFILE_NAME}.Test{CONFIG_EXTENSION}", DisplayName = "config not provided, but environment variable was set.")]
[DataRow("", null, $"{CONFIGFILE_NAME}{CONFIG_EXTENSION}", DisplayName = "neither config was provided, nor environment variable was set.")]
public void TestConfigSelectionBasedOnCliPrecedence(
string? environmentValue,
string? userProvidedConfigFile,
string expectedRuntimeConfigFile)
{
MockFileSystem fileSystem = new();
fileSystem.AddFile(expectedRuntimeConfigFile, new MockFileData(""));
FileSystemRuntimeConfigLoader loader = new(fileSystem);
string? envValueBeforeTest = Environment.GetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME);
Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, environmentValue);
Assert.IsTrue(TryGetConfigFileBasedOnCliPrecedence(loader, userProvidedConfigFile, out string? actualRuntimeConfigFile));
Assert.AreEqual(expectedRuntimeConfigFile, actualRuntimeConfigFile);
Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, envValueBeforeTest);
}
/// <summary>
/// Test to verify negative/positive string numerals are correctly parsed as integers
/// Decimal values are parsed as double.
/// Boolean string is correctly parsed as boolean
/// everything else is parsed as string.
/// </summary>
[TestMethod]
public void TestTryParseSourceParameterDictionary()
{
IEnumerable<string>? parametersList = new string[] { "param1:123", "param2:-243", "param3:220.12", "param4:True", "param5:dab" };
Assert.IsTrue(TryParseSourceParameterDictionary(parametersList, out List<Azure.DataApiBuilder.Config.ObjectModel.ParameterMetadata>? sourceParameters));
Assert.IsNotNull(sourceParameters);
Assert.AreEqual(123, Convert.ToInt32(sourceParameters.First(p => p.Name == "param1").Default));
Assert.AreEqual(-243, Convert.ToInt32(sourceParameters.First(p => p.Name == "param2").Default));
Assert.AreEqual(220.12, Convert.ToDouble(sourceParameters.First(p => p.Name == "param3").Default));
Assert.AreEqual(true, Convert.ToBoolean(sourceParameters.First(p => p.Name == "param4").Default));
Assert.AreEqual("dab", Convert.ToString(sourceParameters.First(p => p.Name == "param5").Default));
}
/// <summary>
/// Validates permissions operations are valid for the provided source type.
/// </summary>
/// <param name="operations">CRUD + Execute + *</param>
/// <param name="entitySourceType">Table, StoredProcedure, View</param>
/// <param name="isSuccess">True/False</param>
[DataTestMethod]
[DataRow(new string[] { "*" }, EntitySourceType.StoredProcedure, true, DisplayName = "PASS: Stored-Procedure with wildcard CRUD operation.")]
[DataRow(new string[] { "execute" }, EntitySourceType.StoredProcedure, true, DisplayName = "PASS: Stored-Procedure with execute operation only.")]
[DataRow(new string[] { "create", "read" }, EntitySourceType.StoredProcedure, false, DisplayName = "FAIL: Stored-Procedure with more than 1 CRUD operation.")]
[DataRow(new string[] { "*" }, EntitySourceType.Table, true, DisplayName = "PASS: Table with wildcard CRUD operation.")]
[DataRow(new string[] { "create" }, EntitySourceType.Table, true, DisplayName = "PASS: Table with 1 CRUD operation.")]
[DataRow(new string[] { "create", "read" }, EntitySourceType.Table, true, DisplayName = "PASS: Table with more than 1 CRUD operation.")]
[DataRow(new string[] { "*" }, EntitySourceType.View, true, DisplayName = "PASS: View with wildcard CRUD operation.")]
[DataRow(new string[] { "create" }, EntitySourceType.View, true, DisplayName = "PASS: View with 1 CRUD operation.")]
[DataRow(new string[] { "create", "read" }, EntitySourceType.View, true, DisplayName = "PASS: View with more than 1 CRUD operation.")]
public void TestStoredProcedurePermissions(
string[] operations,
EntitySourceType entitySourceType,
bool isSuccess)
{
Assert.AreEqual(isSuccess, VerifyOperations(operations, entitySourceType));
}
/// <summary>
/// Test to verify that CLI is able to figure out if the api path prefix for rest/graphql contains invalid characters.
/// </summary>
[DataTestMethod]
[DataRow("/", true, DisplayName = "Only forward slash as api path")]
[DataRow("/$%^", false, DisplayName = "Api path containing only reserved characters.")]
[DataRow("/rest-api", true, DisplayName = "Valid api path")]
[DataRow("/graphql@api", false, DisplayName = "Api path containing some reserved characters.")]
[DataRow("/api path", true, DisplayName = "Api path containing space.")]
public void TestApiPathIsWellFormed(string apiPath, bool expectSuccess)
{
Assert.AreEqual(expectSuccess, IsURIComponentValid(apiPath));
}
/// <summary>
/// Test to verify that both Audience and Issuer is mandatory when Authentication Provider is
/// neither EasyAuthType or Simulator. If Authentication Provider is either EasyAuth or Simulator
/// audience and issuer are ignored.
/// </summary>
[DataTestMethod]
[DataRow("StaticWebApps", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: Audience and Issuer ignored with StaticWebApps.")]
[DataRow("StaticWebApps", null, "issuer-xxx", true, DisplayName = "PASS: Issuer ignored with StaticWebApps.")]
[DataRow("StaticWebApps", "aud-xxx", null, true, DisplayName = "PASS: Audience ignored with StaticWebApps.")]
[DataRow("StaticWebApps", null, null, true, DisplayName = "PASS: StaticWebApps correctly configured with neither audience nor issuer.")]
[DataRow("AppService", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: Audience and Issuer ignored with AppService.")]
[DataRow("AppService", null, "issuer-xxx", true, DisplayName = "PASS: Issuer ignored with AppService.")]
[DataRow("AppService", "aud-xxx", null, true, DisplayName = "PASS: Audience ignored with AppService.")]
[DataRow("AppService", null, null, true, DisplayName = "PASS: AppService correctly configured with neither audience nor issuer.")]
[DataRow("Simulator", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: Audience and Issuer ignored with Simulator.")]
[DataRow("Simulator", null, "issuer-xxx", true, DisplayName = "PASS: Issuer ignored with Simulator.")]
[DataRow("Simulator", "aud-xxx", null, true, DisplayName = "PASS: Audience ignored with Simulator.")]
[DataRow("Simulator", null, null, true, DisplayName = "PASS: Simulator correctly configured with neither audience nor issuer.")]
[DataRow("Unauthenticated", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: Audience and Issuer ignored with Unauthenticated.")]
[DataRow("Unauthenticated", null, "issuer-xxx", true, DisplayName = "PASS: Issuer ignored with Unauthenticated.")]
[DataRow("Unauthenticated", "aud-xxx", null, true, DisplayName = "PASS: Audience ignored with Unauthenticated.")]
[DataRow("Unauthenticated", null, null, true, DisplayName = "PASS: Unauthenticated correctly configured with neither audience nor issuer.")]
[DataRow("AzureAD", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: AzureAD correctly configured with both audience and issuer.")]
[DataRow("AzureAD", null, "issuer-xxx", false, DisplayName = "FAIL: AzureAD incorrectly configured with no audience specified.")]
[DataRow("AzureAD", "aud-xxx", null, false, DisplayName = "FAIL: AzureAD incorrectly configured with no issuer specified.")]
[DataRow("AzureAD", null, null, false, DisplayName = "FAIL: AzureAD incorrectly configured with no audience or issuer specified.")]
[DataRow("EntraID", "aud-xxx", "issuer-xxx", true, DisplayName = "PASS: EntraID correctly configured with both audience and issuer.")]
[DataRow("EntraID", null, "issuer-xxx", false, DisplayName = "FAIL: EntraID incorrectly configured with no audience specified.")]
[DataRow("EntraID", "aud-xxx", null, false, DisplayName = "FAIL: EntraID incorrectly configured with no issuer specified.")]
[DataRow("EntraID", null, null, false, DisplayName = "FAIL: EntraID incorrectly configured with no audience or issuer specified.")]
public void TestValidateAudienceAndIssuerForAuthenticationProvider(
string authenticationProvider,
string? audience,
string? issuer,
bool expectSuccess)
{
Assert.AreEqual(
expectSuccess,
ValidateAudienceAndIssuerForJwtProvider(authenticationProvider, audience, issuer)
);
}
/// <summary>
/// Test to verify that when DAB_ENVIRONMENT variable is set, also base config and
/// dab-config.{DAB_ENVIRONMENT}.json file is present, then when DAB engine is started, it will merge
/// the two config and use the merged config to startup the engine.
/// Here, baseConfig(dab-config.json) has no connection_string, while dab-config.Test.json has a defined connection string.
/// once the `dab start` is executed the merge happens and the merged file contains the connection string from the
/// Test config.
/// Scenarios Covered:
/// 1. Merging of Array: Complete override of Book permissions from the second config (environment based config).
/// 2. Merging Property when present in both config: Connection string in the second config overrides that of the first.
/// 3. Non-merging when a property in the environmentConfig file is null: {data-source.options} is null in the environment config,
/// So it is added to the merged config as it is with no change.
/// 4. Merging when a property is only present in the environmentConfig file: Publisher entity is present only in environment config,
/// So it is directly added to the merged config.
/// 5. Properties of same name but different level do not conflict: source is both a entityName and a property inside book entity, both are
/// treated differently.
/// </summary>
[TestMethod]
public void TestMergeConfig()
{
MockFileSystem fileSystem = new();
fileSystem.AddFile(DEFAULT_CONFIG_FILE_NAME, new MockFileData(BASE_CONFIG));
fileSystem.AddFile("dab-config.Test.json", new MockFileData(ENV_BASED_CONFIG));
FileSystemRuntimeConfigLoader loader = new(fileSystem);
Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, "Test");
Assert.IsTrue(ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), out string? mergedConfig), "Failed to merge config files");
Assert.AreEqual(mergedConfig, "dab-config.Test.merged.json");
Assert.IsTrue(fileSystem.File.Exists(mergedConfig));
Assert.IsTrue(JToken.DeepEquals(JObject.Parse(MERGED_CONFIG), JObject.Parse(fileSystem.File.ReadAllText(mergedConfig))));
}
/// <summary>
/// Test to verify that merged config file is only used for the below scenario
/// 1. Environment value is set.
/// 2. Both Base and envBased config file is present.
/// In all other cases, the TryMergeConfigsIfAvailable method should return false
/// and out param for the mergedConfigFile should be null.
/// </summary>
[DataTestMethod]
[DataRow("", false, false, null, false, DisplayName = "If environment value is not set, merged config file is not generated.")]
[DataRow("", false, true, null, false, DisplayName = "If environment value is not set, merged config file is not generated.")]
[DataRow("", true, false, null, false, DisplayName = "If environment value is not set, merged config file is not generated.")]
[DataRow("", true, true, null, false, DisplayName = "If environment value is not set, merged config file is not generated.")]
[DataRow(null, false, false, null, false, DisplayName = "If environment variable is removed, merged config file is not generated.")]
[DataRow(null, false, true, null, false, DisplayName = "If environment variable is removed, merged config file is not generated.")]
[DataRow(null, true, false, null, false, DisplayName = "If environment variable is removed, merged config file is not generated.")]
[DataRow(null, true, true, null, false, DisplayName = "If environment variable is removed, merged config file is not generated.")]
[DataRow("Test", false, false, null, false, DisplayName = "Environment value set but base config not available, merged config file is not generated.")]
[DataRow("Test", false, true, null, false, DisplayName = "Environment value set but base config not available, merged config file is not generated.")]
[DataRow("Test", true, false, null, false, DisplayName = "Environment value set but env based config not available, merged config file is not generated.")]
[DataRow("Test", true, true, "dab-config.Test.merged.json", true, DisplayName = "Environment value set and both base and envConfig available, merged config file is generated.")]
public void TestMergeConfigAvailability(
string? environmentValue,
bool isBaseConfigPresent,
bool isEnvironmentBasedConfigPresent,
string? expectedMergedConfigFileName,
bool expectedIsMergedConfigAvailable)
{
MockFileSystem fileSystem = new();
// Setting up the test scenarios
Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, environmentValue);
string baseConfig = "dab-config.json";
string envBasedConfig = "dab-config.Test.json";
if (isBaseConfigPresent)
{
fileSystem.AddFile(baseConfig, new("{}"));
}
if (isEnvironmentBasedConfigPresent)
{
fileSystem.AddFile(envBasedConfig, new("{}"));
}
FileSystemRuntimeConfigLoader loader = new(fileSystem);
Assert.AreEqual(
expectedIsMergedConfigAvailable,
ConfigMerger.TryMergeConfigsIfAvailable(fileSystem, loader, new StringLogger(), out string? mergedConfigFile),
"Availability of merge config should match");
Assert.AreEqual(expectedMergedConfigFileName, mergedConfigFile, "Merge config file name should match expected");
Environment.SetEnvironmentVariable(RUNTIME_ENVIRONMENT_VAR_NAME, null);
}
}