@@ -81,6 +81,7 @@ import {SessionFactory} from '../src/session-factory';
8181import { MultiplexedSession } from '../src/multiplexed-session' ;
8282import { WriteAtLeastOnceOptions } from '../src/database' ;
8383import { MetricsTracerFactory } from '../src/metrics/metrics-tracer-factory' ;
84+ import { randomUUID } from 'crypto' ;
8485
8586const {
8687 AlwaysOnSampler,
@@ -3917,6 +3918,124 @@ describe('Spanner with mock server', () => {
39173918 await database . close ( ) ;
39183919 } ) ;
39193920 } ) ;
3921+
3922+ // TODO: enable when mux session support is available in public methods
3923+ describe . skip ( 'when multiplexed session is enabled for R/W' , ( ) => {
3924+ before ( ( ) => {
3925+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'true' ;
3926+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW = 'true' ;
3927+ } ) ;
3928+
3929+ after ( ( ) => {
3930+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'false' ;
3931+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW = 'false' ;
3932+ } ) ;
3933+
3934+ it ( 'should select the insertOrUpdate(upsert)/delete(deleteRows) mutation key over insert' , async ( ) => {
3935+ const database = newTestDatabase ( ) ;
3936+ await database . runTransactionAsync ( async tx => {
3937+ tx . upsert ( 'foo' , [
3938+ { id : 1 , name : 'One' } ,
3939+ { id : 2 , name : 'Two' } ,
3940+ ] ) ;
3941+ tx . insert ( 'foo' , [ { id : 3 , name : 'Three' } ] ) ;
3942+ tx . insert ( 'foo' , [ { id : 4 , name : 'Four' } ] ) ;
3943+ tx . deleteRows ( 'foo' , [ '3' , '4' ] ) ;
3944+ await tx . commit ( ) ;
3945+ } ) ;
3946+
3947+ const beginTransactionRequest = spannerMock
3948+ . getRequests ( )
3949+ . filter ( val => {
3950+ return ( val as v1 . BeginTransactionRequest ) . mutationKey ;
3951+ } ) as v1 . BeginTransactionRequest [ ] ;
3952+
3953+ // assert on begin transaction request
3954+ assert . strictEqual ( beginTransactionRequest . length , 1 ) ;
3955+
3956+ // selected mutation key
3957+ const selectedMutationKey = beginTransactionRequest [ 0 ] ! . mutationKey ;
3958+
3959+ // assert that mutation key have been selected
3960+ assert . ok (
3961+ selectedMutationKey ,
3962+ 'A mutation key should have been selected' ,
3963+ ) ;
3964+
3965+ // get the type of mutation key
3966+ const mutationType = Object . keys ( selectedMutationKey ! ) [ 0 ] ;
3967+
3968+ // assert that mutation key is not insert
3969+ assert . notStrictEqual (
3970+ mutationType ,
3971+ 'insert' ,
3972+ 'The selected mutation key should not be "insert"' ,
3973+ ) ;
3974+
3975+ // assert that mutation key is either insertOrUpdate or delete
3976+ assert . ok (
3977+ [ 'insertOrUpdate' , 'delete' ] . includes ( mutationType ) ,
3978+ "Expected either 'insertOrUpdate' or 'delete' key." ,
3979+ ) ;
3980+
3981+ const commitRequest = spannerMock . getRequests ( ) . filter ( val => {
3982+ return ( val as v1 . CommitRequest ) . precommitToken ;
3983+ } ) as v1 . CommitRequest [ ] ;
3984+
3985+ // assert on commit request
3986+ assert . strictEqual ( commitRequest . length , 1 ) ;
3987+ await database . close ( ) ;
3988+ } ) ;
3989+
3990+ it ( 'should select the mutation key with highest number of values when insert key(s) are present' , async ( ) => {
3991+ const database = newTestDatabase ( ) ;
3992+ await database . runTransactionAsync ( async tx => {
3993+ tx . insert ( 'foo' , [
3994+ { id : randomUUID ( ) , name : 'One' } ,
3995+ { id : randomUUID ( ) , name : 'Two' } ,
3996+ { id : randomUUID ( ) , name : 'Three' } ,
3997+ ] ) ;
3998+ tx . insert ( 'foo' , { id : randomUUID ( ) , name : 'Four' } ) ;
3999+ await tx . commit ( ) ;
4000+ } ) ;
4001+
4002+ const beginTransactionRequest = spannerMock
4003+ . getRequests ( )
4004+ . filter ( val => {
4005+ return ( val as v1 . BeginTransactionRequest ) . mutationKey ;
4006+ } ) as v1 . BeginTransactionRequest [ ] ;
4007+
4008+ // assert on begin transaction request
4009+ assert . strictEqual ( beginTransactionRequest . length , 1 ) ;
4010+
4011+ // selected mutation key
4012+ const selectedMutationKey = beginTransactionRequest [ 0 ] ! . mutationKey ;
4013+
4014+ // assert that mutation key have been selected
4015+ assert . ok (
4016+ selectedMutationKey ,
4017+ 'A mutation key should have been selected' ,
4018+ ) ;
4019+
4020+ // assert that mutation key is insert
4021+ const mutationType = Object . keys ( selectedMutationKey ! ) [ 0 ] ;
4022+ assert . ok (
4023+ [ 'insert' ] . includes ( mutationType ) ,
4024+ 'insert key must have been selected' ,
4025+ ) ;
4026+
4027+ // assert that insert mutation key with highest number of rows has been selected
4028+ assert . strictEqual ( selectedMutationKey . insert ?. values ?. length , 3 ) ;
4029+
4030+ const commitRequest = spannerMock . getRequests ( ) . filter ( val => {
4031+ return ( val as v1 . CommitRequest ) . precommitToken ;
4032+ } ) as v1 . CommitRequest [ ] ;
4033+
4034+ // assert on commit request
4035+ assert . strictEqual ( commitRequest . length , 1 ) ;
4036+ await database . close ( ) ;
4037+ } ) ;
4038+ } ) ;
39204039 } ) ;
39214040
39224041 describe ( 'hand-crafted transaction' , ( ) => {
@@ -5013,6 +5132,68 @@ describe('Spanner with mock server', () => {
50135132
50145133 await database . close ( ) ;
50155134 } ) ;
5135+
5136+ // TODO: enable when mux session support is available in public methods
5137+ describe . skip ( 'when multiplexed session is enabled for R/W' , ( ) => {
5138+ before ( ( ) => {
5139+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'true' ;
5140+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW = 'true' ;
5141+ } ) ;
5142+
5143+ after ( ( ) => {
5144+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'false' ;
5145+ process . env . GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS_FOR_RW = 'false' ;
5146+ } ) ;
5147+
5148+ it ( 'should pass the mutation key in begin transaction request in case of mutations only transactions' , async ( ) => {
5149+ const database = newTestDatabase ( ) ;
5150+ await database . table ( 'foo' ) . upsert ( { id : 1 , name : randomUUID ( ) } ) ;
5151+ await database . table ( 'foo' ) . insert ( { id : 2 , name : randomUUID ( ) } ) ;
5152+ await database . table ( 'foo' ) . deleteRows ( [ '2' ] ) ;
5153+
5154+ const beginTransactionRequest = spannerMock
5155+ . getRequests ( )
5156+ . filter ( val => {
5157+ return ( val as v1 . BeginTransactionRequest ) . mutationKey ;
5158+ } ) as v1 . BeginTransactionRequest [ ] ;
5159+
5160+ // assert on begin transaction request
5161+ assert . strictEqual ( beginTransactionRequest . length , 3 ) ;
5162+
5163+ // assert that on first begin transaction request insertOrUpdate is being selected as mutation key
5164+ assert . ok (
5165+ [ 'insertOrUpdate' ] . includes (
5166+ Object . keys ( beginTransactionRequest [ 0 ] ! . mutationKey ! ) [ 0 ] ,
5167+ ) ,
5168+ 'insertOrUpdate key must have been selected' ,
5169+ ) ;
5170+
5171+ // assert that on second begin transaction request insert is being selected as mutation key
5172+ assert . ok (
5173+ [ 'insert' ] . includes (
5174+ Object . keys ( beginTransactionRequest [ 1 ] ! . mutationKey ! ) [ 0 ] ,
5175+ ) ,
5176+ 'insert key must have been selected' ,
5177+ ) ;
5178+
5179+ // assert that on third begin transaction request delete is being selected as mutation key
5180+ assert . ok (
5181+ [ 'delete' ] . includes (
5182+ Object . keys ( beginTransactionRequest [ 2 ] ! . mutationKey ! ) [ 0 ] ,
5183+ ) ,
5184+ 'delete key must have been selected' ,
5185+ ) ;
5186+
5187+ const commitRequest = spannerMock . getRequests ( ) . filter ( val => {
5188+ return ( val as v1 . CommitRequest ) . precommitToken ;
5189+ } ) as v1 . CommitRequest [ ] ;
5190+
5191+ // assert on commit request
5192+ assert . strictEqual ( commitRequest . length , 3 ) ;
5193+
5194+ await database . close ( ) ;
5195+ } ) ;
5196+ } ) ;
50165197 } ) ;
50175198
50185199 describe ( 'chunking' , ( ) => {
0 commit comments