simple-websocket-vcr 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGES.md +16 -2
- data/README.md +0 -1
- data/lib/simple_websocket_vcr.rb +95 -76
- data/lib/simple_websocket_vcr/cassette.rb +115 -36
- data/lib/simple_websocket_vcr/configuration.rb +14 -11
- data/lib/simple_websocket_vcr/errors.rb +7 -9
- data/lib/simple_websocket_vcr/monkey_patch.rb +18 -0
- data/lib/simple_websocket_vcr/recordable_websocket_client.rb +97 -97
- data/lib/simple_websocket_vcr/version.rb +2 -4
- data/simple-websocket-vcr.gemspec +7 -6
- data/spec/fixtures/vcr_cassettes/EXPLICIT/some_explicitly_specified_json_cassette.json +30 -0
- data/spec/fixtures/vcr_cassettes/EXPLICIT/some_explicitly_specified_yaml_cassette.yml +44 -0
- data/spec/fixtures/vcr_cassettes/EXPLICIT/some_template.json +13 -0
- data/spec/fixtures/vcr_cassettes/EXPLICIT/some_template.yml +25 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/automatically_picked_cassette_name_is_ok,_when_describing_parent_and_example_child1.yml +2 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/automatically_picked_cassette_name_is_ok,_when_describing_parent_and_example_child2.yml +2 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/{automatically_picked_cassette_name_is_ok,_when_describing_parent_and_example_child1.json → automatically_picked_cassette_name_is_ok,_when_describing_parent_and_example_child2_for_json.json} +0 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/automatically_picked_cassette_name_is_ok,_when_using_context_foo_and_example_bar.yml +2 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/should_record_also_the_outgoing_communication.yml +11 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/{should_record_the_closing_event.json → should_record_the_closing_event(json).json} +1 -1
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/should_record_the_closing_event(yaml).yml +6 -0
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/should_record_the_very_first_message_caught_on_the_client_yielded_by_the_connect_method.yml +5 -0
- data/spec/vcr_spec.rb +160 -57
- metadata +62 -51
- data/spec/fixtures/vcr_cassettes/EXPLICIT/some_explicitly_specified_cassette.json +0 -30
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/automatically_picked_cassette_name_is_ok,_when_describing_parent_and_example_child2.json +0 -3
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/automatically_picked_cassette_name_is_ok,_when_using_context_foo_and_example_bar.json +0 -3
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/should_record_also_the_outgoing_communication.json +0 -21
- data/spec/fixtures/vcr_cassettes/VCR_for_WS/should_record_the_very_first_message_caught_on_the_client_yielded_by_the_connect_method.json +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fcc25d2e22bc79b1a5359917af80ec0f10ae45e
|
4
|
+
data.tar.gz: 2736b6895d6da6e42d372e478e92fbeb6bd7d14a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f1e61a02a7c1796460d423406c2d870035f74b761939983292423329ccca8ace7715576b38b45f6b3e2b2ec673bace43bb1045bd12484f54b5159eafbdc11b0
|
7
|
+
data.tar.gz: f1a278cbb9e3dc882113e5a4e83bdf31f2a81af8fb20aaa2f72c17fd69c23d382ff00bf82eb301aae52a4f0eec4bc1c3482f3c9ba2a4339c373a9005607f2b29
|
data/.rubocop.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -3,6 +3,20 @@
|
|
3
3
|
This document describes the relevant changes between releases of the
|
4
4
|
_simple-websocket-vcr_ project.
|
5
5
|
|
6
|
-
###
|
6
|
+
### 0.0.5 (not released yet)
|
7
|
+
* version is aligned with the VCR module so that coverall could do its job properly (https://travis-ci.org/Jiri-Kremser/simple-websocket-vcr/jobs/118142774#L215)
|
7
8
|
|
8
|
-
|
9
|
+
### 0.0.4
|
10
|
+
* if client waits for msg a therad is spawned and it checks the recording for consecutive read operations and translate it to the events, if write operation is on the top of the stack, the thread waits for the client to perform the write
|
11
|
+
|
12
|
+
### 0.0.3
|
13
|
+
* making it more robust
|
14
|
+
|
15
|
+
### 0.0.2
|
16
|
+
* bug fixes
|
17
|
+
* Rubocop
|
18
|
+
* actually, making it work with unseen scenarios
|
19
|
+
|
20
|
+
### 0.0.1
|
21
|
+
|
22
|
+
* First release
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Simple websocket VCR
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/Jiri-Kremser/simple-websocket-vcr.png?branch=master)](https://travis-ci.org/Jiri-Kremser/simple-websocket-vcr)
|
4
|
-
|
5
4
|
[![Coverage](https://coveralls.io/repos/github/Jiri-Kremser/simple-websocket-vcr/badge.svg?branch=master)](https://coveralls.io/github/Jiri-Kremser/simple-websocket-vcr?branch=master)
|
6
5
|
|
7
6
|
|
data/lib/simple_websocket_vcr.rb
CHANGED
@@ -1,103 +1,122 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
require 'websocket-client-simple'
|
1
4
|
require 'simple_websocket_vcr/cassette'
|
2
5
|
require 'simple_websocket_vcr/configuration'
|
3
6
|
require 'simple_websocket_vcr/errors'
|
4
7
|
require 'simple_websocket_vcr/recordable_websocket_client'
|
5
8
|
require 'simple_websocket_vcr/version'
|
6
|
-
require '
|
7
|
-
|
9
|
+
require 'simple_websocket_vcr/monkey_patch'
|
10
|
+
|
11
|
+
module WebSocketVCR
|
12
|
+
extend self
|
13
|
+
|
14
|
+
# @return [String] the current version.
|
15
|
+
# @note This string also has singleton methods:
|
16
|
+
#
|
17
|
+
# * `major` [Integer] The major version.
|
18
|
+
# * `minor` [Integer] The minor version.
|
19
|
+
# * `patch` [Integer] The patch version.
|
20
|
+
# * `parts` [Array<Integer>] List of the version parts.
|
21
|
+
def version
|
22
|
+
@version ||= begin
|
23
|
+
string = WebSocketVCR::VERSION
|
24
|
+
|
25
|
+
def string.parts
|
26
|
+
split('.').map(&:to_i)
|
27
|
+
end
|
8
28
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
yield configuration
|
13
|
-
end
|
29
|
+
def string.major
|
30
|
+
parts[0]
|
31
|
+
end
|
14
32
|
|
15
|
-
|
16
|
-
|
17
|
-
|
33
|
+
def string.minor
|
34
|
+
parts[1]
|
35
|
+
end
|
18
36
|
|
19
|
-
|
20
|
-
|
21
|
-
|
37
|
+
def string.patch
|
38
|
+
parts[2]
|
39
|
+
end
|
22
40
|
|
23
|
-
|
24
|
-
@cassette = v
|
41
|
+
string
|
25
42
|
end
|
43
|
+
end
|
26
44
|
|
27
|
-
|
28
|
-
|
29
|
-
|
45
|
+
def configure
|
46
|
+
yield configuration
|
47
|
+
end
|
30
48
|
|
31
|
-
|
32
|
-
|
33
|
-
|
49
|
+
def configuration
|
50
|
+
@configuration ||= Configuration.new
|
51
|
+
end
|
34
52
|
|
35
|
-
|
36
|
-
|
53
|
+
def cassette
|
54
|
+
@cassette
|
55
|
+
end
|
37
56
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
yield
|
42
|
-
cassette.save
|
43
|
-
self.cassette = nil
|
44
|
-
end
|
57
|
+
def cassette=(v)
|
58
|
+
@cassette = v
|
59
|
+
end
|
45
60
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
use_cassette(name, options, &block)
|
50
|
-
end
|
61
|
+
def disabled
|
62
|
+
@disabled || false
|
63
|
+
end
|
51
64
|
|
52
|
-
|
53
|
-
|
54
|
-
|
65
|
+
def disabled=(v)
|
66
|
+
@disabled = v
|
67
|
+
end
|
55
68
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
69
|
+
def save_session
|
70
|
+
end
|
59
71
|
|
60
|
-
|
61
|
-
|
62
|
-
|
72
|
+
# Use the specified cassette for either recording the real communication or replaying it during the tests.
|
73
|
+
# @param name [String] the cassette
|
74
|
+
# @param options [Hash] options for the cassette
|
75
|
+
# @option options [Symbol] :record if set to :none there will be no recording
|
76
|
+
# @option options [Symbol] :erb a sub-hash with variables used for ERB substitution in given cassette
|
77
|
+
def use_cassette(name, options = {})
|
78
|
+
fail ArgumentError, '`VCR.use_cassette` requires a block.' unless block_given?
|
79
|
+
self.cassette = Cassette.new(name, options)
|
80
|
+
yield
|
81
|
+
cassette.save
|
82
|
+
self.cassette = nil
|
83
|
+
end
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
|
85
|
+
def record(example, context, options = {}, &block)
|
86
|
+
fail ArgumentError, '`VCR.record` requires a block.' unless block_given?
|
87
|
+
name = filename_helper(example, context)
|
88
|
+
use_cassette(name, options, &block)
|
89
|
+
end
|
67
90
|
|
68
|
-
|
91
|
+
def turn_off!(_options = {})
|
92
|
+
# TODO: impl
|
93
|
+
end
|
69
94
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
directory = context.class.metadata[:description].gsub(/\s+/, '_')
|
74
|
-
else
|
75
|
-
example_name = "#{context.class.metadata[:description]}_#{example.description}".gsub(/\s+/, '_')
|
76
|
-
directory = context.class.metadata[:parent_example_group][:description].gsub(/\s+/, '_')
|
77
|
-
end
|
78
|
-
"#{directory}/#{example_name}.json"
|
79
|
-
end
|
95
|
+
def turned_on?
|
96
|
+
!@cassette.nil?
|
97
|
+
end
|
80
98
|
|
81
|
-
|
82
|
-
|
99
|
+
def turn_on!
|
100
|
+
# TODO: impl
|
83
101
|
end
|
84
|
-
end
|
85
102
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
real_connect(url, options)
|
100
|
-
end
|
103
|
+
def live?
|
104
|
+
@cassette && @cassette.recording?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def filename_helper(example, context)
|
110
|
+
if context.class.metadata[:parent_example_group].nil?
|
111
|
+
example_name = example.description.gsub(/\s+/, '_')
|
112
|
+
directory = context.class.metadata[:description].gsub(/\s+/, '_')
|
113
|
+
else
|
114
|
+
example_name = "#{context.class.metadata[:description]}_#{example.description}".gsub(/\s+/, '_')
|
115
|
+
directory = context.class.metadata[:parent_example_group][:description].gsub(/\s+/, '_')
|
101
116
|
end
|
117
|
+
"#{directory}/#{example_name}"
|
102
118
|
end
|
119
|
+
|
120
|
+
module_function :configure, :configuration, :cassette, :cassette=, :disabled, :disabled=, :save_session,
|
121
|
+
:use_cassette, :record, :turn_off!, :turned_on?, :turn_on!, :filename_helper
|
103
122
|
end
|
@@ -1,50 +1,129 @@
|
|
1
1
|
require 'simple_websocket_vcr/errors'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
4
|
+
module WebSocketVCR
|
5
|
+
include Errors
|
6
|
+
|
7
|
+
class Cassette
|
8
|
+
attr_reader :name, :options, :recording, :sessions
|
9
|
+
|
10
|
+
alias_method :recording?, :recording
|
11
|
+
|
12
|
+
def initialize(name, options)
|
13
|
+
@name = name
|
14
|
+
@options = options
|
15
|
+
@using_json = WebSocketVCR.configuration.json_cassettes
|
16
|
+
@name += @using_json ? '.json' : '.yml'
|
17
|
+
|
18
|
+
if File.exist?(filename)
|
19
|
+
@recording = false
|
20
|
+
@sessions = initialize_sessions filename
|
21
|
+
else
|
22
|
+
fail "No cassette '#{name}' found and recording has been turned off" if @options[:record] == :none
|
23
|
+
@recording = true
|
24
|
+
@sessions = []
|
24
25
|
end
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
28
|
+
def next_session
|
29
|
+
if recording?
|
30
|
+
@sessions.push(@using_json ? RecordedJsonSession.new([]) : RecordedYamlSession.new([]))
|
31
|
+
@sessions.last
|
32
|
+
else
|
33
|
+
fail NoMoreSessionsError if @sessions.empty?
|
34
|
+
@sessions.shift
|
34
35
|
end
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
def save
|
39
|
+
return unless recording?
|
40
|
+
dirname = File.dirname(filename)
|
41
|
+
# make sure the directory structure is there
|
42
|
+
FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
|
43
|
+
if @using_json
|
44
|
+
text = JSON.pretty_generate(@sessions.map(&:record_entries))
|
45
|
+
else
|
46
|
+
text = { 'websocket_interactions' => @sessions.map(&:record_entries) }.to_yaml(Indent: 8)
|
41
47
|
end
|
48
|
+
File.open(filename, 'w') { |f| f.write(text) }
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def filename
|
54
|
+
"#{WebSocketVCR.configuration.cassette_library_dir}/#{name}"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
42
58
|
|
43
|
-
|
59
|
+
def initialize_sessions(filename)
|
60
|
+
file_content = File.open(filename, &:read)
|
44
61
|
|
45
|
-
|
46
|
-
|
62
|
+
# do the ERB substitution
|
63
|
+
unless @options[:erb].nil?
|
64
|
+
require 'ostruct'
|
65
|
+
namespace = OpenStruct.new(@options[:erb])
|
66
|
+
file_content = ERB.new(file_content).result(namespace.instance_eval { binding })
|
47
67
|
end
|
68
|
+
|
69
|
+
# parse JSON/YAML
|
70
|
+
if @using_json
|
71
|
+
parsed_content = JSON.parse(file_content)
|
72
|
+
sessions = RecordedJsonSession.load(parsed_content)
|
73
|
+
else
|
74
|
+
parsed_content = YAML.load(file_content)
|
75
|
+
sessions = RecordedYamlSession.load(parsed_content)
|
76
|
+
end
|
77
|
+
sessions
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class RecordedSession
|
82
|
+
attr_reader :record_entries
|
83
|
+
|
84
|
+
def initialize(entries)
|
85
|
+
@record_entries = entries
|
86
|
+
end
|
87
|
+
|
88
|
+
def store(entry)
|
89
|
+
hash = entry.is_a?(RecordEntry) ? entry.attributes.map(&:to_s) : entry.map { |k, v| [k.to_s, v.to_s] }.to_h
|
90
|
+
@record_entries << hash
|
91
|
+
end
|
92
|
+
|
93
|
+
def next
|
94
|
+
RecordEntry.parse(@record_entries.shift)
|
95
|
+
end
|
96
|
+
|
97
|
+
def head
|
98
|
+
@record_entries.empty? ? nil : RecordEntry.parse(@record_entries.first)
|
99
|
+
end
|
100
|
+
|
101
|
+
def method_missing(method_name, *args, &block)
|
102
|
+
@record_entries.__send__(method_name, *args, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class RecordedJsonSession < RecordedSession
|
107
|
+
def self.load(json)
|
108
|
+
json.map { |session| RecordedJsonSession.new(session) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class RecordedYamlSession < RecordedSession
|
113
|
+
def self.load(yaml)
|
114
|
+
yaml['websocket_interactions'].map { |session| RecordedYamlSession.new(session) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class RecordEntry
|
119
|
+
attr_accessor :operation, :type, :data
|
120
|
+
|
121
|
+
def self.parse(obj)
|
122
|
+
record_entry = RecordEntry.new
|
123
|
+
record_entry.operation = obj['operation']
|
124
|
+
record_entry.type = obj['type'] if obj['type']
|
125
|
+
record_entry.data = obj['data'] if obj['data']
|
126
|
+
record_entry
|
48
127
|
end
|
49
128
|
end
|
50
129
|
end
|
@@ -1,16 +1,19 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
attr_accessor :cassette_library_dir, :hook_uris
|
1
|
+
module WebSocketVCR
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :cassette_library_dir, :hook_uris, :json_cassettes
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def initialize
|
6
|
+
reset_defaults!
|
7
|
+
end
|
8
|
+
|
9
|
+
def reset_defaults!
|
10
|
+
@cassette_library_dir = 'spec/fixtures/vcr_cassettes'
|
11
|
+
@hook_uris = []
|
12
|
+
@json_cassettes = false
|
13
|
+
end
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
@hook_uris = []
|
13
|
-
end
|
15
|
+
def method_missing(method_name, *_args, &_block)
|
16
|
+
puts 'unknown method: ' + method_name.to_s
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|