@@ -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