whisperer 0.0.1
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 +15 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +293 -0
- data/Rakefile +1 -0
- data/TODO.md +47 -0
- data/lib/whisperer.rb +102 -0
- data/lib/whisperer/config.rb +40 -0
- data/lib/whisperer/convertors/hash.rb +39 -0
- data/lib/whisperer/convertors/interaction.rb +21 -0
- data/lib/whisperer/dsl.rb +19 -0
- data/lib/whisperer/dsl/base.rb +47 -0
- data/lib/whisperer/dsl/body.rb +33 -0
- data/lib/whisperer/dsl/headers.rb +20 -0
- data/lib/whisperer/dsl/request.rb +15 -0
- data/lib/whisperer/dsl/response.rb +15 -0
- data/lib/whisperer/dsl/response/status.rb +10 -0
- data/lib/whisperer/generator.rb +43 -0
- data/lib/whisperer/helpers.rb +16 -0
- data/lib/whisperer/placeholder.rb +14 -0
- data/lib/whisperer/preprocessors.rb +19 -0
- data/lib/whisperer/preprocessors/base.rb +13 -0
- data/lib/whisperer/preprocessors/content_length.rb +17 -0
- data/lib/whisperer/preprocessors/response_body.rb +23 -0
- data/lib/whisperer/record.rb +48 -0
- data/lib/whisperer/record/body.rb +15 -0
- data/lib/whisperer/record/headers.rb +22 -0
- data/lib/whisperer/record/request.rb +16 -0
- data/lib/whisperer/record/response.rb +18 -0
- data/lib/whisperer/record/response/status.rb +10 -0
- data/lib/whisperer/samples/cassette_builder.rb +24 -0
- data/lib/whisperer/serializers/base.rb +24 -0
- data/lib/whisperer/serializers/json.rb +33 -0
- data/lib/whisperer/serializers/json_multiple.rb +14 -0
- data/lib/whisperer/tasks/whisperer.rake +88 -0
- data/lib/whisperer/version.rb +3 -0
- data/spec/cassette_builders/arya_stark.rb +26 -0
- data/spec/cassette_builders/bran_stark.rb +19 -0
- data/spec/cassette_builders/empty_robb_stark.rb +20 -0
- data/spec/cassette_builders/robb_stark.rb +30 -0
- data/spec/cassette_builders/robb_stark_without_content_length.rb +20 -0
- data/spec/cassette_builders/sansa_stark.rb +9 -0
- data/spec/cassette_builders/starks.rb +31 -0
- data/spec/cassette_builders/wolfs.rb +7 -0
- data/spec/cassettes/empty_robb_stark.yml +22 -0
- data/spec/cassettes/girls/arya_stark.yml +24 -0
- data/spec/cassettes/robb_stark.yml +28 -0
- data/spec/cassettes/robb_stark_without_content_length.yml +22 -0
- data/spec/cassettes/sansa_stark.yml +24 -0
- data/spec/cassettes/starks.yml +28 -0
- data/spec/cassettes/wolfs.yml +28 -0
- data/spec/factories/arya_stark.rb +7 -0
- data/spec/factories/bran_stark.rb +7 -0
- data/spec/factories/ned_stark.rb +7 -0
- data/spec/factories/robb_stark.rb +7 -0
- data/spec/factories/sansa_stark.rb +7 -0
- data/spec/integration/whisperer_spec.rb +51 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/spec_integration_helper.rb +6 -0
- data/spec/support/cassettes.rb +16 -0
- data/spec/support/custom_serializer.rb +5 -0
- data/spec/unit/config_spec.rb +94 -0
- data/spec/unit/convertors/hash_spec.rb +59 -0
- data/spec/unit/convertors/interaction_spec.rb +46 -0
- data/spec/unit/dsl/base_spec.rb +99 -0
- data/spec/unit/dsl/body_spec.rb +73 -0
- data/spec/unit/dsl/headers_spec.rb +31 -0
- data/spec/unit/dsl/request_spec.rb +4 -0
- data/spec/unit/dsl/response_spec.rb +4 -0
- data/spec/unit/dsl/status_spec.rb +4 -0
- data/spec/unit/dsl_spec.rb +4 -0
- data/spec/unit/generator_spec.rb +77 -0
- data/spec/unit/helpers_spec.rb +38 -0
- data/spec/unit/preprocessors/content_length_spec.rb +39 -0
- data/spec/unit/preprocessors/response_body_spec.rb +55 -0
- data/spec/unit/preprocessors_spec.rb +8 -0
- data/spec/unit/record/headers_spec.rb +21 -0
- data/spec/unit/record/response_spec.rb +11 -0
- data/spec/unit/record_spec.rb +77 -0
- data/spec/unit/serializers/base_spec.rb +19 -0
- data/spec/unit/serializers/json_multiple_spec.rb +24 -0
- data/spec/unit/serializers/json_spec.rb +37 -0
- data/spec/unit/whisperer_spec.rb +189 -0
- data/whisperer.gemspec +28 -0
- metadata +277 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Preprocessors::ContentLength do
|
4
|
+
describe '#process' do
|
5
|
+
subject { described_class.new(record) }
|
6
|
+
|
7
|
+
context 'when a content length for a response is defined' do
|
8
|
+
let(:record) {
|
9
|
+
r = Whisperer::Record.new
|
10
|
+
r.response.headers.content_length = '10'
|
11
|
+
r
|
12
|
+
}
|
13
|
+
|
14
|
+
it 'record has a unchanged content length' do
|
15
|
+
subject.process
|
16
|
+
|
17
|
+
expect(record.response.headers.content_length).to eq('10')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when a content length for a response is not defined' do
|
22
|
+
let(:record) {
|
23
|
+
Whisperer::Record.new(
|
24
|
+
response: {
|
25
|
+
body: {
|
26
|
+
string: 'test'
|
27
|
+
}
|
28
|
+
}
|
29
|
+
)
|
30
|
+
}
|
31
|
+
|
32
|
+
it 'measures size of body and writes it to the content length header of the response' do
|
33
|
+
subject.process
|
34
|
+
|
35
|
+
expect(record.response.headers.content_length).to eq('4')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Preprocessors::ResponseBody do
|
4
|
+
describe '#process' do
|
5
|
+
subject { described_class.new(record) }
|
6
|
+
|
7
|
+
context 'when there is not data object' do
|
8
|
+
let(:record) { Whisperer::Record.new }
|
9
|
+
|
10
|
+
it 'does not serialize anything' do
|
11
|
+
expect(Whisperer).to_not receive(:serializer)
|
12
|
+
|
13
|
+
subject.process
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when there is data object' do
|
18
|
+
let(:serialized_data) { 'serialized data' }
|
19
|
+
let(:serializer) { double('Serializer', serialize: true) }
|
20
|
+
|
21
|
+
let(:data_obj) { OpenStruct.new(test: 'obj') }
|
22
|
+
|
23
|
+
let(:record) {
|
24
|
+
r = Whisperer::Record.new
|
25
|
+
r.response.body.data_obj = data_obj
|
26
|
+
r.response.body.serializer = :json
|
27
|
+
r.response.body.serializer_opts = {some: 'options'}
|
28
|
+
r
|
29
|
+
}
|
30
|
+
|
31
|
+
before do
|
32
|
+
allow(Whisperer).to receive(:serializer).and_return(serializer)
|
33
|
+
allow(serializer).to receive(:serialize).and_return(serialized_data)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'gets the serializer' do
|
37
|
+
expect(Whisperer).to receive(:serializer).with(:json)
|
38
|
+
|
39
|
+
subject.process
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'serializes the respose body' do
|
43
|
+
expect(serializer).to receive(:serialize).with(data_obj, {some: 'options'})
|
44
|
+
|
45
|
+
subject.process
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'assigns the serialized data to the record' do
|
49
|
+
subject.process
|
50
|
+
|
51
|
+
expect(record.response.body.string).to eq(serialized_data)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Headers do
|
4
|
+
describe '.to_hash' do
|
5
|
+
subject {
|
6
|
+
obj = described_class.new()
|
7
|
+
|
8
|
+
obj.attribute(:content_type, String)
|
9
|
+
obj.attributes = {
|
10
|
+
content_type: 'json'
|
11
|
+
}
|
12
|
+
obj
|
13
|
+
}
|
14
|
+
|
15
|
+
it 'returns a hash with correct keys' do
|
16
|
+
expect(subject.to_hash).to eq(
|
17
|
+
'Content-Type' => 'json'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Record do
|
4
|
+
describe '#merge!' do
|
5
|
+
let(:record) {
|
6
|
+
r = Whisperer::Record.new(
|
7
|
+
request: {
|
8
|
+
uri: 'http://google.com',
|
9
|
+
},
|
10
|
+
response: {
|
11
|
+
body: {
|
12
|
+
encoding: 'UTF-8',
|
13
|
+
string: 'test'
|
14
|
+
}
|
15
|
+
},
|
16
|
+
recorded_at: 'test data'
|
17
|
+
)
|
18
|
+
|
19
|
+
r.request.headers.attribute(:content_length, Integer)
|
20
|
+
r.request.headers.attribute(:accept, String)
|
21
|
+
|
22
|
+
r.request.headers.content_length = 100
|
23
|
+
r.request.headers.accept = 'javascript'
|
24
|
+
r
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:another_record) {
|
28
|
+
r = Whisperer::Record.new(
|
29
|
+
request: {
|
30
|
+
headers: {
|
31
|
+
content_length: 50
|
32
|
+
}
|
33
|
+
},
|
34
|
+
response: {
|
35
|
+
body: {
|
36
|
+
encoding: 'UTF-16'
|
37
|
+
}
|
38
|
+
}
|
39
|
+
)
|
40
|
+
|
41
|
+
r.request.headers.attribute(:content_length, Integer)
|
42
|
+
|
43
|
+
r.request.headers.content_length = 50
|
44
|
+
r
|
45
|
+
}
|
46
|
+
|
47
|
+
subject { another_record }
|
48
|
+
|
49
|
+
before do
|
50
|
+
subject.merge!(record)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'has a correct content length header for the request' do
|
54
|
+
expect(subject.request.headers.content_length).to eq(50)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'has an accept header for the request' do
|
58
|
+
expect(subject.request.headers.accept).to eq('javascript')
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'has an url' do
|
62
|
+
expect(subject.request.uri).to eq('http://google.com')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'has a correct encoding for the body of the response' do
|
66
|
+
expect(subject.response.body.encoding).to eq('UTF-16')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'has a string for the body of the response' do
|
70
|
+
expect(subject.response.body.string).to eq('test')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has a correct recorded_at value' do
|
74
|
+
expect(subject.recorded_at).to eq('test data')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Serializers::Base do
|
4
|
+
let(:options) { {test: 1} }
|
5
|
+
|
6
|
+
subject { described_class.new(nil, options: options) }
|
7
|
+
|
8
|
+
describe '.new' do
|
9
|
+
it 'keeps given options' do
|
10
|
+
expect(subject.options).to eq(options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#serialize' do
|
15
|
+
it 'raises an error about implementation' do
|
16
|
+
expect{ subject.serialize }.to raise_error(NotImplementedError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer::Serializers::JsonMultiple do
|
4
|
+
describe '#serialize' do
|
5
|
+
let(:attrs) {
|
6
|
+
{
|
7
|
+
first_name: 'John',
|
8
|
+
last_name: 'Snow'
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:data) {
|
13
|
+
[
|
14
|
+
OpenStruct.new(attrs)
|
15
|
+
]
|
16
|
+
}
|
17
|
+
|
18
|
+
subject { described_class.new(data) }
|
19
|
+
|
20
|
+
it 'returns json string with a serialized array' do
|
21
|
+
expect(subject.serialize).to eq('[{"first_name":"John","last_name":"Snow"}]')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestChildJson < Whisperer::Serializers::Json
|
4
|
+
protected
|
5
|
+
def post_prepare_data(data)
|
6
|
+
{test: data}
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Whisperer::Serializers::Json do
|
11
|
+
describe '#serialize' do
|
12
|
+
let(:attrs) {
|
13
|
+
{
|
14
|
+
first_name: 'John',
|
15
|
+
last_name: 'Snow'
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
subject { described_class.new(OpenStruct.new(attrs)) }
|
20
|
+
|
21
|
+
it 'returns json string with serialized attributes' do
|
22
|
+
expect(subject.serialize).to eq('{"first_name":"John","last_name":"Snow"}')
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when the class is inherited' do
|
26
|
+
subject { TestChildJson.new(OpenStruct.new(attrs)) }
|
27
|
+
|
28
|
+
context 'when the child class alters the data structure' do
|
29
|
+
it 'returns the altered structure' do
|
30
|
+
expect(subject.serialize).to eq(
|
31
|
+
'{"test":{"first_name":"John","last_name":"Snow"}}'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Whisperer do
|
4
|
+
after do
|
5
|
+
described_class.cassette_records.clear
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.define' do
|
9
|
+
let(:cassette_record) { instance_double('Whisperer::Record', merge!: true) }
|
10
|
+
let(:dsl) { instance_double('Whisperer::Dsl', container: cassette_record) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
allow(Whisperer::Dsl).to receive(:build).and_return(dsl)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'builds the dsl object' do
|
17
|
+
expect(Whisperer::Dsl).to receive(:build)
|
18
|
+
|
19
|
+
described_class.define(:test) {}
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'executes a given block over the dsl object' do
|
23
|
+
expect(dsl).to receive(:request)
|
24
|
+
|
25
|
+
described_class.define(:test) {
|
26
|
+
request
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'stores the generated cassette record' do
|
31
|
+
described_class.define(:test) {}
|
32
|
+
|
33
|
+
expect(Whisperer.cassette_records[:test]).to eq(cassette_record)
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when a string as a name of the cassette record is given' do
|
37
|
+
it 'stores a cassette record with a symbol key' do
|
38
|
+
described_class.define('test') {}
|
39
|
+
|
40
|
+
expect(Whisperer.cassette_records[:test]).to eq(cassette_record)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when a parent is defined for a cassette record' do
|
45
|
+
context 'when such parent exists' do
|
46
|
+
let(:orig_cassette_record) { double('original cassette record') }
|
47
|
+
|
48
|
+
before do
|
49
|
+
Whisperer.cassette_records[:some_parent] = orig_cassette_record
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'merges the original record with the newly built' do
|
53
|
+
expect(cassette_record).to receive(:merge!).with(orig_cassette_record)
|
54
|
+
|
55
|
+
described_class.define('test', parent: :some_parent) {}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when such parent does not exist' do
|
60
|
+
it 'raises an error' do
|
61
|
+
expect {
|
62
|
+
described_class.define('test', parent: :some_parent) {}
|
63
|
+
}.to raise_error(ArgumentError, 'Parent record "some_parent" is not declared.')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '.defined_any?' do
|
70
|
+
context 'when there are defined cassette records' do
|
71
|
+
before do
|
72
|
+
described_class.cassette_records[:test] = true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'returns true' do
|
76
|
+
expect(Whisperer.defined_any?).to be_truthy
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when there are not defined cassette_records' do
|
81
|
+
it 'returns false' do
|
82
|
+
expect(Whisperer.defined_any?).to be_falsey
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '.generate' do
|
88
|
+
context 'when the cassette record with the given name does not exist' do
|
89
|
+
it 'raises an error' do
|
90
|
+
expect { described_class.generate(:mytest) }.to raise_error(
|
91
|
+
Whisperer::NocassetteRecordError,
|
92
|
+
'There is not cassette builder with "mytest" name.'
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when the cassette with the given name exists' do
|
98
|
+
shared_examples 'generator' do |name|
|
99
|
+
it 'generates the VCR cassette' do
|
100
|
+
expect(Whisperer::Generator).to receive(:generate).with(
|
101
|
+
cassette_record,
|
102
|
+
:test
|
103
|
+
)
|
104
|
+
|
105
|
+
described_class.generate(name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:cassette_record) { double }
|
110
|
+
|
111
|
+
before do
|
112
|
+
described_class.cassette_records[:test] = cassette_record
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when the given name is a symbol' do
|
116
|
+
it_behaves_like 'generator', :test
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when the given name is a string' do
|
120
|
+
it_behaves_like 'generator', 'test'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '.generate_all' do
|
126
|
+
context 'when there are defined cassettes' do
|
127
|
+
let(:record1) { double('cassette record 1') }
|
128
|
+
let(:record2) { double('cassette record 2') }
|
129
|
+
|
130
|
+
before do
|
131
|
+
Whisperer::cassette_records[:record1] = record1
|
132
|
+
Whisperer::cassette_records[:record2] = record2
|
133
|
+
|
134
|
+
allow(Whisperer).to receive(:defined_any?).and_return(true)
|
135
|
+
allow(Whisperer).to receive(:generate)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'generates the VCR cassette based on record1' do
|
139
|
+
expect(Whisperer).to receive(:generate).with(:record1)
|
140
|
+
|
141
|
+
described_class.generate_all
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'generates the VCR cassette based on record2' do
|
145
|
+
expect(Whisperer).to receive(:generate).with(:record2)
|
146
|
+
|
147
|
+
described_class.generate_all
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when there are not defined cassette records' do
|
152
|
+
before do
|
153
|
+
allow(Whisperer).to receive(:defined_any?).and_return(false)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'raises and error' do
|
157
|
+
expect { described_class.generate_all }.to raise_error(
|
158
|
+
Whisperer::NocassetteRecordError,
|
159
|
+
'cassette builders are not found.'
|
160
|
+
)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '.serializer' do
|
166
|
+
context 'when there is not such serializer' do
|
167
|
+
it 'raises an error' do
|
168
|
+
expect { described_class.serializer(:mytest) }.to raise_error(
|
169
|
+
ArgumentError,
|
170
|
+
'There is not serializer registered with "mytest" name'
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when there is such serializer' do
|
176
|
+
it 'returns the registered class' do
|
177
|
+
expect(described_class.serializer(:json)).to eq(Whisperer::Serializers::Json)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe '.register_preprocessor' do
|
183
|
+
it 'registers preprocessor' do
|
184
|
+
expect(Whisperer::Preprocessors).to receive(:register).with('some name', 'some class')
|
185
|
+
|
186
|
+
Whisperer.register_preprocessor('some name', 'some class')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|