ably 0.8.15 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/CHANGELOG.md +6 -2
- data/README.md +5 -1
- data/SPEC.md +1473 -852
- data/ably.gemspec +11 -8
- data/lib/ably/auth.rb +90 -53
- data/lib/ably/exceptions.rb +37 -8
- data/lib/ably/logger.rb +10 -1
- data/lib/ably/models/auth_details.rb +42 -0
- data/lib/ably/models/channel_state_change.rb +18 -4
- data/lib/ably/models/connection_details.rb +6 -3
- data/lib/ably/models/connection_state_change.rb +4 -3
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/message.rb +17 -1
- data/lib/ably/models/message_encoders/base.rb +103 -82
- data/lib/ably/models/message_encoders/base64.rb +1 -1
- data/lib/ably/models/presence_message.rb +16 -1
- data/lib/ably/models/protocol_message.rb +20 -3
- data/lib/ably/models/token_details.rb +11 -1
- data/lib/ably/models/token_request.rb +16 -6
- data/lib/ably/modules/async_wrapper.rb +7 -3
- data/lib/ably/modules/encodeable.rb +51 -12
- data/lib/ably/modules/enum.rb +17 -7
- data/lib/ably/modules/event_emitter.rb +29 -14
- data/lib/ably/modules/model_common.rb +13 -21
- data/lib/ably/modules/state_emitter.rb +7 -4
- data/lib/ably/modules/state_machine.rb +2 -4
- data/lib/ably/modules/uses_state_machine.rb +7 -3
- data/lib/ably/realtime.rb +2 -0
- data/lib/ably/realtime/auth.rb +102 -42
- data/lib/ably/realtime/channel.rb +68 -26
- data/lib/ably/realtime/channel/channel_manager.rb +154 -65
- data/lib/ably/realtime/channel/channel_state_machine.rb +14 -15
- data/lib/ably/realtime/client.rb +18 -3
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +38 -29
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -1
- data/lib/ably/realtime/connection.rb +108 -49
- data/lib/ably/realtime/connection/connection_manager.rb +167 -61
- data/lib/ably/realtime/connection/connection_state_machine.rb +22 -3
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -10
- data/lib/ably/realtime/presence.rb +70 -45
- data/lib/ably/realtime/presence/members_map.rb +201 -36
- data/lib/ably/realtime/presence/presence_manager.rb +30 -6
- data/lib/ably/realtime/presence/presence_state_machine.rb +5 -12
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +5 -5
- data/lib/ably/rest/client.rb +31 -27
- data/lib/ably/rest/middleware/exceptions.rb +1 -3
- data/lib/ably/rest/middleware/logger.rb +2 -2
- data/lib/ably/rest/presence.rb +2 -2
- data/lib/ably/util/pub_sub.rb +1 -1
- data/lib/ably/util/safe_deferrable.rb +26 -0
- data/lib/ably/version.rb +2 -2
- data/spec/acceptance/realtime/auth_spec.rb +470 -111
- data/spec/acceptance/realtime/channel_history_spec.rb +5 -3
- data/spec/acceptance/realtime/channel_spec.rb +1017 -168
- data/spec/acceptance/realtime/client_spec.rb +6 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +458 -27
- data/spec/acceptance/realtime/connection_spec.rb +424 -105
- data/spec/acceptance/realtime/message_spec.rb +52 -23
- data/spec/acceptance/realtime/presence_history_spec.rb +5 -3
- data/spec/acceptance/realtime/presence_spec.rb +1110 -96
- data/spec/acceptance/rest/auth_spec.rb +222 -59
- data/spec/acceptance/rest/base_spec.rb +1 -1
- data/spec/acceptance/rest/channel_spec.rb +1 -2
- data/spec/acceptance/rest/client_spec.rb +104 -48
- data/spec/acceptance/rest/message_spec.rb +42 -15
- data/spec/acceptance/rest/presence_spec.rb +4 -11
- data/spec/rspec_config.rb +2 -1
- data/spec/shared/client_initializer_behaviour.rb +2 -2
- data/spec/shared/safe_deferrable_behaviour.rb +6 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/debug_failure_helper.rb +20 -4
- data/spec/support/event_machine_helper.rb +32 -1
- data/spec/unit/auth_spec.rb +4 -11
- data/spec/unit/logger_spec.rb +28 -2
- data/spec/unit/models/auth_details_spec.rb +49 -0
- data/spec/unit/models/channel_state_change_spec.rb +23 -3
- data/spec/unit/models/connection_details_spec.rb +12 -1
- data/spec/unit/models/connection_state_change_spec.rb +15 -4
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -1
- data/spec/unit/models/message_spec.rb +153 -0
- data/spec/unit/models/presence_message_spec.rb +192 -0
- data/spec/unit/models/protocol_message_spec.rb +64 -6
- data/spec/unit/models/token_details_spec.rb +75 -0
- data/spec/unit/models/token_request_spec.rb +74 -0
- data/spec/unit/modules/async_wrapper_spec.rb +2 -1
- data/spec/unit/modules/enum_spec.rb +69 -0
- data/spec/unit/modules/event_emitter_spec.rb +149 -22
- data/spec/unit/modules/state_emitter_spec.rb +9 -3
- data/spec/unit/realtime/client_spec.rb +1 -1
- data/spec/unit/realtime/connection_spec.rb +8 -5
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +1 -1
- data/spec/unit/realtime/presence_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +1 -1
- data/spec/unit/util/crypto_spec.rb +3 -3
- metadata +22 -19
@@ -6,7 +6,7 @@ describe Ably::Models::ConnectionStateChange do
|
|
6
6
|
|
7
7
|
subject { Ably::Models::ConnectionStateChange }
|
8
8
|
|
9
|
-
context '#current' do
|
9
|
+
context '#current (#TA2)' do
|
10
10
|
it 'is required' do
|
11
11
|
expect { subject.new(previous: true) }.to raise_error ArgumentError
|
12
12
|
end
|
@@ -16,7 +16,7 @@ describe Ably::Models::ConnectionStateChange do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
context '#previous' do
|
19
|
+
context '#previous(#TA2)' do
|
20
20
|
it 'is required' do
|
21
21
|
expect { subject.new(current: true) }.to raise_error ArgumentError
|
22
22
|
end
|
@@ -26,7 +26,18 @@ describe Ably::Models::ConnectionStateChange do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
context '#
|
29
|
+
context '#event(#TA5)' do
|
30
|
+
it 'is not required' do
|
31
|
+
expect { subject.new(previous: true, current: true) }.to_not raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'is an attribute' do
|
35
|
+
expect(subject.new(event: unique, current: true, previous: true).event).to eql(unique)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
context '#retry_in (#TA2)' do
|
30
41
|
it 'is not required' do
|
31
42
|
expect { subject.new(previous: true, current: true) }.to_not raise_error
|
32
43
|
end
|
@@ -36,7 +47,7 @@ describe Ably::Models::ConnectionStateChange do
|
|
36
47
|
end
|
37
48
|
end
|
38
49
|
|
39
|
-
context '#reason' do
|
50
|
+
context '#reason (#TA3)' do
|
40
51
|
it 'is not required' do
|
41
52
|
expect { subject.new(previous: true, current: true) }.to_not raise_error
|
42
53
|
end
|
@@ -57,8 +57,9 @@ describe Ably::Models::MessageEncoders::Base64 do
|
|
57
57
|
|
58
58
|
context '#encode' do
|
59
59
|
context 'over binary transport' do
|
60
|
+
subject { Ably::Models::MessageEncoders::Base64.new(client, binary_protocol: true) }
|
61
|
+
|
60
62
|
before do
|
61
|
-
allow(client).to receive(:protocol_binary?).and_return(true)
|
62
63
|
subject.encode message, {}
|
63
64
|
end
|
64
65
|
|
@@ -25,6 +25,38 @@ describe Ably::Models::Message do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
context '#extras (#TM2i)' do
|
29
|
+
let(:model) { subject.new({ extras: extras }, protocol_message: protocol_message) }
|
30
|
+
|
31
|
+
context 'when missing' do
|
32
|
+
let(:model) { subject.new({}, protocol_message: protocol_message) }
|
33
|
+
it 'is nil' do
|
34
|
+
expect(model.extras).to be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when a string' do
|
39
|
+
let(:extras) { 'string' }
|
40
|
+
it 'raises an exception' do
|
41
|
+
expect { model.extras }.to raise_error ArgumentError, /extras contains an unsupported type/
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when a Hash' do
|
46
|
+
let(:extras) { { key: 'value' } }
|
47
|
+
it 'contains a Hash Json object' do
|
48
|
+
expect(model.extras).to eq(extras)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when a Json Array' do
|
53
|
+
let(:extras) { [{ 'key' => 'value' }] }
|
54
|
+
it 'contains a Json Array object' do
|
55
|
+
expect(model.extras).to eq(extras)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
28
60
|
context '#connection_id attribute' do
|
29
61
|
let(:protocol_connection_id) { random_str }
|
30
62
|
let(:protocol_message) { Ably::Models::ProtocolMessage.new('connectionId' => protocol_connection_id, action: 1, timestamp: protocol_message_timestamp) }
|
@@ -379,4 +411,125 @@ describe Ably::Models::Message do
|
|
379
411
|
end
|
380
412
|
end
|
381
413
|
end
|
414
|
+
|
415
|
+
context '#from_encoded (#TM3)' do
|
416
|
+
context 'with no encoding' do
|
417
|
+
let(:message_data) do
|
418
|
+
{ name: 'name', data: 'data-string' }
|
419
|
+
end
|
420
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
421
|
+
|
422
|
+
it 'returns a message object' do
|
423
|
+
expect(from_encoded).to be_a(Ably::Models::Message)
|
424
|
+
expect(from_encoded.name).to eql('name')
|
425
|
+
expect(from_encoded.data).to eql('data-string')
|
426
|
+
expect(from_encoded.encoding).to be_nil
|
427
|
+
end
|
428
|
+
|
429
|
+
context 'with a block' do
|
430
|
+
it 'does not call the block' do
|
431
|
+
block_called = false
|
432
|
+
subject.from_encoded(message_data) do |exception, message|
|
433
|
+
block_called = true
|
434
|
+
end
|
435
|
+
expect(block_called).to be_falsey
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
context 'with an encoding' do
|
441
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
442
|
+
let(:message_data) do
|
443
|
+
{ name: 'name', data: JSON.dump(hash_data), encoding: 'json' }
|
444
|
+
end
|
445
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
446
|
+
|
447
|
+
it 'returns a message object' do
|
448
|
+
expect(from_encoded).to be_a(Ably::Models::Message)
|
449
|
+
expect(from_encoded.name).to eql('name')
|
450
|
+
expect(from_encoded.data).to eql(hash_data)
|
451
|
+
expect(from_encoded.encoding).to be_nil
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
context 'with a custom encoding' do
|
456
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
457
|
+
let(:message_data) do
|
458
|
+
{ name: 'name', data: JSON.dump(hash_data), encoding: 'foo/json' }
|
459
|
+
end
|
460
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
461
|
+
|
462
|
+
it 'returns a message object with the residual incompatible transforms left in the encoding property' do
|
463
|
+
expect(from_encoded).to be_a(Ably::Models::Message)
|
464
|
+
expect(from_encoded.name).to eql('name')
|
465
|
+
expect(from_encoded.data).to eql(hash_data)
|
466
|
+
expect(from_encoded.encoding).to eql('foo')
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context 'with a Cipher encoding' do
|
471
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
472
|
+
let(:cipher_params) { { key: Ably::Util::Crypto.generate_random_key(128), algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
473
|
+
let(:crypto) { Ably::Util::Crypto.new(cipher_params) }
|
474
|
+
let(:payload) { random_str }
|
475
|
+
let(:message_data) do
|
476
|
+
{ name: 'name', data: crypto.encrypt(payload), encoding: 'utf-8/cipher+aes-128-cbc' }
|
477
|
+
end
|
478
|
+
let(:channel_options) { { cipher: cipher_params } }
|
479
|
+
let(:from_encoded) { subject.from_encoded(message_data, channel_options) }
|
480
|
+
|
481
|
+
it 'returns a message object with the residual incompatible transforms left in the encoding property' do
|
482
|
+
expect(from_encoded).to be_a(Ably::Models::Message)
|
483
|
+
expect(from_encoded.name).to eql('name')
|
484
|
+
expect(from_encoded.data).to eql(payload)
|
485
|
+
expect(from_encoded.encoding).to be_nil
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
context 'with invalid Cipher encoding' do
|
490
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
491
|
+
let(:cipher_params) { { key: Ably::Util::Crypto.generate_random_key(128), algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
492
|
+
let(:unencryped_payload) { random_str }
|
493
|
+
let(:message_data) do
|
494
|
+
{ name: 'name', data: unencryped_payload, encoding: 'utf-8/cipher+aes-128-cbc' }
|
495
|
+
end
|
496
|
+
let(:channel_options) { { cipher: cipher_params } }
|
497
|
+
|
498
|
+
context 'without a block' do
|
499
|
+
it 'raises an exception' do
|
500
|
+
expect { subject.from_encoded(message_data, channel_options) }.to raise_exception(Ably::Exceptions::CipherError)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
context 'with a block' do
|
505
|
+
it 'calls the block with the exception' do
|
506
|
+
block_called = false
|
507
|
+
subject.from_encoded(message_data, channel_options) do |exception, message|
|
508
|
+
expect(exception).to be_a(Ably::Exceptions::CipherError)
|
509
|
+
block_called = true
|
510
|
+
end
|
511
|
+
expect(block_called).to be_truthy
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
context '#from_encoded_array (#TM3)' do
|
518
|
+
context 'with no encoding' do
|
519
|
+
let(:message_data) do
|
520
|
+
[{ name: 'name1', data: 'data-string' }, { name: 'name2', data: 'data-string' }]
|
521
|
+
end
|
522
|
+
let(:from_encoded) { subject.from_encoded_array(message_data) }
|
523
|
+
|
524
|
+
it 'returns an Array of message objects' do
|
525
|
+
first = from_encoded.first
|
526
|
+
expect(first).to be_a(Ably::Models::Message)
|
527
|
+
expect(first.name).to eql('name1')
|
528
|
+
expect(first.data).to eql('data-string')
|
529
|
+
expect(first.encoding).to be_nil
|
530
|
+
last = from_encoded.last
|
531
|
+
expect(last.name).to eql('name2')
|
532
|
+
end
|
533
|
+
end
|
534
|
+
end
|
382
535
|
end
|
@@ -383,4 +383,196 @@ describe Ably::Models::PresenceMessage do
|
|
383
383
|
end
|
384
384
|
end
|
385
385
|
end
|
386
|
+
|
387
|
+
|
388
|
+
context '#from_encoded (#TP4)' do
|
389
|
+
context 'with no encoding' do
|
390
|
+
let(:message_data) do
|
391
|
+
{ action: 2, data: 'data-string' }
|
392
|
+
end
|
393
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
394
|
+
|
395
|
+
it 'returns a presence message object' do
|
396
|
+
expect(from_encoded).to be_a(Ably::Models::PresenceMessage)
|
397
|
+
expect(from_encoded.action).to eq(:enter)
|
398
|
+
expect(from_encoded.data).to eql('data-string')
|
399
|
+
expect(from_encoded.encoding).to be_nil
|
400
|
+
end
|
401
|
+
|
402
|
+
context 'with a block' do
|
403
|
+
it 'does not call the block' do
|
404
|
+
block_called = false
|
405
|
+
subject.from_encoded(message_data) do |exception, message|
|
406
|
+
block_called = true
|
407
|
+
end
|
408
|
+
expect(block_called).to be_falsey
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
context 'with an encoding' do
|
414
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
415
|
+
let(:message_data) do
|
416
|
+
{ action: 'leave', data: JSON.dump(hash_data), encoding: 'json' }
|
417
|
+
end
|
418
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
419
|
+
|
420
|
+
it 'returns a presence message object' do
|
421
|
+
expect(from_encoded).to be_a(Ably::Models::PresenceMessage)
|
422
|
+
expect(from_encoded.action).to eq(:leave)
|
423
|
+
expect(from_encoded.data).to eql(hash_data)
|
424
|
+
expect(from_encoded.encoding).to be_nil
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'with a custom encoding' do
|
429
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
430
|
+
let(:message_data) do
|
431
|
+
{ action: 1, data: JSON.dump(hash_data), encoding: 'foo/json' }
|
432
|
+
end
|
433
|
+
let(:from_encoded) { subject.from_encoded(message_data) }
|
434
|
+
|
435
|
+
it 'returns a presence message object with the residual incompatible transforms left in the encoding property' do
|
436
|
+
expect(from_encoded).to be_a(Ably::Models::PresenceMessage)
|
437
|
+
expect(from_encoded.action).to eq(1)
|
438
|
+
expect(from_encoded.data).to eql(hash_data)
|
439
|
+
expect(from_encoded.encoding).to eql('foo')
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
context 'with a Cipher encoding' do
|
444
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
445
|
+
let(:cipher_params) { { key: Ably::Util::Crypto.generate_random_key(128), algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
446
|
+
let(:crypto) { Ably::Util::Crypto.new(cipher_params) }
|
447
|
+
let(:payload) { random_str }
|
448
|
+
let(:message_data) do
|
449
|
+
{ action: 1, data: crypto.encrypt(payload), encoding: 'utf-8/cipher+aes-128-cbc' }
|
450
|
+
end
|
451
|
+
let(:channel_options) { { cipher: cipher_params } }
|
452
|
+
let(:from_encoded) { subject.from_encoded(message_data, channel_options) }
|
453
|
+
|
454
|
+
it 'returns a presence message object with the residual incompatible transforms left in the encoding property' do
|
455
|
+
expect(from_encoded).to be_a(Ably::Models::PresenceMessage)
|
456
|
+
expect(from_encoded.data).to eql(payload)
|
457
|
+
expect(from_encoded.encoding).to be_nil
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
context 'with invalid Cipher encoding' do
|
462
|
+
let(:hash_data) { { 'key' => 'value', 'key2' => 123 } }
|
463
|
+
let(:cipher_params) { { key: Ably::Util::Crypto.generate_random_key(128), algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
464
|
+
let(:unencryped_payload) { random_str }
|
465
|
+
let(:message_data) do
|
466
|
+
{ action: 1, data: unencryped_payload, encoding: 'utf-8/cipher+aes-128-cbc' }
|
467
|
+
end
|
468
|
+
let(:channel_options) { { cipher: cipher_params } }
|
469
|
+
|
470
|
+
context 'without a block' do
|
471
|
+
it 'raises an exception' do
|
472
|
+
expect { subject.from_encoded(message_data, channel_options) }.to raise_exception(Ably::Exceptions::CipherError)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
context 'with a block' do
|
477
|
+
it 'calls the block with the exception' do
|
478
|
+
block_called = false
|
479
|
+
subject.from_encoded(message_data, channel_options) do |exception, message|
|
480
|
+
expect(exception).to be_a(Ably::Exceptions::CipherError)
|
481
|
+
block_called = true
|
482
|
+
end
|
483
|
+
expect(block_called).to be_truthy
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
context '#from_encoded_array (#TP4)' do
|
490
|
+
context 'with no encoding' do
|
491
|
+
let(:message_data) do
|
492
|
+
[{ action: 1, data: 'data-string' }, { action: 2, data: 'data-string' }]
|
493
|
+
end
|
494
|
+
let(:from_encoded) { subject.from_encoded_array(message_data) }
|
495
|
+
|
496
|
+
it 'returns an Array of presence message objects' do
|
497
|
+
first = from_encoded.first
|
498
|
+
expect(first).to be_a(Ably::Models::PresenceMessage)
|
499
|
+
expect(first.action).to eq(1)
|
500
|
+
expect(first.data).to eql('data-string')
|
501
|
+
expect(first.encoding).to be_nil
|
502
|
+
last = from_encoded.last
|
503
|
+
expect(last.action).to eq(:enter)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
context '#shallow_clone' do
|
509
|
+
context 'with inherited attributes from ProtocolMessage' do
|
510
|
+
let(:protocol_message) {
|
511
|
+
Ably::Models::ProtocolMessage.new('id' => 'fooId', 'connectionId' => protocol_connection_id, 'action' => 1, 'timestamp' => protocol_message_timestamp)
|
512
|
+
}
|
513
|
+
let(:protocol_connection_id) { random_str }
|
514
|
+
let(:model) { subject.new({ 'action' => 2 }, protocol_message: protocol_message) }
|
515
|
+
|
516
|
+
it 'creates a duplicate of the message without any ProtocolMessage dependency' do
|
517
|
+
clone = model.shallow_clone
|
518
|
+
expect(clone.id).to match(/fooId/)
|
519
|
+
expect(clone.connection_id).to eql(protocol_connection_id)
|
520
|
+
expect(as_since_epoch(clone.timestamp)).to eq(protocol_message_timestamp)
|
521
|
+
expect(clone.action).to eq(2)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
context 'with embedded attributes for all fields' do
|
526
|
+
let(:message_timestamp) { as_since_epoch(Time.now) + 100 }
|
527
|
+
let(:connection_id) { random_str }
|
528
|
+
let(:model) { subject.new({ 'action' => 3, 'id' => 'fooId', 'connectionId' => connection_id, 'timestamp' => message_timestamp }) }
|
529
|
+
|
530
|
+
it 'creates a duplicate of the message without any ProtocolMessage dependency' do
|
531
|
+
clone = model.shallow_clone
|
532
|
+
expect(clone.id).to eql('fooId')
|
533
|
+
expect(clone.connection_id).to eql(connection_id)
|
534
|
+
expect(as_since_epoch(clone.timestamp)).to eq(message_timestamp)
|
535
|
+
expect(clone.action).to eq(3)
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
context 'with new attributes passed in to the method' do
|
540
|
+
let(:protocol_message) {
|
541
|
+
Ably::Models::ProtocolMessage.new('id' => 'fooId', 'connectionId' => protocol_connection_id, 'action' => 1, 'timestamp' => protocol_message_timestamp)
|
542
|
+
}
|
543
|
+
let(:protocol_connection_id) { random_str }
|
544
|
+
let(:model) { subject.new({ 'action' => 2 }, protocol_message: protocol_message) }
|
545
|
+
|
546
|
+
it 'creates a duplicate of the message without any ProtocolMessage dependency' do
|
547
|
+
clone = model.shallow_clone(id: 'newId', action: 1, timestamp: protocol_message_timestamp + 1000)
|
548
|
+
expect(clone.id).to match(/newId/)
|
549
|
+
expect(clone.connection_id).to eql(protocol_connection_id)
|
550
|
+
expect(as_since_epoch(clone.timestamp)).to eq(protocol_message_timestamp + 1000)
|
551
|
+
expect(clone.action).to eq(1)
|
552
|
+
end
|
553
|
+
|
554
|
+
context 'with an invalid ProtocolMessage (missing an ID)' do
|
555
|
+
let(:protocol_message) {
|
556
|
+
Ably::Models::ProtocolMessage.new('connectionId' => protocol_connection_id, 'action' => 1, 'timestamp' => protocol_message_timestamp)
|
557
|
+
}
|
558
|
+
it 'allows an ID to be passed in to the shallow clone that takes precedence' do
|
559
|
+
clone = model.shallow_clone(id: 'newId', action: 1, timestamp: protocol_message_timestamp + 1000)
|
560
|
+
expect(clone.id).to match(/newId/)
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
context 'with mixing of cases' do
|
565
|
+
it 'resolves case issues and can use camelCase or snake_case' do
|
566
|
+
clone = model.shallow_clone(connectionId: 'camelCaseSym')
|
567
|
+
expect(clone.connection_id).to match(/camelCaseSym/)
|
568
|
+
|
569
|
+
clone = model.shallow_clone('connectionId' => 'camelCaseStr')
|
570
|
+
expect(clone.connection_id).to match(/camelCaseStr/)
|
571
|
+
|
572
|
+
clone = model.shallow_clone(connection_id: 'snake_case_sym')
|
573
|
+
expect(clone.connection_id).to match(/snake_case_sym/)
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
386
578
|
end
|
@@ -10,6 +10,7 @@ describe Ably::Models::ProtocolMessage do
|
|
10
10
|
subject.new({ action: 1 }.merge(options))
|
11
11
|
end
|
12
12
|
|
13
|
+
# TR4n, TR4b, TR4c, TR4d, TR4e
|
13
14
|
it_behaves_like 'a model',
|
14
15
|
with_simple_attributes: %w(id channel channel_serial connection_id connection_key),
|
15
16
|
base_model_options: { action: 1 } do
|
@@ -134,7 +135,7 @@ describe Ably::Models::ProtocolMessage do
|
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
137
|
-
context '#flags' do
|
138
|
+
context '#flags (#TR4i)' do
|
138
139
|
context 'when nil' do
|
139
140
|
let(:protocol_message) { new_protocol_message({}) }
|
140
141
|
|
@@ -151,12 +152,40 @@ describe Ably::Models::ProtocolMessage do
|
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
154
|
-
context 'when
|
155
|
+
context 'when presence flag present' do
|
155
156
|
let(:protocol_message) { new_protocol_message(flags: 1) }
|
156
157
|
|
157
158
|
it '#has_presence_flag? is true' do
|
158
159
|
expect(protocol_message.has_presence_flag?).to be_truthy
|
159
160
|
end
|
161
|
+
|
162
|
+
it '#has_channel_resumed_flag? is false' do
|
163
|
+
expect(protocol_message.has_channel_resumed_flag?).to be_falsey
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'when channel resumed flag present' do
|
168
|
+
let(:protocol_message) { new_protocol_message(flags: 4) }
|
169
|
+
|
170
|
+
it '#has_channel_resumed_flag? is true' do
|
171
|
+
expect(protocol_message.has_channel_resumed_flag?).to be_truthy
|
172
|
+
end
|
173
|
+
|
174
|
+
it '#has_presence_flag? is false' do
|
175
|
+
expect(protocol_message.has_presence_flag?).to be_falsey
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when channel resumed and presence flags present' do
|
180
|
+
let(:protocol_message) { new_protocol_message(flags: 5) }
|
181
|
+
|
182
|
+
it '#has_channel_resumed_flag? is true' do
|
183
|
+
expect(protocol_message.has_channel_resumed_flag?).to be_truthy
|
184
|
+
end
|
185
|
+
|
186
|
+
it '#has_presence_flag? is true' do
|
187
|
+
expect(protocol_message.has_presence_flag?).to be_truthy
|
188
|
+
end
|
160
189
|
end
|
161
190
|
|
162
191
|
context 'when has another future flag' do
|
@@ -165,6 +194,10 @@ describe Ably::Models::ProtocolMessage do
|
|
165
194
|
it '#has_presence_flag? is false' do
|
166
195
|
expect(protocol_message.has_presence_flag?).to be_falsey
|
167
196
|
end
|
197
|
+
|
198
|
+
it '#has_backlog_flag? is true' do
|
199
|
+
expect(protocol_message.has_backlog_flag?).to be_truthy
|
200
|
+
end
|
168
201
|
end
|
169
202
|
end
|
170
203
|
|
@@ -265,7 +298,7 @@ describe Ably::Models::ProtocolMessage do
|
|
265
298
|
end
|
266
299
|
end
|
267
300
|
|
268
|
-
context '#messages' do
|
301
|
+
context '#messages (#TR4k)' do
|
269
302
|
let(:protocol_message) { new_protocol_message(messages: [{ name: 'test' }]) }
|
270
303
|
|
271
304
|
it 'contains Message objects' do
|
@@ -275,7 +308,7 @@ describe Ably::Models::ProtocolMessage do
|
|
275
308
|
end
|
276
309
|
end
|
277
310
|
|
278
|
-
context '#presence' do
|
311
|
+
context '#presence (#TR4l)' do
|
279
312
|
let(:protocol_message) { new_protocol_message(presence: [{ action: 1, data: 'test' }]) }
|
280
313
|
|
281
314
|
it 'contains PresenceMessage objects' do
|
@@ -285,7 +318,7 @@ describe Ably::Models::ProtocolMessage do
|
|
285
318
|
end
|
286
319
|
end
|
287
320
|
|
288
|
-
context '#connection_details' do
|
321
|
+
context '#connection_details (#TR4o)' do
|
289
322
|
let(:connection_details) { protocol_message.connection_details }
|
290
323
|
|
291
324
|
context 'with a JSON value' do
|
@@ -312,7 +345,32 @@ describe Ably::Models::ProtocolMessage do
|
|
312
345
|
end
|
313
346
|
end
|
314
347
|
|
315
|
-
context '#
|
348
|
+
context '#auth (#TR4p)' do
|
349
|
+
let(:auth) { protocol_message.auth }
|
350
|
+
|
351
|
+
context 'with a JSON value' do
|
352
|
+
let(:protocol_message) { new_protocol_message(auth: { accesstoken: 'foo' }) }
|
353
|
+
|
354
|
+
it 'contains a AuthDetails object' do
|
355
|
+
expect(auth).to be_a(Ably::Models::AuthDetails)
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'contains the attributes from the JSON auth details' do
|
359
|
+
expect(auth.access_token).to eql('foo')
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context 'without a JSON value' do
|
364
|
+
let(:protocol_message) { new_protocol_message({}) }
|
365
|
+
|
366
|
+
it 'contains an empty AuthDetails object' do
|
367
|
+
expect(auth).to be_a(Ably::Models::AuthDetails)
|
368
|
+
expect(auth.access_token).to eql(nil)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
context '#connection_key (#TR4e)' do
|
316
374
|
context 'existing only in #connection_details.connection_key' do
|
317
375
|
let(:protocol_message) { new_protocol_message(connectionDetails: { connectionKey: 'key' }) }
|
318
376
|
|