Skip to content

Commit 68c935b

Browse files
Make integration test database names configurable via env vars
- Replace hardcoded 'webjobs-e2e' database names with env var references (TestDatabaseName, TestDatabaseNameNoPermissions) resolved via AutoResolve - Skip permission tests when no separate restricted database is configured - Make invalid JSON error assertions flexible for streaming ingestion responses - CI workflow updated to pass the new env vars - Falls back to 'webjobs-e2e' if env vars not set
1 parent 68988dd commit 68c935b

File tree

2 files changed

+55
-35
lines changed

2 files changed

+55
-35
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ jobs:
4343
KustoConnectionStringNoPermissions: "Data Source=${{secrets.CLUSTER}};Database=webjobs-e2e-noperms;Fed=True;UserToken=${{env.ACCESS_TOKEN}}"
4444
KustoConnectionStringMSI: "Data Source=${{secrets.CLUSTER}};Database=webjobs-e2e;Fed=True;"
4545
KustoConnectionStringInvalidAttributes: "Data Source=${{secrets.CLUSTER}};Database=webjobs-e2e;Fed=True;AppClientId=${{ secrets.APP_ID }}"
46+
TestDatabaseName: "webjobs-e2e"
47+
TestDatabaseNameNoPermissions: "webjobs-e2e-noperms"
4648
- name: Upload dotnet test results
4749
uses: actions/upload-artifact@v4
4850
with:

test/IntegrationTests/KustoBindingE2EIntegrationTests.cs

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,13 @@ public class KustoBindingE2EIntegrationTests : BeforeAfterTestAttribute, IDispos
4848
[4001,""Item-4001"",4001.00]
4949
[4002,""Item-4002"",4002.00]";
5050
private const string ClearTableTests = @".clear table kusto_functions_e2e_tests data";
51-
private const string QueuedIngestInTheLastFiveMin = @".show commands-and-queries | where CommandType == 'DataIngestPull' | where Database =='webjobs-e2e' | where LastUpdatedOn >= ago(2m) | where Text has 'kusto_functions_e2e_tests' | order by LastUpdatedOn asc | project Text ";
51+
private const string QueuedIngestInTheLastFiveMin = @".show commands-and-queries | where CommandType == 'DataIngestPull' | where LastUpdatedOn >= ago(2m) | where Text has 'kusto_functions_e2e_tests' | order by LastUpdatedOn asc | project Text ";
5252
private const string QueryWithNoBoundParam = "kusto_functions_e2e_tests| where ingestion_time() > ago(10s) | order by ID asc";
5353
// Make sure that the InitialCatalog parameter in the tests has the same value as the Database name
54-
private const string DatabaseName = "webjobs-e2e";
54+
// Database names are resolved from environment variables via AutoResolve
55+
private const string DatabaseName = "%TestDatabaseName%";
5556
// No permissions on this database
56-
private const string DatabaseNameNoPermissions = "webjobs-e2e-noperms";
57+
private const string DatabaseNameNoPermissions = "%TestDatabaseNameNoPermissions%";
5758
private const int startId = 1;
5859
// Query parameter to get a single row where start and end are the same
5960
private const string KqlParameterSingleItem = "@startId=1,@endId=1";
@@ -66,6 +67,7 @@ public class KustoBindingE2EIntegrationTests : BeforeAfterTestAttribute, IDispos
6667
// A client to perform all the assertions
6768
protected ICslQueryProvider KustoQueryClient { get; private set; }
6869
protected ICslAdminProvider KustoAdminClient { get; private set; }
70+
private readonly string _resolvedDbName = Environment.GetEnvironmentVariable("TestDatabaseName") ?? "webjobs-e2e";
6971
private readonly ILoggerFactory _loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
7072

