@@ -784,6 +784,153 @@ public async Task ValidateSessionTokenMergedIntoDtcClient()
784784 }
785785 }
786786
787+ // Read Transaction Tests
788+
789+ [ TestMethod ]
790+ public async Task ValidateReadTransactionHappyPath ( )
791+ {
792+ // Arrange
793+ ToDoActivity doc1 = ToDoActivity . CreateRandomToDoActivity ( ) ;
794+ ToDoActivity doc2 = ToDoActivity . CreateRandomToDoActivity ( ) ;
795+
796+ DistributedTransactionMockHandler handler = new DistributedTransactionMockHandler (
797+ request => Task . FromResult ( this . BuildMockResponse (
798+ HttpStatusCode . OK ,
799+ BuildReadSuccessResponseJson ( 2 , JsonSerializer . Serialize ( doc1 ) , JsonSerializer . Serialize ( doc2 ) ) ) ) ) ;
800+
801+ using CosmosClient client = this . CreateMockClient ( handler ) ;
802+
803+ // Act
804+ DistributedTransactionResponse response = await client
805+ . CreateDistributedReadTransaction ( )
806+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( doc1 . pk ) , doc1 . id )
807+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( doc2 . pk ) , doc2 . id )
808+ . CommitTransactionAsync ( CancellationToken . None ) ;
809+
810+ // Assert
811+ Assert . IsNotNull ( handler . CapturedRequestBody ) ;
812+ Assert . AreEqual ( HttpStatusCode . OK , response . StatusCode ) ;
813+ Assert . IsTrue ( response . IsSuccessStatusCode ) ;
814+ Assert . AreEqual ( 2 , response . Count ) ;
815+
816+ response . Dispose ( ) ;
817+ }
818+
819+ [ TestMethod ]
820+ public async Task ValidateReadTransactionRequestStructure ( )
821+ {
822+ // Arrange
823+ ToDoActivity doc = ToDoActivity . CreateRandomToDoActivity ( ) ;
824+
825+ DistributedTransactionMockHandler handler = new DistributedTransactionMockHandler (
826+ request => Task . FromResult ( this . BuildMockResponse (
827+ HttpStatusCode . OK ,
828+ BuildReadSuccessResponseJson ( 1 , JsonSerializer . Serialize ( doc ) ) ) ) ) ;
829+
830+ using CosmosClient client = this . CreateMockClient ( handler ) ;
831+
832+ // Act
833+ DistributedTransactionResponse response = await client
834+ . CreateDistributedReadTransaction ( )
835+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( doc . pk ) , doc . id )
836+ . CommitTransactionAsync ( CancellationToken . None ) ;
837+
838+ // Assert – request structure
839+ Assert . IsNotNull ( handler . CapturedRequestBody ) ;
840+ using JsonDocument requestJson = JsonDocument . Parse ( handler . CapturedRequestBody ) ;
841+ JsonElement operation = requestJson . RootElement . GetProperty ( "operations" ) [ 0 ] ;
842+
843+ Assert . AreEqual ( OperationType . Read . ToString ( ) , operation . GetProperty ( "operationType" ) . GetString ( ) ) ;
844+ Assert . AreEqual ( doc . id , operation . GetProperty ( "id" ) . GetString ( ) ) ;
845+ Assert . IsTrue ( operation . TryGetProperty ( "databaseName" , out _ ) , "databaseName should be present" ) ;
846+ Assert . IsTrue ( operation . TryGetProperty ( "collectionName" , out _ ) , "collectionName should be present" ) ;
847+ Assert . IsTrue ( operation . TryGetProperty ( "partitionKey" , out _ ) , "partitionKey should be present" ) ;
848+ Assert . IsFalse ( operation . TryGetProperty ( "resourceBody" , out _ ) , "resourceBody must NOT be present for read operations" ) ;
849+
850+ response . Dispose ( ) ;
851+ }
852+
853+ [ TestMethod ]
854+ public async Task ValidateReadTransactionResponseDeserialization ( )
855+ {
856+ // Arrange
857+ ToDoActivity expectedDoc = ToDoActivity . CreateRandomToDoActivity ( ) ;
858+
859+ DistributedTransactionMockHandler handler = new DistributedTransactionMockHandler (
860+ request => Task . FromResult ( this . BuildMockResponse (
861+ HttpStatusCode . OK ,
862+ BuildReadSuccessResponseJson ( 1 , JsonSerializer . Serialize ( expectedDoc ) ) ) ) ) ;
863+
864+ using CosmosClient client = this . CreateMockClient ( handler ) ;
865+
866+ // Act
867+ DistributedTransactionResponse response = await client
868+ . CreateDistributedReadTransaction ( )
869+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( expectedDoc . pk ) , expectedDoc . id )
870+ . CommitTransactionAsync ( CancellationToken . None ) ;
871+
872+ // Assert
873+ Assert . IsTrue ( response . IsSuccessStatusCode ) ;
874+ ToDoActivity actualDoc = JsonSerializer . Deserialize < ToDoActivity > ( response [ 0 ] . ResourceStream ) ;
875+ Assert . IsNotNull ( actualDoc ) ;
876+ Assert . AreEqual ( expectedDoc . id , actualDoc . id ) ;
877+ Assert . AreEqual ( expectedDoc . pk , actualDoc . pk ) ;
878+ Assert . AreEqual ( expectedDoc . taskNum , actualDoc . taskNum ) ;
879+
880+ response . Dispose ( ) ;
881+ }
882+
883+ [ TestMethod ]
884+ public async Task ValidateReadTransactionResourceStream ( )
885+ {
886+ // Arrange
887+ ToDoActivity expectedDoc = ToDoActivity . CreateRandomToDoActivity ( ) ;
888+
889+ DistributedTransactionMockHandler handler = new DistributedTransactionMockHandler (
890+ request => Task . FromResult ( this . BuildMockResponse (
891+ HttpStatusCode . OK ,
892+ BuildReadSuccessResponseJson ( 1 , JsonSerializer . Serialize ( expectedDoc ) ) ) ) ) ;
893+
894+ using CosmosClient client = this . CreateMockClient ( handler ) ;
895+
896+ // Act
897+ DistributedTransactionResponse response = await client
898+ . CreateDistributedReadTransaction ( )
899+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( expectedDoc . pk ) , expectedDoc . id )
900+ . CommitTransactionAsync ( CancellationToken . None ) ;
901+
902+ // Assert – raw stream access
903+ Stream stream = response [ 0 ] . ResourceStream ;
904+ Assert . IsNotNull ( stream ) ;
905+ ToDoActivity actualDoc = JsonSerializer . Deserialize < ToDoActivity > ( stream ) ;
906+ Assert . AreEqual ( expectedDoc . id , actualDoc . id ) ;
907+ Assert . AreEqual ( expectedDoc . pk , actualDoc . pk ) ;
908+
909+ response . Dispose ( ) ;
910+ }
911+
912+ [ TestMethod ]
913+ public void ValidateReadTransactionMissingIdThrows ( )
914+ {
915+ using CosmosClient client = this . CreateMockClient ( new DistributedTransactionMockHandler (
916+ request => Task . FromResult ( this . BuildMockResponse ( HttpStatusCode . OK , BuildSuccessResponseJson ( 1 ) ) ) ) ) ;
917+
918+ Assert . ThrowsException < ArgumentNullException > ( ( ) =>
919+ client . CreateDistributedReadTransaction ( )
920+ . ReadItem ( this . database . Id , this . container . Id , new PartitionKey ( "pk" ) , id : null ) ) ;
921+ }
922+
923+ [ TestMethod ]
924+ public void ValidateReadTransactionMissingDatabaseThrows ( )
925+ {
926+ using CosmosClient client = this . CreateMockClient ( new DistributedTransactionMockHandler (
927+ request => Task . FromResult ( this . BuildMockResponse ( HttpStatusCode . OK , BuildSuccessResponseJson ( 1 ) ) ) ) ) ;
928+
929+ Assert . ThrowsException < ArgumentNullException > ( ( ) =>
930+ client . CreateDistributedReadTransaction ( )
931+ . ReadItem ( null , this . container . Id , new PartitionKey ( "pk" ) , "item-id" ) ) ;
932+ }
933+
787934 // Helpers
788935
789936 private void ValidateValueKind ( JsonElement operation , string property , JsonValueKind expectedValueKind , int operationIndex , bool isRequired )
@@ -827,6 +974,19 @@ private static string BuildSuccessResponseJson(int operationCount)
827974 return $@ "{{""operationResponses"":[{ string . Join ( "," , results ) } ]}}";
828975 }
829976
977+ private static string BuildReadSuccessResponseJson ( int operationCount , params string [ ] itemJsonBodies )
978+ {
979+ List < string > results = new List < string > ( ) ;
980+ for ( int i = 0 ; i < operationCount ; i ++ )
981+ {
982+ string body = i < itemJsonBodies . Length ? itemJsonBodies [ i ] : "{}" ;
983+ string base64Body = Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( body ) ) ;
984+ results . Add ( $@ "{{""index"":{ i } ,""statusCode"":200,""etag"":""\""etag-{ i } \"""",""resourcebody"":""{ base64Body } ""}}") ;
985+ }
986+
987+ return $@ "{{""operationResponses"":[{ string . Join ( "," , results ) } ]}}";
988+ }
989+
830990 // Mock handler
831991
832992 /// <summary>
0 commit comments