diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 1a9b47e2c3b..adab8c9d14d 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -29,14 +29,18 @@ shared_context 'setup: tags' do expect(recvd_rpc).to_not eq nil server_call = recvd_rpc.call ops = { CallOps::SEND_INITIAL_METADATA => metadata } - svr_batch = server_call.run_batch(ops) - expect(svr_batch.send_metadata).to be true + server_batch = server_call.run_batch(ops) + expect(server_batch.send_metadata).to be true server_call end def new_client_call @ch.create_call(nil, nil, '/method', nil, deadline) end + + def ok_status + Struct::Status.new(StatusCodes::OK, 'OK') + end end shared_examples 'basic GRPC message delivery is OK' do @@ -70,19 +74,32 @@ shared_examples 'basic GRPC message delivery is OK' do client_ops = { CallOps::SEND_INITIAL_METADATA => {}, - CallOps::SEND_MESSAGE => sent_message + CallOps::SEND_MESSAGE => sent_message, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.send_message).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_message).to be true + expect(client_batch.send_close).to be true # confirm the server can read the inbound message server_thread.join server_ops = { - CallOps::RECV_MESSAGE => nil + CallOps::RECV_MESSAGE => nil, + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_STATUS_FROM_SERVER => ok_status } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.message).to eq(sent_message) + server_batch = server_call.run_batch(server_ops) + expect(server_batch.message).to eq(sent_message) + expect(server_batch.send_close).to be true + expect(server_batch.send_status).to be true + + # finish the call + final_client_batch = call.run_batch( + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.status.code).to eq(0) end it 'responses written by servers are received by the client' do @@ -95,21 +112,36 @@ shared_examples 'basic GRPC message delivery is OK' do client_ops = { CallOps::SEND_INITIAL_METADATA => {}, - CallOps::SEND_MESSAGE => sent_message + CallOps::SEND_MESSAGE => sent_message, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.send_message).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_message).to be true + expect(client_batch.send_close).to be true # confirm the server can read the inbound message server_thread.join server_ops = { CallOps::RECV_MESSAGE => nil, - CallOps::SEND_MESSAGE => reply_text + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_MESSAGE => reply_text, + CallOps::SEND_STATUS_FROM_SERVER => ok_status } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.message).to eq(sent_message) - expect(svr_batch.send_message).to be true + server_batch = server_call.run_batch(server_ops) + expect(server_batch.message).to eq(sent_message) + expect(server_batch.send_close).to be true + expect(server_batch.send_message).to be true + expect(server_batch.send_status).to be true + + # finish the call + final_client_batch = call.run_batch( + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_MESSAGE => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.message).to eq(reply_text) + expect(final_client_batch.status.code).to eq(0) end it 'compressed messages can be sent and received' do @@ -125,30 +157,37 @@ shared_examples 'basic GRPC message delivery is OK' do client_ops = { CallOps::SEND_INITIAL_METADATA => md, - CallOps::SEND_MESSAGE => long_request_str + CallOps::SEND_MESSAGE => long_request_str, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.send_message).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_message).to be true + expect(client_batch.send_close).to be true # confirm the server can read the inbound message server_thread.join server_ops = { CallOps::RECV_MESSAGE => nil, - CallOps::SEND_MESSAGE => long_response_str + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_MESSAGE => long_response_str, + CallOps::SEND_STATUS_FROM_SERVER => ok_status } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.message).to eq(long_request_str) - expect(svr_batch.send_message).to be true + server_batch = server_call.run_batch(server_ops) + expect(server_batch.message).to eq(long_request_str) + expect(server_batch.send_close).to be true + expect(server_batch.send_message).to be true + expect(server_batch.send_status).to be true client_ops = { - CallOps::SEND_CLOSE_FROM_CLIENT => nil, CallOps::RECV_INITIAL_METADATA => nil, - CallOps::RECV_MESSAGE => nil + CallOps::RECV_MESSAGE => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_close).to be true - expect(batch_result.message).to eq long_response_str + final_client_batch = call.run_batch(client_ops) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.message).to eq long_response_str + expect(final_client_batch.status.code).to eq(0) end it 'servers can ignore a client write and send a status' do @@ -161,11 +200,13 @@ shared_examples 'basic GRPC message delivery is OK' do client_ops = { CallOps::SEND_INITIAL_METADATA => {}, - CallOps::SEND_MESSAGE => sent_message + CallOps::SEND_MESSAGE => sent_message, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.send_message).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_message).to be true + expect(client_batch.send_close).to be true # confirm the server can read the inbound message the_status = Struct::Status.new(StatusCodes::OK, 'OK') @@ -173,9 +214,15 @@ shared_examples 'basic GRPC message delivery is OK' do server_ops = { CallOps::SEND_STATUS_FROM_SERVER => the_status } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.message).to eq nil - expect(svr_batch.send_status).to be true + server_batch = server_call.run_batch(server_ops) + expect(server_batch.message).to eq nil + expect(server_batch.send_status).to be true + + final_client_batch = call.run_batch( + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.status.code).to eq(0) end it 'completes calls by sending status to client and server' do @@ -190,9 +237,9 @@ shared_examples 'basic GRPC message delivery is OK' do CallOps::SEND_INITIAL_METADATA => {}, CallOps::SEND_MESSAGE => sent_message } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.send_message).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_message).to be true # confirm the server can read the inbound message and respond the_status = Struct::Status.new(StatusCodes::OK, 'OK', {}) @@ -202,10 +249,10 @@ shared_examples 'basic GRPC message delivery is OK' do CallOps::SEND_MESSAGE => reply_text, CallOps::SEND_STATUS_FROM_SERVER => the_status } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.message).to eq sent_message - expect(svr_batch.send_status).to be true - expect(svr_batch.send_message).to be true + server_batch = server_call.run_batch(server_ops) + expect(server_batch.message).to eq sent_message + expect(server_batch.send_status).to be true + expect(server_batch.send_message).to be true # confirm the client can receive the server response and status. client_ops = { @@ -214,17 +261,17 @@ shared_examples 'basic GRPC message delivery is OK' do CallOps::RECV_MESSAGE => nil, CallOps::RECV_STATUS_ON_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_close).to be true - expect(batch_result.message).to eq reply_text - expect(batch_result.status).to eq the_status + final_client_batch = call.run_batch(client_ops) + expect(final_client_batch.send_close).to be true + expect(final_client_batch.message).to eq reply_text + expect(final_client_batch.status).to eq the_status # confirm the server can receive the client close. server_ops = { CallOps::RECV_CLOSE_ON_SERVER => nil } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.send_close).to be true + final_server_batch = server_call.run_batch(server_ops) + expect(final_server_batch.send_close).to be true end def client_cancel_test(cancel_proc, expected_code, @@ -240,9 +287,9 @@ shared_examples 'basic GRPC message delivery is OK' do CallOps::SEND_INITIAL_METADATA => {}, CallOps::RECV_INITIAL_METADATA => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true - expect(batch_result.metadata).to eq({}) + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.metadata).to eq({}) cancel_proc.call(call) @@ -250,16 +297,16 @@ shared_examples 'basic GRPC message delivery is OK' do server_ops = { CallOps::RECV_CLOSE_ON_SERVER => nil } - svr_batch = server_call.run_batch(server_ops) - expect(svr_batch.send_close).to be true + server_batch = server_call.run_batch(server_ops) + expect(server_batch.send_close).to be true client_ops = { CallOps::RECV_STATUS_ON_CLIENT => {} } - batch_result = call.run_batch(client_ops) + client_batch = call.run_batch(client_ops) - expect(batch_result.status.code).to be expected_code - expect(batch_result.status.details).to eq expected_details + expect(client_batch.status.code).to be expected_code + expect(client_batch.status.details).to eq expected_details end it 'clients can cancel a call on the server' do @@ -325,10 +372,11 @@ shared_examples 'GRPC metadata delivery works OK' do call = new_client_call client_ops = { - CallOps::SEND_INITIAL_METADATA => md + CallOps::SEND_INITIAL_METADATA => md, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true # confirm the server can receive the client metadata rcv_thread.join @@ -336,6 +384,21 @@ shared_examples 'GRPC metadata delivery works OK' do recvd_md = recvd_rpc.metadata replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] expect(recvd_md).to eq(recvd_md.merge(replace_symbols)) + + # finish the call + final_server_batch = recvd_rpc.call.run_batch( + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_INITIAL_METADATA => nil, + CallOps::SEND_STATUS_FROM_SERVER => ok_status) + expect(final_server_batch.send_close).to be(true) + expect(final_server_batch.send_metadata).to be(true) + expect(final_server_batch.send_status).to be(true) + + final_client_batch = call.run_batch( + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.status.code).to eq(0) end end end @@ -381,6 +444,9 @@ shared_examples 'GRPC metadata delivery works OK' do recvd_rpc.call.run_batch(server_ops) end expect(&blk).to raise_error + + # cancel the call so the server can shut down immediately + call.cancel end end @@ -394,25 +460,37 @@ shared_examples 'GRPC metadata delivery works OK' do # client signals that it's done sending metadata to allow server to # respond client_ops = { - CallOps::SEND_INITIAL_METADATA => nil + CallOps::SEND_INITIAL_METADATA => nil, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - call.run_batch(client_ops) + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_close).to be true # server gets the invocation but sends no metadata back rcv_thread.join expect(recvd_rpc).to_not eq nil server_call = recvd_rpc.call server_ops = { - CallOps::SEND_INITIAL_METADATA => nil + # receive close and send status to finish the call + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_INITIAL_METADATA => nil, + CallOps::SEND_STATUS_FROM_SERVER => ok_status } - server_call.run_batch(server_ops) + srv_batch = server_call.run_batch(server_ops) + expect(srv_batch.send_close).to be true + expect(srv_batch.send_metadata).to be true + expect(srv_batch.send_status).to be true # client receives nothing as expected client_ops = { - CallOps::RECV_INITIAL_METADATA => nil + CallOps::RECV_INITIAL_METADATA => nil, + # receive status to finish the call + CallOps::RECV_STATUS_ON_CLIENT => nil } - batch_result = call.run_batch(client_ops) - expect(batch_result.metadata).to eq({}) + final_client_batch = call.run_batch(client_ops) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.status.code).to eq(0) end it 'sends all the pairs when keys and values are valid' do @@ -426,26 +504,36 @@ shared_examples 'GRPC metadata delivery works OK' do # client signals that it's done sending metadata to allow server to # respond client_ops = { - CallOps::SEND_INITIAL_METADATA => nil + CallOps::SEND_INITIAL_METADATA => nil, + CallOps::SEND_CLOSE_FROM_CLIENT => nil } - call.run_batch(client_ops) + client_batch = call.run_batch(client_ops) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_close).to be true # server gets the invocation but sends no metadata back rcv_thread.join expect(recvd_rpc).to_not eq nil server_call = recvd_rpc.call server_ops = { - CallOps::SEND_INITIAL_METADATA => md + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_INITIAL_METADATA => md, + CallOps::SEND_STATUS_FROM_SERVER => ok_status } - server_call.run_batch(server_ops) + srv_batch = server_call.run_batch(server_ops) + expect(srv_batch.send_close).to be true + expect(srv_batch.send_metadata).to be true + expect(srv_batch.send_status).to be true # client receives nothing as expected client_ops = { - CallOps::RECV_INITIAL_METADATA => nil + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil } - batch_result = call.run_batch(client_ops) + final_client_batch = call.run_batch(client_ops) replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }] - expect(batch_result.metadata).to eq(replace_symbols) + expect(final_client_batch.metadata).to eq(replace_symbols) + expect(final_client_batch.status.code).to eq(0) end end end @@ -510,8 +598,7 @@ describe 'the secure http client/server' do initial_md_key = 'k2' initial_md_val = 'v2' - initial_md = {} - initial_md[initial_md_key] = initial_md_val + initial_md = { initial_md_key => initial_md_val } expected_md = creds_update_md.clone fail 'bad test param' unless expected_md[initial_md_key].nil? expected_md[initial_md_key] = initial_md_val @@ -523,11 +610,12 @@ describe 'the secure http client/server' do call = new_client_call call.set_credentials! call_creds - client_ops = { - CallOps::SEND_INITIAL_METADATA => initial_md - } - batch_result = call.run_batch(client_ops) - expect(batch_result.send_metadata).to be true + + client_batch = call.run_batch( + CallOps::SEND_INITIAL_METADATA => initial_md, + CallOps::SEND_CLOSE_FROM_CLIENT => nil) + expect(client_batch.send_metadata).to be true + expect(client_batch.send_close).to be true # confirm the server can receive the client metadata rcv_thread.join @@ -535,6 +623,24 @@ describe 'the secure http client/server' do recvd_md = recvd_rpc.metadata replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }] expect(recvd_md).to eq(recvd_md.merge(replace_symbols)) + + credentials_update_test_finish_call(call, recvd_rpc.call) + end + + def credentials_update_test_finish_call(client_call, server_call) + final_server_batch = server_call.run_batch( + CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_INITIAL_METADATA => nil, + CallOps::SEND_STATUS_FROM_SERVER => ok_status) + expect(final_server_batch.send_close).to be(true) + expect(final_server_batch.send_metadata).to be(true) + expect(final_server_batch.send_status).to be(true) + + final_client_batch = client_call.run_batch( + CallOps::RECV_INITIAL_METADATA => nil, + CallOps::RECV_STATUS_ON_CLIENT => nil) + expect(final_client_batch.metadata).to eq({}) + expect(final_client_batch.status.code).to eq(0) end it 'modifies metadata with CallCredentials' do diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb index ec0c2941741..a00df9236d2 100644 --- a/src/ruby/spec/generic/active_call_spec.rb +++ b/src/ruby/spec/generic/active_call_spec.rb @@ -22,6 +22,21 @@ describe GRPC::ActiveCall do CallOps = GRPC::Core::CallOps WriteFlags = GRPC::Core::WriteFlags + def ok_status + Struct::Status.new(OK, 'OK') + end + + def send_and_receive_close_and_status(client_call, server_call) + client_call.run_batch(CallOps::SEND_CLOSE_FROM_CLIENT => nil) + server_call.run_batch(CallOps::RECV_CLOSE_ON_SERVER => nil, + CallOps::SEND_STATUS_FROM_SERVER => ok_status) + client_call.run_batch(CallOps::RECV_STATUS_ON_CLIENT => nil) + end + + def inner_call_of_active_call(active_call) + active_call.instance_variable_get(:@call) + end + before(:each) do @pass_through = proc { |x| x } host = '0.0.0.0:0' @@ -70,13 +85,13 @@ describe GRPC::ActiveCall do end describe '#remote_send' do - it 'allows a client to send a payload to the server' do + it 'allows a client to send a payload to the server', test: true do call = make_test_call ActiveCall.client_invoke(call) - @client_call = ActiveCall.new(call, @pass_through, - @pass_through, deadline) + client_call = ActiveCall.new(call, @pass_through, + @pass_through, deadline) msg = 'message is a string' - @client_call.remote_send(msg) + client_call.remote_send(msg) # check that server rpc new was received recvd_rpc = @server.request_call @@ -86,8 +101,13 @@ describe GRPC::ActiveCall do # Accept the call, and verify that the server reads the response ok. server_call = ActiveCall.new(recvd_call, @pass_through, @pass_through, deadline, - metadata_received: true) + metadata_received: true, + started: false) expect(server_call.remote_read).to eq(msg) + # finish the call + server_call.send_initial_metadata + call.run_batch(CallOps::RECV_INITIAL_METADATA => nil) + send_and_receive_close_and_status(call, recvd_call) end it 'marshals the payload using the marshal func' do @@ -109,6 +129,9 @@ describe GRPC::ActiveCall do @pass_through, deadline, metadata_received: true) expect(server_call.remote_read).to eq('marshalled:' + msg) + # finish the call + call.run_batch(CallOps::RECV_INITIAL_METADATA => nil) + send_and_receive_close_and_status(call, recvd_call) end TEST_WRITE_FLAGS = [WriteFlags::BUFFER_HINT, WriteFlags::NO_COMPRESS] @@ -136,6 +159,9 @@ describe GRPC::ActiveCall do @pass_through, deadline, metadata_received: true) expect(server_call.remote_read).to eq('marshalled:' + msg) + # finish the call + server_call.send_status(OK, '', true) + client_call.receive_and_check_status end end end @@ -177,7 +203,6 @@ describe GRPC::ActiveCall do @pass_through, @pass_through, deadline) - expect(@client_call.metadata_sent).to eql(true) expect(call).to( receive(:run_batch).with(hash_including( @@ -291,6 +316,10 @@ describe GRPC::ActiveCall do expect(recvd_rpc.metadata).to_not be_nil expect(recvd_rpc.metadata['k1']).to eq('v1') expect(recvd_rpc.metadata['k2']).to eq('v2') + # finish the call + recvd_call.run_batch(CallOps::SEND_INITIAL_METADATA => {}) + call.run_batch(CallOps::RECV_INITIAL_METADATA => nil) + send_and_receive_close_and_status(call, recvd_call) end end @@ -324,6 +353,8 @@ describe GRPC::ActiveCall do server_call = expect_server_to_receive(msg) server_call.remote_send('server_response') expect(client_call.remote_read).to eq('server_response') + send_and_receive_close_and_status( + call, inner_call_of_active_call(server_call)) end it 'saves no metadata when the server adds no metadata' do @@ -338,6 +369,8 @@ describe GRPC::ActiveCall do expect(client_call.metadata).to be_nil client_call.remote_read expect(client_call.metadata).to eq({}) + send_and_receive_close_and_status( + call, inner_call_of_active_call(server_call)) end it 'saves metadata add by the server' do @@ -353,6 +386,8 @@ describe GRPC::ActiveCall do client_call.remote_read expected = { 'k1' => 'v1', 'k2' => 'v2' } expect(client_call.metadata).to eq(expected) + send_and_receive_close_and_status( + call, inner_call_of_active_call(server_call)) end it 'get a status from server when nothing else sent from server' do @@ -409,6 +444,8 @@ describe GRPC::ActiveCall do server_call = expect_server_to_receive(msg) server_call.remote_send('server_response') expect(client_call.remote_read).to eq('unmarshalled:server_response') + send_and_receive_close_and_status( + call, inner_call_of_active_call(server_call)) end end @@ -418,9 +455,11 @@ describe GRPC::ActiveCall do client_call = ActiveCall.new(call, @pass_through, @pass_through, deadline) expect(client_call.each_remote_read).to be_a(Enumerator) + # finish the call + client_call.cancel end - it 'the returns an enumerator that can read n responses' do + it 'the returned enumerator can read n responses' do call = make_test_call ActiveCall.client_invoke(call) client_call = ActiveCall.new(call, @pass_through, @@ -435,6 +474,8 @@ describe GRPC::ActiveCall do server_call.remote_send(reply) expect(e.next).to eq(reply) end + send_and_receive_close_and_status( + call, inner_call_of_active_call(server_call)) end it 'the returns an enumerator that stops after an OK Status' do @@ -453,7 +494,7 @@ describe GRPC::ActiveCall do server_call.remote_send(reply) expect(e.next).to eq(reply) end - server_call.send_status(OK, 'OK') + server_call.send_status(OK, 'OK', true) expect { e.next }.to raise_error(StopIteration) end end