@@ -603,6 +603,112 @@ class DeltaSharingSuite extends QueryTest with SharedSparkSession with DeltaShar
603603 .collect()
604604 assert(TestDeltaSharingClient .limits === Seq (1L ))
605605 }
606+
607+ integrationTest(" async query timeout" ) {
608+ // Test that async query times out when it exceeds the specified timeout
609+ withSQLConf(
610+ " spark.delta.sharing.network.useAsyncQuery" -> " true" ,
611+ " spark.delta.sharing.network.asyncQueryTimeout" -> " 1" , // 1ms timeout
612+ " spark.delta.sharing.network.asyncQueryPollIntervalMillis" -> " 100"
613+ ) {
614+ val tablePath = testProfileFile.getCanonicalPath + " #share_azure.default.table_wasb"
615+
616+ // Note: This test requires server-side support for async queries that take longer
617+ // than timeout In a real scenario, this would timeout if the server keeps returning
618+ // queryStatus
619+ val ex = intercept[IllegalStateException ] {
620+ spark.read.format(" deltaSharing" ).load(tablePath).collect()
621+ }
622+ assert(ex.getMessage.contains(" Query is timed out after" ) ||
623+ ex.getMessage.contains(" 1000 ms" ))
624+ }
625+ }
626+
627+ integrationTest(" async query initial query table errors" ) {
628+ // Test error conditions in the initial query table request (DeltaSharingClient.scala:L650)
629+ withSQLConf(" spark.delta.sharing.network.useAsyncQuery" -> " true" ) {
630+ val tablePath = testProfileFile.getCanonicalPath + " #share1.default.nonexistent_table"
631+
632+ // Test 1: Invalid table should throw UnexpectedHttpStatus
633+ val ex1 = intercept[io.delta.sharing.client.util.UnexpectedHttpStatus ] {
634+ spark.read.format(" deltaSharing" ).load(tablePath).collect()
635+ }
636+ assert(ex1.getMessage.contains(" 404" ) || ex1.getMessage.contains(" Not Found" ))
637+ }
638+ }
639+
640+ integrationTest(" async query polling errors" ) {
641+ // Test error conditions during polling (DeltaSharingClient.scala:L1099)
642+ withSQLConf(
643+ " spark.delta.sharing.network.useAsyncQuery" -> " true" ,
644+ " spark.delta.sharing.network.asyncQueryPollIntervalMillis" -> " 100"
645+ ) {
646+ // Test 1: Inconsistent queryId in polling response
647+ // Server will change the queryId mid-query for tables ending with "_change_query_id"
648+ val tablePath1 = testProfileFile.getCanonicalPath +
649+ " #share_azure.default.table_wasb_change_query_id"
650+ val ex1 = intercept[IllegalStateException ] {
651+ spark.read.format(" deltaSharing" ).load(tablePath1).collect()
652+ }
653+ assert(ex1.getMessage.contains(" QueryId is not consistent" ) ||
654+ ex1.getMessage.contains(" query" ))
655+
656+ // Test 2: Missing queryId when query is still pending
657+ // Server will return null queryId for tables ending with "_change_query_id_to_be_null"
658+ val tablePath2 = testProfileFile.getCanonicalPath +
659+ " #share_azure.default.table_wasb_change_query_id_to_be_null"
660+ val ex2 = intercept[IllegalStateException ] {
661+ spark.read.format(" deltaSharing" ).load(tablePath2).collect()
662+ }
663+ assert(ex2.getMessage.contains(" QueryId is not returned" ) ||
664+ ex2.getMessage.contains(" null" ))
665+ }
666+ }
667+
668+ integrationTest(" async query handles load table failures" ) {
669+ withSQLConf(
670+ " spark.delta.sharing.network.useAsyncQuery" -> " true" ,
671+ " spark.delta.sharing.network.asyncQueryPollIntervalMillis" -> " 100"
672+ ) {
673+ // Test: Server encounters error loading table after polling
674+ // Server will try to load a non-existent table on the 2nd poll for tables ending with
675+ // "_bad_table", which will cause loadTable to fail
676+ val tablePath3 = testProfileFile.getCanonicalPath +
677+ " #share_azure.default.table_wasb_bad_table"
678+ val ex3 = intercept[io.delta.sharing.client.util.UnexpectedHttpStatus ] {
679+ spark.read.format(" deltaSharing" ).load(tablePath3).collect()
680+ }
681+ assert(ex3.getMessage.contains(" 404" ) || ex3.getMessage.contains(" 400" ) ||
682+ ex3.getMessage.contains(" does not exist" ))
683+ }
684+ }
685+
686+ integrationTest(" async query error handling in initial response" ) {
687+ // Test various error conditions that can occur in getNDJsonWithAsync
688+ withSQLConf(" spark.delta.sharing.network.useAsyncQuery" -> " true" ) {
689+ // Test 1: Invalid version parameter
690+ val tablePath = testProfileFile.getCanonicalPath + " #share8.default.cdf_table_cdf_enabled"
691+ val ex1 = intercept[io.delta.sharing.client.util.UnexpectedHttpStatus ] {
692+ spark.read
693+ .format(" deltaSharing" )
694+ .option(" versionAsOf" , " 999999" ) // Non-existent version
695+ .load(tablePath)
696+ .collect()
697+ }
698+ assert(ex1.getMessage.contains(" 400" ) || ex1.getMessage.contains(" 404" ))
699+
700+ // Test 2: Invalid timestamp parameter
701+ val ex2 = intercept[io.delta.sharing.client.util.UnexpectedHttpStatus ] {
702+ spark.read
703+ .format(" deltaSharing" )
704+ .option(" timestampAsOf" , " 1970-01-01 00:00:00" )
705+ .load(tablePath)
706+ .collect()
707+ }
708+ assert(ex2.getMessage.contains(" 400" ) ||
709+ ex2.getMessage.contains(" The provided timestamp" ))
710+ }
711+ }
606712}
607713
608714class DeltaSharingWithParquetIOCacheEnabledSuite extends DeltaSharingSuite {
0 commit comments