@@ -57,6 +57,14 @@ const spannerProtoDescriptor = protoDescriptor['google']['spanner']['v1'];
5757const RETRY_INFO_BIN = 'google.rpc.retryinfo-bin' ;
5858const RETRY_INFO_TYPE = 'type.googleapis.com/google.rpc.retryinfo' ;
5959
60+ /**
61+ * The type of result for an ReadRequest statement that the mock server should return.
62+ */
63+ enum ReadRequestResultType {
64+ ERROR ,
65+ RESULT_SET ,
66+ }
67+
6068/**
6169 * The type of result for an SQL statement that the mock server should return.
6270 */
@@ -66,6 +74,65 @@ enum StatementResultType {
6674 UPDATE_COUNT ,
6775}
6876
77+ /**
78+ * ReadRequestResult contains the result for an ReadRequest statement on the mock server.
79+ */
80+ export class ReadRequestResult {
81+ private readonly _type : ReadRequestResultType ;
82+ get type ( ) : ReadRequestResultType {
83+ return this . _type ;
84+ }
85+ private readonly _error : Error | null ;
86+ get error ( ) : Error {
87+ if ( this . _error ) {
88+ return this . _error ;
89+ }
90+ throw new Error ( 'The ReadRequestResult does not contain an Error' ) ;
91+ }
92+ private readonly _resultSet :
93+ | protobuf . ResultSet
94+ | protobuf . PartialResultSet [ ]
95+ | null ;
96+ get resultSet ( ) : protobuf . ResultSet | protobuf . PartialResultSet [ ] {
97+ if ( this . _resultSet ) {
98+ return this . _resultSet ;
99+ }
100+ throw new Error ( 'The ReadRequestResult does not contain a ResultSet' ) ;
101+ }
102+
103+ private constructor (
104+ type : ReadRequestResultType ,
105+ error : Error | null ,
106+ resultSet : protobuf . ResultSet | protobuf . PartialResultSet [ ] | null ,
107+ ) {
108+ this . _type = type ;
109+ this . _error = error ;
110+ this . _resultSet = resultSet ;
111+ }
112+
113+ /**
114+ * Create a ReadRequestResult that will return an error.
115+ * @param error The error to return for the statement.
116+ */
117+ static error ( error : Error ) : ReadRequestResult {
118+ return new ReadRequestResult ( ReadRequestResultType . ERROR , error , null ) ;
119+ }
120+
121+ /**
122+ * Create a ReadRequestResult that will return a ResultSet or a stream of PartialResultSets.
123+ * @param resultSet The result set to return.
124+ */
125+ static resultSet (
126+ resultSet : protobuf . ResultSet | protobuf . PartialResultSet [ ] ,
127+ ) : ReadRequestResult {
128+ return new ReadRequestResult (
129+ ReadRequestResultType . RESULT_SET ,
130+ null ,
131+ resultSet ,
132+ ) ;
133+ }
134+ }
135+
69136/**
70137 * StatementResult contains the result for an SQL statement on the mock server.
71138 */
@@ -246,13 +313,18 @@ export class MockSpanner {
246313 string ,
247314 StatementResult
248315 > ( ) ;
316+ private readRequestResults : Map < string , ReadRequestResult > = new Map <
317+ string ,
318+ ReadRequestResult
319+ > ( ) ;
249320 private executionTimes : Map < string , SimulatedExecutionTime > = new Map <
250321 string ,
251322 SimulatedExecutionTime
252323 > ( ) ;
253324
254325 private constructor ( ) {
255326 this . putStatementResult = this . putStatementResult . bind ( this ) ;
327+ this . putReadRequestResult = this . putReadRequestResult . bind ( this ) ;
256328 this . batchCreateSessions = this . batchCreateSessions . bind ( this ) ;
257329 this . createSession = this . createSession . bind ( this ) ;
258330 this . deleteSession = this . deleteSession . bind ( this ) ;
@@ -298,6 +370,11 @@ export class MockSpanner {
298370 return this . metadata ;
299371 }
300372
373+ putReadRequestResult ( query : ReadRequest , result : ReadRequestResult ) {
374+ const key = this . stableKeyFromReadRequest ( query ) ;
375+ this . readRequestResults . set ( key , result ) ;
376+ }
377+
301378 /**
302379 * Registers a result for an SQL statement on the server.
303380 * @param sql The SQL statement that should return the result.
@@ -862,6 +939,14 @@ export class MockSpanner {
862939 callback ( createUnimplementedError ( 'Read is not yet implemented' ) ) ;
863940 }
864941
942+ stableKeyFromReadRequest ( req : ReadRequest ) : string {
943+ const keySet = JSON . stringify (
944+ req . keySet ?? { } ,
945+ Object . keys ( req . keySet ?? { } ) . sort ( ) ,
946+ ) ;
947+ return `${ req . table } |${ keySet } ` ;
948+ }
949+
865950 streamingRead ( call : grpc . ServerWritableStream < protobuf . ReadRequest , { } > ) {
866951 this . pushRequest ( call . request ! , call . metadata ) ;
867952
@@ -881,8 +966,8 @@ export class MockSpanner {
881966 return ;
882967 }
883968 }
884- const key = ` ${ call . request ! . table } | ${ JSON . stringify ( call . request ! . keySet ) } ` ;
885- const res = this . statementResults . get ( key ) ;
969+ const key = this . stableKeyFromReadRequest ( call . request ! as ReadRequest ) ;
970+ const res = this . readRequestResults . get ( key ) ;
886971 if ( res ) {
887972 if ( call . request ! . transaction ?. begin ) {
888973 const txn = this . _updateTransaction (
@@ -895,15 +980,14 @@ export class MockSpanner {
895980 call . end ( ) ;
896981 return ;
897982 }
898- if ( res . type === StatementResultType . RESULT_SET ) {
983+ if ( res . type === ReadRequestResultType . RESULT_SET ) {
899984 ( res . resultSet as protobuf . ResultSet ) . metadata ! . transaction = txn ;
900985 }
901986 }
902987 let partialResultSets ;
903988 let resumeIndex ;
904- let streamErr ;
905989 switch ( res . type ) {
906- case StatementResultType . RESULT_SET :
990+ case ReadRequestResultType . RESULT_SET :
907991 if ( Array . isArray ( res . resultSet ) ) {
908992 partialResultSets = res . resultSet ;
909993 } else {
@@ -924,7 +1008,7 @@ export class MockSpanner {
9241008 index ++
9251009 ) {
9261010 const streamErr = this . shiftStreamError (
927- this . executeStreamingSql . name ,
1011+ this . streamingRead . name ,
9281012 index ,
9291013 ) ;
9301014 if ( streamErr ) {
@@ -935,14 +1019,14 @@ export class MockSpanner {
9351019 call . write ( partialResultSets [ index ] ) ;
9361020 }
9371021 break ;
938- case StatementResultType . ERROR :
1022+ case ReadRequestResultType . ERROR :
9391023 call . sendMetadata ( new Metadata ( ) ) ;
9401024 call . emit ( 'error' , res . error ) ;
9411025 break ;
9421026 default :
9431027 call . emit (
9441028 'error' ,
945- new Error ( `Unknown StatementResult type: ${ res . type } ` ) ,
1029+ new Error ( `Unknown ReadRequestResult type: ${ res . type } ` ) ,
9461030 ) ;
9471031 }
9481032 } else {
@@ -1149,6 +1233,31 @@ export function createMockSpanner(server: grpc.Server): MockSpanner {
11491233 return mock ;
11501234}
11511235
1236+ export function createReadRequestResultSet ( ) : protobuf . ResultSet {
1237+ const fields = [
1238+ protobuf . StructType . Field . create ( {
1239+ name : 'ID' ,
1240+ type : protobuf . Type . create ( { code : protobuf . TypeCode . STRING } ) ,
1241+ } ) ,
1242+ protobuf . StructType . Field . create ( {
1243+ name : 'VALUE' ,
1244+ type : protobuf . Type . create ( { code : protobuf . TypeCode . STRING } ) ,
1245+ } ) ,
1246+ ] ;
1247+ const metadata = new protobuf . ResultSetMetadata ( {
1248+ rowType : new protobuf . StructType ( { fields} ) ,
1249+ } ) ;
1250+
1251+ return protobuf . ResultSet . create ( {
1252+ metadata,
1253+ rows : [
1254+ { values : [ { stringValue : 'a' } , { stringValue : 'Alpha' } ] } ,
1255+ { values : [ { stringValue : 'b' } , { stringValue : 'Beta' } ] } ,
1256+ { values : [ { stringValue : 'c' } , { stringValue : 'Gamma' } ] } ,
1257+ ] ,
1258+ } ) ;
1259+ }
1260+
11521261/**
11531262 * Creates a simple result set containing the following data:
11541263 *
0 commit comments