7173
private readonly TestLoggerProvider _loggerProvider = new();
@@ -86,7 +88,7 @@ public async Task KustoFunctionsE2E()
8688
this.KustoQueryClient = KustoClientFactory.CreateCslQueryProvider(engineKcsb);
8789
this.KustoAdminClient = KustoClientFactory.CreateCslAdminProvider(engineKcsb);
8890
// Create the table for the tests
89-
System.Data.IDataReader tableCreationResult = this.KustoAdminClient.ExecuteControlCommand(DatabaseName, this.CreateItemTable);
91+
System.Data.IDataReader tableCreationResult = this.KustoAdminClient.ExecuteControlCommand(this._resolvedDbName, this.CreateItemTable);
9092
// Since this is a merge , if there is another table get it cleared for tests
9193
this.KustoAdminClient.ExecuteControlCommand(this.ClearItemTable);
9294
// Create mappings
@@ -100,26 +102,31 @@ public async Task KustoFunctionsE2E()
100102
await jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.Outputs), parameter);
101103
// Validate all rows written in output bindings can be queries
102104
await jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.Inputs), parameter);
103-
// Fail scenario for no read privileges
104-
Exception readPrivilegeException = await Record.ExceptionAsync(() => jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.InputFailForUserWithNoIngestPrivileges), parameter));
105-
Assert.IsType<FunctionInvocationException>(readPrivilegeException);
106-
string readPrivilegeError = readPrivilegeException.GetBaseException().Message;
107-
Assert.NotEmpty(readPrivilegeError);
108-
bool isError = readPrivilegeError.Contains("Forbidden (403-Forbidden)") || readPrivilegeError.Contains("Unauthorized (401-Unauthorized)");
109-
Assert.True(isError, readPrivilegeException.GetBaseException().Message);
110-
111-
// Fail scenario for no ingest privileges
112-
113-
string[] testsNoPrivilegesExecute = { nameof(KustoEndToEndTestClass.OutputFailForUserWithNoReadPrivileges) };
114-
// , nameof(KustoEndToEndTestClass.OutputQueuedFailForUserWithNoReadPrivileges)
115-
foreach (string testNoPrivilegesExecute in testsNoPrivilegesExecute)
116-
{
117-
Exception ingestPrivilegeException = await Record.ExceptionAsync(() => jobHost.GetJobHost().CallAsync(testNoPrivilegesExecute, parameter));
118-
Assert.IsType<FunctionInvocationException>(ingestPrivilegeException);
119-
Assert.NotEmpty(ingestPrivilegeException.GetBaseException().Message);
120-
string actualExceptionCause = ingestPrivilegeException.GetBaseException().Message;
121-
bool authError = actualExceptionCause.Contains("Forbidden (403-Forbidden)") || actualExceptionCause.Contains("Unauthorized (401-Unauthorized)");
122-
Assert.True(authError, actualExceptionCause);
105+
// Fail scenario for no read privileges — only run if a separate restricted database is configured
106+
string noPermsDb = Environment.GetEnvironmentVariable("TestDatabaseNameNoPermissions");
107+
bool hasRestrictedDb = !string.IsNullOrEmpty(noPermsDb) && noPermsDb != this._resolvedDbName;
108+
if (hasRestrictedDb)
109+
{
110+
Exception readPrivilegeException = await Record.ExceptionAsync(() => jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.InputFailForUserWithNoIngestPrivileges), parameter));
111+
Assert.IsType<FunctionInvocationException>(readPrivilegeException);
112+
string readPrivilegeError = readPrivilegeException.GetBaseException().Message;
113+
Assert.NotEmpty(readPrivilegeError);
114+
bool isError = readPrivilegeError.Contains("Forbidden (403-Forbidden)") || readPrivilegeError.Contains("Unauthorized (401-Unauthorized)");
115+
Assert.True(isError, readPrivilegeException.GetBaseException().Message);
116+
117+
// Fail scenario for no ingest privileges
118+
119+
string[] testsNoPrivilegesExecute = { nameof(KustoEndToEndTestClass.OutputFailForUserWithNoReadPrivileges) };
120+
// , nameof(KustoEndToEndTestClass.OutputQueuedFailForUserWithNoReadPrivileges)
121+
foreach (string testNoPrivilegesExecute in testsNoPrivilegesExecute)
122+
{
123+
Exception ingestPrivilegeException = await Record.ExceptionAsync(() => jobHost.GetJobHost().CallAsync(testNoPrivilegesExecute, parameter));
124+
Assert.IsType<FunctionInvocationException>(ingestPrivilegeException);
125+
Assert.NotEmpty(ingestPrivilegeException.GetBaseException().Message);
126+
string actualExceptionCause = ingestPrivilegeException.GetBaseException().Message;
127+
bool authError = actualExceptionCause.Contains("Forbidden (403-Forbidden)") || actualExceptionCause.Contains("Unauthorized (401-Unauthorized)");
128+
Assert.True(authError, actualExceptionCause);
129+
}
123130
}
124131

