xref: /aosp_15_r20/external/grpc-grpc/src/ruby/spec/client_server_spec.rb (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15require 'spec_helper'
16
17include GRPC::Core
18
19shared_context 'setup: tags' do
20  let(:sent_message) { 'sent message' }
21  let(:reply_text) { 'the reply' }
22
23  def deadline
24    Time.now + 5
25  end
26
27  def server_allows_client_to_proceed(metadata = {})
28    recvd_rpc = @server.request_call
29    expect(recvd_rpc).to_not eq nil
30    server_call = recvd_rpc.call
31    ops = { CallOps::SEND_INITIAL_METADATA => metadata }
32    server_batch = server_call.run_batch(ops)
33    expect(server_batch.send_metadata).to be true
34    server_call
35  end
36
37  def new_client_call
38    @ch.create_call(nil, nil, '/method', nil, deadline)
39  end
40
41  def ok_status
42    Struct::Status.new(StatusCodes::OK, 'OK')
43  end
44end
45
46shared_examples 'basic GRPC message delivery is OK' do
47  include GRPC::Core
48  include_context 'setup: tags'
49
50  context 'the test channel' do
51    it 'should have a target' do
52      expect(@ch.target).to be_a(String)
53    end
54  end
55
56  context 'a client call' do
57    it 'should have a peer' do
58      expect(new_client_call.peer).to be_a(String)
59    end
60  end
61
62  it 'calls have peer info' do
63    call = new_client_call
64    expect(call.peer).to be_a(String)
65  end
66
67  it 'servers receive requests from clients and can respond' do
68    call = new_client_call
69    server_call = nil
70
71    server_thread = Thread.new do
72      server_call = server_allows_client_to_proceed
73    end
74
75    client_ops = {
76      CallOps::SEND_INITIAL_METADATA => {},
77      CallOps::SEND_MESSAGE => sent_message,
78      CallOps::SEND_CLOSE_FROM_CLIENT => nil
79    }
80    client_batch = call.run_batch(client_ops)
81    expect(client_batch.send_metadata).to be true
82    expect(client_batch.send_message).to be true
83    expect(client_batch.send_close).to be true
84
85    # confirm the server can read the inbound message
86    server_thread.join
87    server_ops = {
88      CallOps::RECV_MESSAGE => nil
89    }
90    server_batch = server_call.run_batch(server_ops)
91    expect(server_batch.message).to eq(sent_message)
92    server_ops = {
93      CallOps::RECV_CLOSE_ON_SERVER => nil,
94      CallOps::SEND_STATUS_FROM_SERVER => ok_status
95    }
96    server_batch = server_call.run_batch(server_ops)
97    expect(server_batch.send_close).to be true
98    expect(server_batch.send_status).to be true
99
100    # finish the call
101    final_client_batch = call.run_batch(
102      CallOps::RECV_INITIAL_METADATA => nil,
103      CallOps::RECV_STATUS_ON_CLIENT => nil)
104    expect(final_client_batch.metadata).to eq({})
105    expect(final_client_batch.status.code).to eq(0)
106  end
107
108  it 'responses written by servers are received by the client' do
109    call = new_client_call
110    server_call = nil
111
112    server_thread = Thread.new do
113      server_call = server_allows_client_to_proceed
114    end
115
116    client_ops = {
117      CallOps::SEND_INITIAL_METADATA => {},
118      CallOps::SEND_MESSAGE => sent_message,
119      CallOps::SEND_CLOSE_FROM_CLIENT => nil
120    }
121    client_batch = call.run_batch(client_ops)
122    expect(client_batch.send_metadata).to be true
123    expect(client_batch.send_message).to be true
124    expect(client_batch.send_close).to be true
125
126    # confirm the server can read the inbound message
127    server_thread.join
128    server_ops = {
129      CallOps::RECV_MESSAGE => nil
130    }
131    server_batch = server_call.run_batch(server_ops)
132    expect(server_batch.message).to eq(sent_message)
133    server_ops = {
134      CallOps::RECV_CLOSE_ON_SERVER => nil,
135      CallOps::SEND_MESSAGE => reply_text,
136      CallOps::SEND_STATUS_FROM_SERVER => ok_status
137    }
138    server_batch = server_call.run_batch(server_ops)
139    expect(server_batch.send_close).to be true
140    expect(server_batch.send_message).to be true
141    expect(server_batch.send_status).to be true
142
143    # finish the call
144    final_client_batch = call.run_batch(
145      CallOps::RECV_INITIAL_METADATA => nil,
146      CallOps::RECV_MESSAGE => nil,
147      CallOps::RECV_STATUS_ON_CLIENT => nil)
148    expect(final_client_batch.metadata).to eq({})
149    expect(final_client_batch.message).to eq(reply_text)
150    expect(final_client_batch.status.code).to eq(0)
151  end
152
153  it 'compressed messages can be sent and received' do
154    call = new_client_call
155    server_call = nil
156    long_request_str = '0' * 2000
157    long_response_str = '1' * 2000
158    md = { 'grpc-internal-encoding-request' => 'gzip' }
159
160    server_thread = Thread.new do
161      server_call = server_allows_client_to_proceed(md)
162    end
163
164    client_ops = {
165      CallOps::SEND_INITIAL_METADATA => md,
166      CallOps::SEND_MESSAGE => long_request_str,
167      CallOps::SEND_CLOSE_FROM_CLIENT => nil
168    }
169    client_batch = call.run_batch(client_ops)
170    expect(client_batch.send_metadata).to be true
171    expect(client_batch.send_message).to be true
172    expect(client_batch.send_close).to be true
173
174    # confirm the server can read the inbound message
175    server_thread.join
176    server_ops = {
177      CallOps::RECV_MESSAGE => nil
178    }
179    server_batch = server_call.run_batch(server_ops)
180    expect(server_batch.message).to eq(long_request_str)
181    server_ops = {
182      CallOps::RECV_CLOSE_ON_SERVER => nil,
183      CallOps::SEND_MESSAGE => long_response_str,
184      CallOps::SEND_STATUS_FROM_SERVER => ok_status
185    }
186    server_batch = server_call.run_batch(server_ops)
187    expect(server_batch.send_close).to be true
188    expect(server_batch.send_message).to be true
189    expect(server_batch.send_status).to be true
190
191    client_ops = {
192      CallOps::RECV_INITIAL_METADATA => nil,
193      CallOps::RECV_MESSAGE => nil,
194      CallOps::RECV_STATUS_ON_CLIENT => nil
195    }
196    final_client_batch = call.run_batch(client_ops)
197    expect(final_client_batch.metadata).to eq({})
198    expect(final_client_batch.message).to eq long_response_str
199    expect(final_client_batch.status.code).to eq(0)
200  end
201
202  it 'servers can ignore a client write and send a status' do
203    call = new_client_call
204    server_call = nil
205
206    server_thread = Thread.new do
207      server_call = server_allows_client_to_proceed
208    end
209
210    client_ops = {
211      CallOps::SEND_INITIAL_METADATA => {},
212      CallOps::SEND_MESSAGE => sent_message,
213      CallOps::SEND_CLOSE_FROM_CLIENT => nil
214    }
215    client_batch = call.run_batch(client_ops)
216    expect(client_batch.send_metadata).to be true
217    expect(client_batch.send_message).to be true
218    expect(client_batch.send_close).to be true
219
220    # confirm the server can read the inbound message
221    the_status = Struct::Status.new(StatusCodes::OK, 'OK')
222    server_thread.join
223    server_ops = {
224      CallOps::SEND_STATUS_FROM_SERVER => the_status
225    }
226    server_batch = server_call.run_batch(server_ops)
227    expect(server_batch.message).to eq nil
228    expect(server_batch.send_status).to be true
229
230    final_client_batch = call.run_batch(
231      CallOps::RECV_INITIAL_METADATA => nil,
232      CallOps::RECV_STATUS_ON_CLIENT => nil)
233    expect(final_client_batch.metadata).to eq({})
234    expect(final_client_batch.status.code).to eq(0)
235  end
236
237  it 'completes calls by sending status to client and server' do
238    call = new_client_call
239    server_call = nil
240
241    server_thread = Thread.new do
242      server_call = server_allows_client_to_proceed
243    end
244
245    client_ops = {
246      CallOps::SEND_INITIAL_METADATA => {},
247      CallOps::SEND_MESSAGE => sent_message
248    }
249    client_batch = call.run_batch(client_ops)
250    expect(client_batch.send_metadata).to be true
251    expect(client_batch.send_message).to be true
252
253    # confirm the server can read the inbound message and respond
254    the_status = Struct::Status.new(StatusCodes::OK, 'OK', {})
255    server_thread.join
256    server_ops = {
257      CallOps::RECV_MESSAGE => nil
258    }
259    server_batch = server_call.run_batch(server_ops)
260    expect(server_batch.message).to eq sent_message
261    server_ops = {
262      CallOps::SEND_MESSAGE => reply_text,
263      CallOps::SEND_STATUS_FROM_SERVER => the_status
264    }
265    server_batch = server_call.run_batch(server_ops)
266    expect(server_batch.send_status).to be true
267    expect(server_batch.send_message).to be true
268
269    # confirm the client can receive the server response and status.
270    client_ops = {
271      CallOps::SEND_CLOSE_FROM_CLIENT => nil,
272      CallOps::RECV_INITIAL_METADATA => nil,
273      CallOps::RECV_MESSAGE => nil,
274      CallOps::RECV_STATUS_ON_CLIENT => nil
275    }
276    final_client_batch = call.run_batch(client_ops)
277    expect(final_client_batch.send_close).to be true
278    expect(final_client_batch.message).to eq reply_text
279    expect(final_client_batch.status).to eq the_status
280
281    # confirm the server can receive the client close.
282    server_ops = {
283      CallOps::RECV_CLOSE_ON_SERVER => nil
284    }
285    final_server_batch = server_call.run_batch(server_ops)
286    expect(final_server_batch.send_close).to be true
287  end
288
289  def client_cancel_test(cancel_proc, expected_code,
290                         expected_details)
291    call = new_client_call
292    server_call = nil
293
294    server_thread = Thread.new do
295      server_call = server_allows_client_to_proceed
296    end
297
298    client_ops = {
299      CallOps::SEND_INITIAL_METADATA => {},
300      CallOps::RECV_INITIAL_METADATA => nil
301    }
302    client_batch = call.run_batch(client_ops)
303    expect(client_batch.send_metadata).to be true
304    expect(client_batch.metadata).to eq({})
305
306    cancel_proc.call(call)
307
308    server_thread.join
309    server_ops = {
310      CallOps::RECV_CLOSE_ON_SERVER => nil
311    }
312    server_batch = server_call.run_batch(server_ops)
313    expect(server_batch.send_close).to be true
314
315    client_ops = {
316      CallOps::RECV_STATUS_ON_CLIENT => {}
317    }
318    client_batch = call.run_batch(client_ops)
319
320    expect(client_batch.status.code).to be expected_code
321    expect(client_batch.status.details).to eq expected_details
322  end
323
324  it 'clients can cancel a call on the server' do
325    expected_code = StatusCodes::CANCELLED
326    expected_details = 'CANCELLED'
327    cancel_proc = proc { |call| call.cancel }
328    client_cancel_test(cancel_proc, expected_code, expected_details)
329  end
330
331  it 'cancel_with_status unknown status' do
332    code = StatusCodes::UNKNOWN
333    details = 'test unknown reason'
334    cancel_proc = proc { |call| call.cancel_with_status(code, details) }
335    client_cancel_test(cancel_proc, code, details)
336  end
337
338  it 'cancel_with_status unknown status' do
339    code = StatusCodes::FAILED_PRECONDITION
340    details = 'test failed precondition reason'
341    cancel_proc = proc { |call| call.cancel_with_status(code, details) }
342    client_cancel_test(cancel_proc, code, details)
343  end
344end
345
346shared_examples 'GRPC metadata delivery works OK' do
347  include_context 'setup: tags'
348
349  describe 'from client => server' do
350    before(:example) do
351      n = 7  # arbitrary number of metadata
352      diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
353      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
354      null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
355      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
356      same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
357      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
358      symbol_key = { a_key: 'a val' }
359      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
360      @bad_keys = []
361      @bad_keys << { Object.new => 'a value' }
362      @bad_keys << { 1 => 'a value' }
363    end
364
365    it 'raises an exception if a metadata key is invalid' do
366      @bad_keys.each do |md|
367        call = new_client_call
368        client_ops = {
369          CallOps::SEND_INITIAL_METADATA => md
370        }
371        blk = proc do
372          call.run_batch(client_ops)
373        end
374        expect(&blk).to raise_error
375      end
376    end
377
378    it 'sends all the metadata pairs when keys and values are valid' do
379      @valid_metadata.each do |md|
380        recvd_rpc = nil
381        rcv_thread = Thread.new do
382          recvd_rpc = @server.request_call
383        end
384
385        call = new_client_call
386        client_ops = {
387          CallOps::SEND_INITIAL_METADATA => md,
388          CallOps::SEND_CLOSE_FROM_CLIENT => nil
389        }
390        client_batch = call.run_batch(client_ops)
391        expect(client_batch.send_metadata).to be true
392
393        # confirm the server can receive the client metadata
394        rcv_thread.join
395        expect(recvd_rpc).to_not eq nil
396        recvd_md = recvd_rpc.metadata
397        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
398        expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
399
400        # finish the call
401        final_server_batch = recvd_rpc.call.run_batch(
402          CallOps::RECV_CLOSE_ON_SERVER => nil,
403          CallOps::SEND_INITIAL_METADATA => nil,
404          CallOps::SEND_STATUS_FROM_SERVER => ok_status)
405        expect(final_server_batch.send_close).to be(true)
406        expect(final_server_batch.send_metadata).to be(true)
407        expect(final_server_batch.send_status).to be(true)
408
409        final_client_batch = call.run_batch(
410          CallOps::RECV_INITIAL_METADATA => nil,
411          CallOps::RECV_STATUS_ON_CLIENT => nil)
412        expect(final_client_batch.metadata).to eq({})
413        expect(final_client_batch.status.code).to eq(0)
414      end
415    end
416  end
417
418  describe 'from server => client' do
419    before(:example) do
420      n = 7  # arbitrary number of metadata
421      diff_keys_fn = proc { |i| [format('k%d', i), format('v%d', i)] }
422      diff_keys = Hash[n.times.collect { |x| diff_keys_fn.call x }]
423      null_vals_fn = proc { |i| [format('k%d', i), format('v\0%d', i)] }
424      null_vals = Hash[n.times.collect { |x| null_vals_fn.call x }]
425      same_keys_fn = proc { |i| [format('k%d', i), [format('v%d', i)] * n] }
426      same_keys = Hash[n.times.collect { |x| same_keys_fn.call x }]
427      symbol_key = { a_key: 'a val' }
428      @valid_metadata = [diff_keys, same_keys, null_vals, symbol_key]
429      @bad_keys = []
430      @bad_keys << { Object.new => 'a value' }
431      @bad_keys << { 1 => 'a value' }
432    end
433
434    it 'raises an exception if a metadata key is invalid' do
435      @bad_keys.each do |md|
436        recvd_rpc = nil
437        rcv_thread = Thread.new do
438          recvd_rpc = @server.request_call
439        end
440
441        call = new_client_call
442        # client signals that it's done sending metadata to allow server to
443        # respond
444        client_ops = {
445          CallOps::SEND_INITIAL_METADATA => nil
446        }
447        call.run_batch(client_ops)
448
449        # server gets the invocation
450        rcv_thread.join
451        expect(recvd_rpc).to_not eq nil
452        server_ops = {
453          CallOps::SEND_INITIAL_METADATA => md
454        }
455        blk = proc do
456          recvd_rpc.call.run_batch(server_ops)
457        end
458        expect(&blk).to raise_error
459
460        # cancel the call so the server can shut down immediately
461        call.cancel
462      end
463    end
464
465    it 'sends an empty hash if no metadata is added' do
466      recvd_rpc = nil
467      rcv_thread = Thread.new do
468        recvd_rpc = @server.request_call
469      end
470
471      call = new_client_call
472      # client signals that it's done sending metadata to allow server to
473      # respond
474      client_ops = {
475        CallOps::SEND_INITIAL_METADATA => nil,
476        CallOps::SEND_CLOSE_FROM_CLIENT => nil
477      }
478      client_batch = call.run_batch(client_ops)
479      expect(client_batch.send_metadata).to be true
480      expect(client_batch.send_close).to be true
481
482      # server gets the invocation but sends no metadata back
483      rcv_thread.join
484      expect(recvd_rpc).to_not eq nil
485      server_call = recvd_rpc.call
486      server_ops = {
487        # receive close and send status to finish the call
488        CallOps::RECV_CLOSE_ON_SERVER => nil,
489        CallOps::SEND_INITIAL_METADATA => nil,
490        CallOps::SEND_STATUS_FROM_SERVER => ok_status
491      }
492      srv_batch = server_call.run_batch(server_ops)
493      expect(srv_batch.send_close).to be true
494      expect(srv_batch.send_metadata).to be true
495      expect(srv_batch.send_status).to be true
496
497      # client receives nothing as expected
498      client_ops = {
499        CallOps::RECV_INITIAL_METADATA => nil,
500        # receive status to finish the call
501        CallOps::RECV_STATUS_ON_CLIENT => nil
502      }
503      final_client_batch = call.run_batch(client_ops)
504      expect(final_client_batch.metadata).to eq({})
505      expect(final_client_batch.status.code).to eq(0)
506    end
507
508    it 'sends all the pairs when keys and values are valid' do
509      @valid_metadata.each do |md|
510        recvd_rpc = nil
511        rcv_thread = Thread.new do
512          recvd_rpc = @server.request_call
513        end
514
515        call = new_client_call
516        # client signals that it's done sending metadata to allow server to
517        # respond
518        client_ops = {
519          CallOps::SEND_INITIAL_METADATA => nil,
520          CallOps::SEND_CLOSE_FROM_CLIENT => nil
521        }
522        client_batch = call.run_batch(client_ops)
523        expect(client_batch.send_metadata).to be true
524        expect(client_batch.send_close).to be true
525
526        # server gets the invocation but sends no metadata back
527        rcv_thread.join
528        expect(recvd_rpc).to_not eq nil
529        server_call = recvd_rpc.call
530        server_ops = {
531          CallOps::RECV_CLOSE_ON_SERVER => nil,
532          CallOps::SEND_INITIAL_METADATA => md,
533          CallOps::SEND_STATUS_FROM_SERVER => ok_status
534        }
535        srv_batch = server_call.run_batch(server_ops)
536        expect(srv_batch.send_close).to be true
537        expect(srv_batch.send_metadata).to be true
538        expect(srv_batch.send_status).to be true
539
540        # client receives nothing as expected
541        client_ops = {
542          CallOps::RECV_INITIAL_METADATA => nil,
543          CallOps::RECV_STATUS_ON_CLIENT => nil
544        }
545        final_client_batch = call.run_batch(client_ops)
546        replace_symbols = Hash[md.each_pair.collect { |x, y| [x.to_s, y] }]
547        expect(final_client_batch.metadata).to eq(replace_symbols)
548        expect(final_client_batch.status.code).to eq(0)
549      end
550    end
551  end
552end
553
554describe 'the http client/server' do
555  before(:example) do
556    server_host = '0.0.0.0:0'
557    @server = new_core_server_for_testing(nil)
558    server_port = @server.add_http2_port(server_host, :this_port_is_insecure)
559    @server.start
560    @ch = Channel.new("0.0.0.0:#{server_port}", nil, :this_channel_is_insecure)
561  end
562
563  after(:example) do
564    @ch.close
565    @server.shutdown_and_notify(deadline)
566    @server.close
567  end
568
569  it_behaves_like 'basic GRPC message delivery is OK' do
570  end
571
572  it_behaves_like 'GRPC metadata delivery works OK' do
573  end
574end
575
576describe 'the secure http client/server' do
577  include_context 'setup: tags'
578
579  def load_test_certs
580    test_root = File.join(File.dirname(__FILE__), 'testdata')
581    files = ['ca.pem', 'server1.key', 'server1.pem']
582    files.map { |f| File.open(File.join(test_root, f)).read }
583  end
584
585  before(:example) do
586    certs = load_test_certs
587    server_host = '0.0.0.0:0'
588    server_creds = GRPC::Core::ServerCredentials.new(
589      nil, [{ private_key: certs[1], cert_chain: certs[2] }], false)
590    @server = new_core_server_for_testing(nil)
591    server_port = @server.add_http2_port(server_host, server_creds)
592    @server.start
593    args = { Channel::SSL_TARGET => 'foo.test.google.fr' }
594    @ch = Channel.new("0.0.0.0:#{server_port}", args,
595                      GRPC::Core::ChannelCredentials.new(certs[0], nil, nil))
596  end
597
598  after(:example) do
599    @server.shutdown_and_notify(deadline)
600    @server.close
601  end
602
603  it_behaves_like 'basic GRPC message delivery is OK' do
604  end
605
606  it_behaves_like 'GRPC metadata delivery works OK' do
607  end
608
609  def credentials_update_test(creds_update_md)
610    auth_proc = proc { creds_update_md }
611    call_creds = GRPC::Core::CallCredentials.new(auth_proc)
612
613    initial_md_key = 'k2'
614    initial_md_val = 'v2'
615    initial_md = { initial_md_key => initial_md_val }
616    expected_md = creds_update_md.clone
617    fail 'bad test param' unless expected_md[initial_md_key].nil?
618    expected_md[initial_md_key] = initial_md_val
619
620    recvd_rpc = nil
621    rcv_thread = Thread.new do
622      recvd_rpc = @server.request_call
623    end
624
625    call = new_client_call
626    call.set_credentials! call_creds
627
628    client_batch = call.run_batch(
629      CallOps::SEND_INITIAL_METADATA => initial_md,
630      CallOps::SEND_CLOSE_FROM_CLIENT => nil)
631    expect(client_batch.send_metadata).to be true
632    expect(client_batch.send_close).to be true
633
634    # confirm the server can receive the client metadata
635    rcv_thread.join
636    expect(recvd_rpc).to_not eq nil
637    recvd_md = recvd_rpc.metadata
638    replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }]
639    expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
640
641    credentials_update_test_finish_call(call, recvd_rpc.call)
642  end
643
644  def credentials_update_test_finish_call(client_call, server_call)
645    final_server_batch = server_call.run_batch(
646      CallOps::RECV_CLOSE_ON_SERVER => nil,
647      CallOps::SEND_INITIAL_METADATA => nil,
648      CallOps::SEND_STATUS_FROM_SERVER => ok_status)
649    expect(final_server_batch.send_close).to be(true)
650    expect(final_server_batch.send_metadata).to be(true)
651    expect(final_server_batch.send_status).to be(true)
652
653    final_client_batch = client_call.run_batch(
654      CallOps::RECV_INITIAL_METADATA => nil,
655      CallOps::RECV_STATUS_ON_CLIENT => nil)
656    expect(final_client_batch.metadata).to eq({})
657    expect(final_client_batch.status.code).to eq(0)
658  end
659
660  it 'modifies metadata with CallCredentials' do
661    credentials_update_test('k1' => 'updated-v1')
662  end
663
664  it 'modifies large metadata with CallCredentials' do
665    val_array = %w(
666      '00000000000000000000000000000000000000000000000000000000000000',
667      '11111111111111111111111111111111111111111111111111111111111111',
668    )
669    md = {
670      k3: val_array,
671      k4: '0000000000000000000000000000000000000000000000000000000000',
672      keeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeey5: 'v1'
673    }
674    credentials_update_test(md)
675  end
676end
677