whisperer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|