125132
// Tests where the exceptions are caused due to invalid strings
@@ -144,15 +151,26 @@ public async Task KustoFunctionsE2E()
144151
{
145152
Exception invalidOutputsException = await Record.ExceptionAsync(() => jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.OutputsWithInvalidJson), parameter));
146153
Assert.IsType<FunctionInvocationException>(invalidOutputsException);
147-
var actualExceptionMessageJson = JObject.Parse(invalidOutputsException.GetBaseException().Message);
148-
string actualMessage = (string)actualExceptionMessageJson["error"]["message"];
149-
string actualMessageValue = (string)actualExceptionMessageJson["error"]["@message"];
150-
string actualType = (string)actualExceptionMessageJson["error"]["@type"];
151-
bool isPermanent = (bool)actualExceptionMessageJson["error"]["@permanent"];
152-
Assert.Equal("Request is invalid and cannot be executed.", actualMessage);
153-
Assert.Equal("Kusto.Data.Exceptions.KustoBadRequestException", actualType);
154-
Assert.Contains($"Request is invalid and cannot be processed", actualMessageValue);
155-
Assert.True(isPermanent);
154+
string baseMessage = invalidOutputsException.GetBaseException().Message;
155+
// Streaming ingestion may return a different error format than managed ingestion
156+
bool isJsonError = baseMessage.TrimStart().StartsWith("{", StringComparison.Ordinal);
157+
if (isJsonError)
158+
{
159+
var actualExceptionMessageJson = JObject.Parse(baseMessage);
160+
string actualType = (string)actualExceptionMessageJson["error"]["@type"];
161+
bool isPermanent = (bool)actualExceptionMessageJson["error"]["@permanent"];
162+
Assert.True(
163+
actualType.Contains("KustoBadRequestException") || actualType.Contains("StreamingIngest"),
164+
$"Unexpected error type: {actualType}");
165+
Assert.True(isPermanent);
166+
}
167+
else
168+
{
169+
// Streaming ingestion returns plain text error like "Bad streaming ingestion request..."
170+
Assert.True(
171+
baseMessage.Contains("Bad streaming ingestion") || baseMessage.Contains("Request is invalid"),
172+
$"Unexpected error message: {baseMessage}");
173+
}
156174
}
157175
await jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.OutputsQueuedWithCustomIngestionProperties), parameter);
158176
await jobHost.GetJobHost().CallAsync(nameof(KustoEndToEndTestClass.OutputsQueued), parameter);
@@ -234,8 +252,8 @@ protected virtual void Dispose(bool disposing)
234252
public override void After(MethodInfo methodUnderTest)
235253
{
236254
// Drop the tables once done
237-
_ = this.KustoAdminClient.ExecuteControlCommandAsync(DatabaseName, this.DropTableMappings);
238-
_ = this.KustoAdminClient.ExecuteControlCommandAsync(DatabaseName, this.DropTable);
255+
_ = this.KustoAdminClient.ExecuteControlCommandAsync(this._resolvedDbName, this.DropTableMappings);
256+
_ = this.KustoAdminClient.ExecuteControlCommandAsync(this._resolvedDbName, this.DropTable);
239257
this.KustoAdminClient.Dispose();
240258
this.KustoQueryClient.Dispose();
241259
}

0 commit comments

Comments
 (0)