diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..4e1e0d2 --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--color diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..a9a18fd --- /dev/null +++ b/.simplecov @@ -0,0 +1,3 @@ +SimpleCov.start do + add_group 'lib', 'lib' +end diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..20e09ea --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +sudo: false +language: ruby +rvm: + - 2.3.3 +before_install: gem install bundler -v 1.16.1 diff --git a/Gemfile b/Gemfile index 2492017..4b9a750 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,8 @@ -source "http://rubygems.org" +source "https://rubygems.org" +git_source(:github) {|repo_name| "https://github.com/opensocket" } + +# Specify your gem's dependencies in socketclusterclient.gemspec gemspec gem 'websocket-eventmachine-client' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..9643d0e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,54 @@ +PATH + remote: . + specs: + socketclusterclient (1.0.0) + websocket-eventmachine-client (~> 1.2) + +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.3) + docile (1.3.1) + eventmachine (1.2.7) + json (2.1.0) + rake (10.5.0) + rspec (3.7.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-core (3.7.1) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.7.0) + rspec-support (3.7.1) + simplecov (0.16.1) + docile (~> 1.1) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) + websocket (1.2.8) + websocket-eventmachine-base (1.2.0) + eventmachine (~> 1.0) + websocket (~> 1.0) + websocket-native (~> 1.0) + websocket-eventmachine-client (1.2.0) + websocket-eventmachine-base (~> 1.0) + websocket-native (1.0.0) + +PLATFORMS + ruby + +DEPENDENCIES + bundler (~> 1.16) + rake (~> 10.0) + rspec (~> 3.0) + simplecov + socketclusterclient! + websocket-eventmachine-client + +BUNDLED WITH + 1.16.1 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..478e256 --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "socketclusterclient" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/lib/socketclusterclient.rb b/lib/socketclusterclient.rb index 2430db6..5feb307 100644 --- a/lib/socketclusterclient.rb +++ b/lib/socketclusterclient.rb @@ -1,3 +1,5 @@ +require 'socketclusterclient/version' + require_relative './sc_client' # @@ -5,5 +7,5 @@ # # @author Maanav Shah # -module SocketclusterClient +module Socketclusterclient end diff --git a/lib/socketclusterclient/version.rb b/lib/socketclusterclient/version.rb index ff08a80..279a8cb 100644 --- a/lib/socketclusterclient/version.rb +++ b/lib/socketclusterclient/version.rb @@ -1,3 +1,3 @@ -module SocketClusterClient +module Socketclusterclient VERSION = '1.0.0'.freeze end diff --git a/socketclusterclient.gemspec b/socketclusterclient.gemspec index 8654acb..c6ee9b5 100644 --- a/socketclusterclient.gemspec +++ b/socketclusterclient.gemspec @@ -1,23 +1,32 @@ -require 'rake' -$LOAD_PATH.push File.expand_path('lib', __dir__) +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'socketclusterclient/version' -Gem::Specification.new do |s| - s.name = 'socketclusterclient' - s.version = SocketClusterClient::VERSION - s.platform = Gem::Platform::RUBY - s.licenses = ['MIT'] - s.summary = 'Ruby client for socketcluster' - s.homepage = 'https://socketcluster.io/' - s.description = 'A socketcluster client designed in ruby' - s.email = %w[shahmaanav07@gmail.com piyushwww13@gmail.com sachinshinde7676@gmail.com] - s.authors = ['Maanav Shah', 'Piyush Wani', 'Sachin Shinde'] +Gem::Specification.new do |spec| + spec.name = 'socketclusterclient' + spec.version = Socketclusterclient::VERSION + spec.platform = Gem::Platform::RUBY + spec.licenses = ['MIT'] + spec.summary = 'Ruby client for socketcluster' + spec.homepage = 'https://socketcluster.io/' + spec.description = 'A socketcluster client designed in ruby' + spec.email = %w[shahmaanav07@gmail.com piyushwww13@gmail.com sachinshinde7676@gmail.com] + spec.authors = ['Maanav Shah', 'Piyush Wani', 'Sachin Shinde'] - s.files = Rake::FileList['lib/**/*.rb'] - s.require_paths = ['lib'] + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] - s.required_ruby_version = '>= 2.2.0' + spec.required_ruby_version = '>= 2.2.0' + + spec.add_development_dependency "bundler", "~> 1.16" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "simplecov" + spec.add_dependency('websocket-eventmachine-client', '~> 1.2') - s.add_dependency('websocket-eventmachine-client', '~> 1.2') end diff --git a/spec/lib/data_models_spec.rb b/spec/lib/data_models_spec.rb new file mode 100644 index 0000000..90ba847 --- /dev/null +++ b/spec/lib/data_models_spec.rb @@ -0,0 +1,164 @@ +require 'spec_helper' + +RSpec.describe DataModels do + include DataModels + + describe 'data_models' do + context 'get_ack_object' do + let(:error) { 'error' } + let(:data) { { key: 'value' } } + let(:cid) { [*1..6].sample } + + before(:each) do + @ack_object = get_ack_object(error, data, cid) + end + + it 'should return an ack object of type hash' do + expect(@ack_object.class).to eq(Hash) + end + + it 'should return an error, data and cid in ack object' do + expect(@ack_object.keys.include?(:error)).to be(true) + expect(@ack_object[:error]).to eq(error) + expect(@ack_object.keys.include?(:data)).to be(true) + expect(@ack_object[:data]).to eq(data) + expect(@ack_object.keys.include?(:cid)).to be(true) + expect(@ack_object[:cid]).to eq(cid) + end + end + + context 'get_emit_object' do + let(:event) { 'event' } + let(:data) { { key: 'value' } } + + before(:each) do + @emit_object = get_emit_object(event, data) + end + + it 'should return an emit object of type hash' do + expect(@emit_object.class).to eq(Hash) + end + + it 'should return an event and data in emit object' do + expect(@emit_object.keys.include?(:event)).to be(true) + expect(@emit_object[:event]).to eq(event) + expect(@emit_object.keys.include?(:data)).to be(true) + expect(@emit_object[:data]).to eq(data) + end + end + + context 'get_emit_ack_object' do + let(:event) { 'event' } + let(:data) { { key: 'value' } } + let(:cid) { [*1..6].sample } + + before(:each) do + @emit_ack_object = get_emit_ack_object(event, data, cid) + end + + it 'should return an emit ack object of type hash' do + expect(@emit_ack_object.class).to eq(Hash) + end + + it 'should return an event, data and cid in emit ack object' do + expect(@emit_ack_object.keys.include?(:event)).to be(true) + expect(@emit_ack_object[:event]).to eq(event) + expect(@emit_ack_object.keys.include?(:data)).to be(true) + expect(@emit_ack_object[:data]).to eq(data) + expect(@emit_ack_object.keys.include?(:cid)).to be(true) + expect(@emit_ack_object[:cid]).to eq(cid) + end + end + + context 'get_handshake_object' do + let(:cid) { [*1..6].sample } + + before(:each) do + @handshake_object = get_handshake_object(cid) + end + + it 'should return a handshake object of type hash' do + expect(@handshake_object.class).to eq(Hash) + end + + it 'should return an event, data and cid in handshake object' do + expect(@handshake_object.keys.include?(:event)).to be(true) + expect(@handshake_object[:event]).to eq('#handshake') + expect(@handshake_object.keys.include?(:data)).to be(true) + expect(@handshake_object[:data].keys.include?(:authToken)).to be(true) + expect(@handshake_object.keys.include?(:cid)).to be(true) + expect(@handshake_object[:cid]).to eq(cid) + end + end + + context 'get_publish_object' do + let(:channel) { 'channel' } + let(:data) { { key: 'value' } } + let(:cid) { [*1..6].sample } + + before(:each) do + @publish_object = get_publish_object(channel, data, cid) + end + + it 'should return a publish object of type hash' do + expect(@publish_object.class).to eq(Hash) + end + + it 'should return an event, channel, data and cid in publish object' do + expect(@publish_object.keys.include?(:event)).to be(true) + expect(@publish_object[:event]).to eq('#publish') + expect(@publish_object.keys.include?(:data)).to be(true) + expect(@publish_object[:data].keys.include?(:channel)).to be(true) + expect(@publish_object[:data].class).to eq(Hash) + expect(@publish_object.keys.include?(:cid)).to be(true) + expect(@publish_object[:cid]).to be(cid) + end + end + + context 'get_subscribe_object' do + let(:channel) { 'channel' } + let(:cid) { [*1..6].sample } + + before(:each) do + @subscribe_object = get_subscribe_object(channel, cid) + end + + it 'should return a subscribe object of type hash' do + expect(@subscribe_object.class).to eq(Hash) + end + + it 'should return an event, data, channel and cid in subscribe object' do + expect(@subscribe_object.keys.include?(:event)).to be(true) + expect(@subscribe_object[:event]).to eq('#subscribe') + expect(@subscribe_object[:data].class).to eq(Hash) + expect(@subscribe_object.keys.include?(:data)).to be(true) + expect(@subscribe_object[:data].keys.include?(:channel)).to be(true) + expect(@subscribe_object[:data][:channel]).to eq(channel) + expect(@subscribe_object.keys.include?(:cid)).to be(true) + expect(@subscribe_object[:cid]).to be(cid) + end + end + + context 'get_unsubscribe_object' do + let(:channel) { 'channel' } + let(:cid) { [*1..6].sample } + + before(:each) do + @unsubscribe_object = get_unsubscribe_object(channel, cid) + end + + it 'should return an unsubscribe object of type hash' do + expect(@unsubscribe_object.class).to eq(Hash) + end + + it 'should return an event, data and cid in unsubscribe object' do + expect(@unsubscribe_object.keys.include?(:event)).to be(true) + expect(@unsubscribe_object[:event]).to eq('#unsubscribe') + expect(@unsubscribe_object.keys.include?(:data)).to be(true) + expect(@unsubscribe_object[:data]).to eq(channel) + expect(@unsubscribe_object.keys.include?(:cid)).to be(true) + expect(@unsubscribe_object[:cid]).to be(cid) + end + end + end +end diff --git a/spec/lib/emitter_spec.rb b/spec/lib/emitter_spec.rb new file mode 100644 index 0000000..0077b16 --- /dev/null +++ b/spec/lib/emitter_spec.rb @@ -0,0 +1,139 @@ +require 'spec_helper' + +RSpec.describe Emitter do + include Emitter + + describe 'emitter' do + context 'initialize_emitter' do + it 'should not have instance variables' do + expect(@events).to eq(nil) + expect(@events_ack).to eq(nil) + end + + it 'should initialize instance variables' do + initialize_emitter + + expect(@events).to_not eq(nil) + expect(@events_ack).to_not eq(nil) + expect(@events.class).to eq(Hash) + expect(@events_ack.class).to eq(Hash) + end + end + + context 'on' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an event with key' do + expect(@events[key]).to eq(nil) + end + + it 'should have an event with key' do + on(key, message) + + expect(@events[key]).to be(message) + end + end + + context 'onchannel' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an event with key' do + expect(@events[key]).to eq(nil) + end + + it 'should have an event with key' do + onchannel(key, message) + + expect(@events[key]).to be(message) + end + end + + context 'onack' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object, ack_block) { ack_block.call(key, object) } } + let(:ack_message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an acknowledgement event with key' do + expect(@events_ack[key]).to eq(nil) + end + + it 'should have an acknowledgement event with key' do + onack(key, ack_message) + + expect(@events_ack[key]).to be(ack_message) + end + end + + context 'execute' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an event with key' do + expect(@events[key]).to eq(nil) + end + + it 'should execute event' do + on(key, message) + + expect(execute(key, 'pong')).to eq('ping pong') + end + end + + context 'haseventack' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an event with key' do + expect(@events_ack[key]).to eq(nil) + end + + it 'should return an acknowledgment event from key' do + onack(key, message) + + expect(haseventack(key)).to eq(message) + end + end + + context 'onack' do + before(:each) do + initialize_emitter + end + + let(:key) { 'ping' } + let(:message) { ->(key, object, ack_block) { ack_block.call(key, object) } } + let(:ack_message) { ->(key, object) { "#{key} #{object}" } } + + it 'should not have an event with key' do + expect(@events_ack[key]).to eq(nil) + end + + it 'should expect an acknowledgment event from key' do + onack(key, message) + + expect(executeack(key, 'pong', ack_message)).to eq('ping pong') + end + end + end +end diff --git a/spec/lib/parser_spec.rb b/spec/lib/parser_spec.rb new file mode 100644 index 0000000..5505ae8 --- /dev/null +++ b/spec/lib/parser_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +RSpec.describe Parser do + CHECK_AUTHENTICATION = 1 + PUBLISH = 2 + REMOVE_AUTHENTICATION = 3 + SET_AUTHENTICATION = 4 + EVENT = 5 + ACKNOWLEDGEMENT = 6 + + describe 'parser' + describe 'parse' + context 'check authentication' do + let(:event) { '' } + let(:response_id) { 1 } + + it 'should check if client is authenticated' do + result = Parser.parse(event, response_id) + expect(result).to eq(CHECK_AUTHENTICATION) + end + end + + context 'publish' do + let(:event) { '#publish' } + let(:response_id) { [*1..6].sample } + + it 'should check if event is publish' do + result = Parser.parse(event, response_id) + expect(result).to eq(PUBLISH) + end + end + + context 'remove authentication token' do + let(:event) { '#removeAuthToken' } + let(:response_id) { [*1..6].sample } + + it 'should remove authentication token' do + result = Parser.parse(event, response_id) + expect(result).to eq(REMOVE_AUTHENTICATION) + end + end + + context 'set authentication' do + let(:event) { '#setAuthToken' } + let(:response_id) { [*1..6].sample } + + it 'should set authentication token' do + result = Parser.parse(event, response_id) + expect(result).to eq(SET_AUTHENTICATION) + end + end + + context 'random event' do + let(:event) { '#event' } + let(:response_id) { [*1..6].sample } + + it 'should check if event is publish' do + result = Parser.parse(event, response_id) + expect(result).to eq(EVENT) + end + end + + context 'acknowledgment' do + let(:event) { '' } + let(:response_id) { 6 } + + it 'should acknowledge the event' do + result = Parser.parse(event, response_id) + expect(result).to eq(ACKNOWLEDGEMENT) + end + end +end diff --git a/spec/lib/sc_client_spec.rb b/spec/lib/sc_client_spec.rb new file mode 100644 index 0000000..d329f1b --- /dev/null +++ b/spec/lib/sc_client_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +RSpec.describe Socketclusterclient do + include Socketclusterclient + + describe 'socketcluster client' do + before(:each) do + @connect_url = 'ws://localhost:8000/socketcluster/' + @socket = ScClient.new(@connect_url) + end + + describe 'initialize' do + context 'instance variables' do + it 'should initialize all instance variables' do + expect(@socket.instance_variable_get(:@id)).to eq('') + expect(@socket.instance_variable_get(:@cnt)).to eq(0) + expect(@socket.instance_variable_get(:@acks)).to eq({}) + expect(@socket.instance_variable_get(:@channels)).to eq([]) + expect(@socket.instance_variable_get(:@enable_reconnection)).to eq(true) + expect(@socket.instance_variable_get(:@delay)).to eq(3) + end + + it 'should assign connect_url to instance variable @url' do + expect(@socket.instance_variable_get(:@url)).to eq(@connect_url) + end + end + end + + describe 'set_basic_listener' do + let(:on_connect) { -> { puts 'on connect got called' } } + let(:on_disconnect) { -> { puts 'on connect got called' } } + let(:on_connect_error) { -> { puts 'on connect got called' } } + + before(:each) do + @socket.set_basic_listener(on_connect, on_disconnect, on_connect_error) + end + + context 'set connect, disconnect and error listeners' do + it 'should have on_connected, on_disconnected and on_connect_error listeners' do + expect(@socket.instance_variable_get(:@on_connected)).to eq(on_connect) + expect(@socket.instance_variable_get(:@on_disconnected)).to eq(on_disconnect) + expect(@socket.instance_variable_get(:@on_connect_error)).to eq(on_connect_error) + end + end + end + + describe 'set_authentication_listener' do + let(:on_set_authentication) { ->(_socket, token) { @socket.set_auth_token(token) } } + let(:on_authentication) { ->(_socket, is_authenticated) { puts "Authenticated is #{is_authenticated}" } } + + before(:each) do + @socket.set_authentication_listener(on_set_authentication, on_authentication) + end + + context 'set on_set_authentication and on_authentication listeners' do + it 'should have on_set_authentication and on_authentication listeners' do + expect(@socket.instance_variable_get(:@on_set_authentication)).to eq(on_set_authentication) + expect(@socket.instance_variable_get(:@on_authentication)).to eq(on_authentication) + end + end + end + + describe 'set_auth_token' do + let(:token) { 1_234_567_890 } + + before(:each) do + @socket.set_auth_token(token) + end + + context 'set authentication token' do + it 'should have authentication token' do + expect(@socket.instance_variable_get(:@auth_token)).to eq(token.to_s) + end + end + end + + describe 'get_subscribed_channels' do + let(:channels) { %w[channel1 channel2] } + + before(:each) do + @socket.instance_variable_set(:@channels, channels) + end + + context 'should provide subscribed channels' do + it 'should provide list of all subscribed channels' do + expect(@socket.get_subscribed_channels).to eq(channels) + end + end + end + + describe 'set_delay' do + let(:delay) { 5 } + + before(:each) do + @socket.set_delay(delay) + end + + context 'should set delay' do + it 'should set delay to assigned value' do + expect(@socket.instance_variable_get(:@delay)).to eq(delay) + end + end + end + + describe 'set_reconnection' do + let(:reconnect) { true } + + before(:each) do + @socket.set_reconnection(reconnect) + end + + context 'should set reconnection strategy' do + it 'should set to enable reconnection to assigned value' do + expect(@socket.instance_variable_get(:@enable_reconnection)).to eq(reconnect) + end + end + end + end +end diff --git a/spec/socketclusterclient_spec.rb b/spec/socketclusterclient_spec.rb new file mode 100644 index 0000000..75ad1d4 --- /dev/null +++ b/spec/socketclusterclient_spec.rb @@ -0,0 +1,5 @@ +RSpec.describe Socketclusterclient do + it 'has a version number' do + expect(Socketclusterclient::VERSION).not_to be nil + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..8145adf --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,14 @@ +require "bundler/setup" +require "socketclusterclient" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end