twingly-search 5.0.1 → 5.1.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 +1 -2
- data/CHANGELOG.md +16 -0
- data/README.md +33 -34
- data/Rakefile +0 -6
- data/examples/find_all_posts_mentioning_github.rb +3 -3
- data/examples/hello_world.rb +2 -2
- data/examples/livefeed_loop.rb +24 -0
- data/lib/twingly/livefeed/client.rb +121 -0
- data/lib/twingly/livefeed/error.rb +28 -0
- data/lib/twingly/livefeed/parser.rb +96 -0
- data/lib/twingly/livefeed/post.rb +66 -0
- data/lib/twingly/livefeed/result.rb +39 -0
- data/lib/twingly/livefeed/version.rb +5 -0
- data/lib/twingly/livefeed.rb +6 -0
- data/lib/twingly/search/client.rb +3 -2
- data/lib/twingly/search/error.rb +6 -5
- data/lib/twingly/search/parser.rb +39 -13
- data/lib/twingly/search/post.rb +65 -21
- data/lib/twingly/search/query.rb +46 -16
- data/lib/twingly/search/result.rb +11 -0
- data/lib/twingly/search/version.rb +1 -1
- data/spec/client_spec.rb +2 -2
- data/spec/error_spec.rb +27 -7
- data/spec/fixtures/incomplete_result.xml +2 -0
- data/spec/fixtures/livefeed/empty_api_key_result.xml +3 -0
- data/spec/fixtures/livefeed/non_xml_result.xml +1 -0
- data/spec/fixtures/livefeed/not_found_result.xml +3 -0
- data/spec/fixtures/livefeed/service_unavailable_result.xml +3 -0
- data/spec/fixtures/livefeed/unauthorized_api_key_result.xml +3 -0
- data/spec/fixtures/livefeed/valid_empty_result.xml +2 -0
- data/spec/fixtures/livefeed/valid_result.xml +79 -0
- data/spec/fixtures/minimal_valid_result.xml +81 -52
- data/spec/fixtures/nonexistent_api_key_result.xml +3 -3
- data/spec/fixtures/service_unavailable_result.xml +3 -3
- data/spec/fixtures/unauthorized_api_key_result.xml +3 -3
- data/spec/fixtures/undefined_error_result.xml +3 -3
- data/spec/fixtures/valid_empty_result.xml +2 -2
- data/spec/fixtures/valid_links_result.xml +36 -0
- data/spec/fixtures/vcr_cassettes/livefeed_valid_request.yml +169 -0
- data/spec/fixtures/vcr_cassettes/search_for_spotify_on_sv_blogs.yml +578 -447
- data/spec/fixtures/vcr_cassettes/search_without_valid_api_key.yml +15 -14
- data/spec/livefeed/client_spec.rb +135 -0
- data/spec/livefeed/error_spec.rb +51 -0
- data/spec/livefeed/parser_spec.rb +351 -0
- data/spec/livefeed/post_spec.rb +26 -0
- data/spec/livefeed/result_spec.rb +18 -0
- data/spec/parser_spec.rb +191 -94
- data/spec/post_spec.rb +25 -6
- data/spec/query_spec.rb +41 -34
- data/spec/result_spec.rb +1 -0
- data/spec/spec_helper.rb +10 -0
- data/twingly-search-api-ruby.gemspec +2 -3
- metadata +44 -24
- data/spec/fixtures/valid_non_blog_result.xml +0 -26
- data/spec/fixtures/valid_result.xml +0 -22975
@@ -2,40 +2,41 @@
|
|
2
2
|
http_interactions:
|
3
3
|
- request:
|
4
4
|
method: get
|
5
|
-
uri: https://api.twingly.com/
|
5
|
+
uri: https://api.twingly.com/blog/search/api/v3/search?apikey=wrong&q=something
|
6
6
|
body:
|
7
7
|
encoding: US-ASCII
|
8
8
|
string: ''
|
9
9
|
headers:
|
10
10
|
User-Agent:
|
11
|
-
- Twingly
|
11
|
+
- Twingly Search Ruby Client/5.0.1
|
12
12
|
Accept-Encoding:
|
13
13
|
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
14
|
Accept:
|
15
15
|
- "*/*"
|
16
16
|
response:
|
17
17
|
status:
|
18
|
-
code:
|
19
|
-
message:
|
18
|
+
code: 401
|
19
|
+
message: Unauthorized
|
20
20
|
headers:
|
21
21
|
Server:
|
22
22
|
- nginx
|
23
23
|
Date:
|
24
|
-
-
|
24
|
+
- Thu, 04 May 2017 07:14:00 GMT
|
25
25
|
Content-Type:
|
26
|
-
-
|
26
|
+
- application/xml; charset=utf-8
|
27
27
|
Content-Length:
|
28
|
-
- '
|
28
|
+
- '97'
|
29
29
|
Connection:
|
30
30
|
- keep-alive
|
31
31
|
Cache-Control:
|
32
|
-
-
|
33
|
-
|
34
|
-
-
|
32
|
+
- no-cache
|
33
|
+
Pragma:
|
34
|
+
- no-cache
|
35
|
+
Expires:
|
36
|
+
- "-1"
|
35
37
|
body:
|
36
38
|
encoding: UTF-8
|
37
|
-
string:
|
38
|
-
|
39
|
-
|
40
|
-
recorded_at: Tue, 03 Nov 2015 09:29:04 GMT
|
39
|
+
string: <?xml version="1.0" encoding="utf-8"?><error code="40101"><message>Unauthorized</message></error>
|
40
|
+
http_version:
|
41
|
+
recorded_at: Thu, 04 May 2017 07:14:00 GMT
|
41
42
|
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "time"
|
3
|
+
|
4
|
+
module Twingly::LiveFeed
|
5
|
+
describe Client do
|
6
|
+
let(:valid_api_key) { "api_key" }
|
7
|
+
let(:max_posts) { 3 }
|
8
|
+
|
9
|
+
subject(:client) do
|
10
|
+
described_class.new do |client|
|
11
|
+
client.max_posts = max_posts
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".new" do
|
16
|
+
context "with API key as argument" do
|
17
|
+
subject { described_class.new(valid_api_key) }
|
18
|
+
|
19
|
+
it { should be_a Twingly::LiveFeed::Client }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "without API key as argument" do
|
23
|
+
before do
|
24
|
+
expect_any_instance_of(described_class)
|
25
|
+
.to receive(:env_api_key).and_return(valid_api_key)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be read from the environment" do
|
29
|
+
described_class.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with no API key at all" do
|
34
|
+
before do
|
35
|
+
expect_any_instance_of(described_class)
|
36
|
+
.to receive(:env_api_key).and_return(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
subject { described_class.new }
|
40
|
+
|
41
|
+
it do
|
42
|
+
expect { subject }
|
43
|
+
.to raise_error(AuthError, "No API key has been provided.")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with block" do
|
48
|
+
it "should yield self" do
|
49
|
+
yielded_client = nil
|
50
|
+
client = described_class.new(valid_api_key) do |c|
|
51
|
+
yielded_client = c
|
52
|
+
end
|
53
|
+
|
54
|
+
expect(yielded_client).to equal(client)
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when api key gets set in block" do
|
58
|
+
let(:api_key) { "api_key_from_block" }
|
59
|
+
subject do
|
60
|
+
described_class.new do |client|
|
61
|
+
client.api_key = api_key
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not raise an AuthError" do
|
66
|
+
expect { subject }.not_to raise_exception
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should use that api key" do
|
70
|
+
expect(subject.api_key).to eq(api_key)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with optional :user_agent given" do
|
76
|
+
let(:user_agent) { "TwinglyLiveFeedTest/1.0" }
|
77
|
+
subject { described_class.new(valid_api_key, user_agent: user_agent) }
|
78
|
+
|
79
|
+
it "should use that user agent" do
|
80
|
+
expect(subject.user_agent).to eq(user_agent)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#next_result" do
|
86
|
+
before do
|
87
|
+
client.timestamp = timestamp
|
88
|
+
end
|
89
|
+
|
90
|
+
context "with a valid timestamp" do
|
91
|
+
let(:timestamp) { Time.parse("2017-04-19T22:00:00 UTC") }
|
92
|
+
|
93
|
+
subject do
|
94
|
+
VCR.use_cassette("livefeed_valid_request") do
|
95
|
+
client.next_result
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it { is_expected.to be_a(Result) }
|
100
|
+
|
101
|
+
it "should update the timestamp" do
|
102
|
+
timestamp_before = client.timestamp
|
103
|
+
subject
|
104
|
+
timestamp_after = client.timestamp
|
105
|
+
|
106
|
+
expect(timestamp_after).to be > timestamp_before
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with an invalid timestamp" do
|
111
|
+
let(:timestamp) { "not a timestamp" }
|
112
|
+
|
113
|
+
subject { client.next_result }
|
114
|
+
|
115
|
+
it "should raise an error" do
|
116
|
+
expect{ subject }
|
117
|
+
.to raise_error(QueryError, /Not a Time object/)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#endpoint_url" do
|
123
|
+
subject { described_class.new(valid_api_key).endpoint_url }
|
124
|
+
let(:expected_url) do
|
125
|
+
"#{described_class::BASE_URL}#{described_class::LIVEFEED_PATH}"
|
126
|
+
end
|
127
|
+
|
128
|
+
it { is_expected.to eq(expected_url) }
|
129
|
+
|
130
|
+
it "should be parsable" do
|
131
|
+
expect(URI(subject).to_s).to eq(expected_url)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Twingly::LiveFeed
|
4
|
+
describe Error do
|
5
|
+
it { is_expected.to be_a(StandardError) }
|
6
|
+
|
7
|
+
let(:message) { "This is the error message!" }
|
8
|
+
|
9
|
+
describe ".from_api_response" do
|
10
|
+
subject { described_class.from_api_response(code, message) }
|
11
|
+
|
12
|
+
context "when given code 401" do
|
13
|
+
let(:code) { 401 }
|
14
|
+
|
15
|
+
it { is_expected.to be_a(AuthError) }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when given code 400" do
|
19
|
+
let(:code) { 400 }
|
20
|
+
|
21
|
+
it { is_expected.to be_a(QueryError) }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when given code 404" do
|
25
|
+
let(:code) { 404 }
|
26
|
+
|
27
|
+
it { is_expected.to be_a(QueryError) }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when given another code" do
|
31
|
+
let(:code) { 500 }
|
32
|
+
|
33
|
+
it { is_expected.to be_a(ServerError) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "all error classes" do
|
38
|
+
error_classes = [
|
39
|
+
AuthError,
|
40
|
+
ServerError,
|
41
|
+
QueryError,
|
42
|
+
]
|
43
|
+
|
44
|
+
error_classes.each do |error_class|
|
45
|
+
describe error_class do
|
46
|
+
it { is_expected.to be_kind_of(Twingly::LiveFeed::Error) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Twingly::LiveFeed
|
7
|
+
describe Parser do
|
8
|
+
it { should respond_to(:parse) }
|
9
|
+
|
10
|
+
describe "#parse" do
|
11
|
+
let(:document) { Fixture.livefeed_get(fixture) }
|
12
|
+
subject(:result) { described_class.new.parse(document) }
|
13
|
+
|
14
|
+
context "with a valid result" do
|
15
|
+
let(:fixture) { :valid }
|
16
|
+
|
17
|
+
it { is_expected.to be_a Result }
|
18
|
+
|
19
|
+
describe "#ts" do
|
20
|
+
subject { result.ts }
|
21
|
+
it { is_expected.to eq(Time.parse("2017-04-11T15:09:48.8750635Z")) }
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#from" do
|
25
|
+
subject { result.from }
|
26
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:00Z")) }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#first_post" do
|
30
|
+
subject { result.first_post }
|
31
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:29.267Z")) }
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#last_post" do
|
35
|
+
subject { result.last_post }
|
36
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:11:47.243Z")) }
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#number_of_posts" do
|
40
|
+
subject { result.number_of_posts }
|
41
|
+
it { is_expected.to eq(3) }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#max_number_of_posts" do
|
45
|
+
subject { result.max_number_of_posts }
|
46
|
+
it { is_expected.to eq(3) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#posts" do
|
50
|
+
subject { result.posts }
|
51
|
+
|
52
|
+
it { is_expected.to all(be_a(Post)) }
|
53
|
+
|
54
|
+
describe "#count" do
|
55
|
+
subject { result.posts.count }
|
56
|
+
|
57
|
+
it { is_expected.to eq(3) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#posts.first" do
|
62
|
+
subject(:post) { result.posts.first }
|
63
|
+
|
64
|
+
describe "#id" do
|
65
|
+
subject { post.id }
|
66
|
+
it { is_expected.to eq("727444183574244541") }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#author" do
|
70
|
+
subject { post.author }
|
71
|
+
it { is_expected.to eq("") }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#url" do
|
75
|
+
subject { post.url }
|
76
|
+
it { is_expected.to eq("http://flinnman.blogg.se/2017/april/mandag-igen.html") }
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#title" do
|
80
|
+
subject { post.title }
|
81
|
+
it { is_expected.to eq("Måndag igen") }
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#text" do
|
85
|
+
subject { post.text }
|
86
|
+
it { is_expected.to match("Hoppla hejsan bloggy!Måndag.") }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#language_code" do
|
90
|
+
subject { post.language_code }
|
91
|
+
it { is_expected.to eq("sv") }
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#location_code" do
|
95
|
+
subject { post.location_code }
|
96
|
+
it { is_expected.to eq("se") }
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#coordinates" do
|
100
|
+
subject { post.coordinates }
|
101
|
+
it { is_expected.to eq({}) }
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#links" do
|
105
|
+
subject { post.links }
|
106
|
+
it { is_expected.to be_empty }
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#tags" do
|
110
|
+
subject { post.tags }
|
111
|
+
it { is_expected.to eq(%w(Jag)) }
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#images" do
|
115
|
+
subject { post.images }
|
116
|
+
it { is_expected.to be_empty }
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#indexed_at" do
|
120
|
+
subject { post.indexed_at }
|
121
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:24Z")) }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#published_at" do
|
125
|
+
subject { post.published_at }
|
126
|
+
it { is_expected.to eq(Time.parse("2017-04-10T19:11:11Z")) }
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#reindexed_at" do
|
130
|
+
subject { post.reindexed_at }
|
131
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:24Z")) }
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#inlinks_count" do
|
135
|
+
subject { post.inlinks_count }
|
136
|
+
it { is_expected.to eq(0) }
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#blog_id" do
|
140
|
+
subject { post.blog_id }
|
141
|
+
it { is_expected.to eq("10357806725947705095") }
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#blog_name" do
|
145
|
+
subject { post.blog_name }
|
146
|
+
it { is_expected.to eq("Frida L") }
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#blog_url" do
|
150
|
+
subject { post.blog_url }
|
151
|
+
it { is_expected.to eq("http://flinnman.blogg.se") }
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#blog_rank" do
|
155
|
+
subject { post.blog_rank }
|
156
|
+
it { is_expected.to eq(1) }
|
157
|
+
end
|
158
|
+
|
159
|
+
describe "#authority" do
|
160
|
+
subject { post.authority }
|
161
|
+
it { is_expected.to eq(2) }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#posts.last" do
|
166
|
+
subject(:post) { result.posts.last }
|
167
|
+
|
168
|
+
describe "#id" do
|
169
|
+
subject { post.id }
|
170
|
+
it { is_expected.to eq("3062976931264108164") }
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#author" do
|
174
|
+
subject { post.author }
|
175
|
+
it { is_expected.to eq("josegacel") }
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#url" do
|
179
|
+
subject { post.url }
|
180
|
+
it { is_expected.to eq("https://josegabrielcelis.wordpress.com/2017/04/09/1476/") }
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "#title" do
|
184
|
+
subject { post.title }
|
185
|
+
it { is_expected.to eq("") }
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "#text" do
|
189
|
+
subject { post.text }
|
190
|
+
it { is_expected.to eq("from Instagram: http://ift.tt/2ofZdhV") }
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "#language_code" do
|
194
|
+
subject { post.language_code }
|
195
|
+
it { is_expected.to eq("sv") }
|
196
|
+
end
|
197
|
+
|
198
|
+
describe "#location_code" do
|
199
|
+
subject { post.location_code }
|
200
|
+
it { is_expected.to eq("") }
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#coordinates" do
|
204
|
+
subject { post.coordinates }
|
205
|
+
it { is_expected.to eq({}) }
|
206
|
+
end
|
207
|
+
|
208
|
+
describe "#links" do
|
209
|
+
subject { post.links }
|
210
|
+
let(:expected) do
|
211
|
+
%w(
|
212
|
+
http://www.ift.tt/2ofZdhV
|
213
|
+
http://feeds.wordpress.com/1.0/gocomments/josegabrielcelis.wordpress.com/1476
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
it { is_expected.to eq(expected) }
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#tags" do
|
221
|
+
subject { post.tags }
|
222
|
+
it { is_expected.to eq(%w(Fotos Instagram)) }
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "#images" do
|
226
|
+
subject { post.images }
|
227
|
+
it { is_expected.to be_empty }
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#indexed_at" do
|
231
|
+
subject { post.indexed_at }
|
232
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:28Z")) }
|
233
|
+
end
|
234
|
+
|
235
|
+
describe "#published_at" do
|
236
|
+
subject { post.published_at }
|
237
|
+
it { is_expected.to eq(Time.parse("2017-04-09T22:31:34Z")) }
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "#reindexed_at" do
|
241
|
+
subject { post.reindexed_at }
|
242
|
+
it { is_expected.to eq(Time.parse("2017-04-10T22:00:28Z")) }
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "#inlinks_count" do
|
246
|
+
subject { post.inlinks_count }
|
247
|
+
it { is_expected.to eq(0) }
|
248
|
+
end
|
249
|
+
|
250
|
+
describe "#blog_id" do
|
251
|
+
subject { post.blog_id }
|
252
|
+
it { is_expected.to eq("1811310581070495497") }
|
253
|
+
end
|
254
|
+
|
255
|
+
describe "#blog_name" do
|
256
|
+
subject { post.blog_name }
|
257
|
+
it { is_expected.to eq("José Gabriel Celis") }
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "#blog_url" do
|
261
|
+
subject { post.blog_url }
|
262
|
+
it { is_expected.to eq("https://josegabrielcelis.wordpress.com") }
|
263
|
+
end
|
264
|
+
|
265
|
+
describe "#blog_rank" do
|
266
|
+
subject { post.blog_rank }
|
267
|
+
it { is_expected.to eq(1) }
|
268
|
+
end
|
269
|
+
|
270
|
+
describe "#authority" do
|
271
|
+
subject { post.authority }
|
272
|
+
it { is_expected.to eq(0) }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "with a valid empty result" do
|
278
|
+
let(:fixture) { :valid_empty }
|
279
|
+
|
280
|
+
describe "#posts" do
|
281
|
+
subject { result.posts }
|
282
|
+
it { is_expected.to be_empty }
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "#number_of_posts" do
|
286
|
+
subject { result.number_of_posts }
|
287
|
+
it { is_expected.to eq(0) }
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "#first_post" do
|
291
|
+
subject { result.first_post }
|
292
|
+
it { is_expected.to eq(nil) }
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "#last_post" do
|
296
|
+
subject { result.last_post }
|
297
|
+
it { is_expected.to eq(nil) }
|
298
|
+
end
|
299
|
+
|
300
|
+
describe "#next_timestamp" do
|
301
|
+
subject { result.next_timestamp }
|
302
|
+
it { is_expected.to eq(Time.parse("2017-04-25T22:00:00Z")) }
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "with an unauthorized api key result" do
|
307
|
+
let(:fixture) { :unauthorized_api_key }
|
308
|
+
|
309
|
+
it "should raise AuthError" do
|
310
|
+
expect { subject }.to raise_error(AuthError)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
context "with an empty api key result" do
|
315
|
+
let(:fixture) { :empty_api_key }
|
316
|
+
|
317
|
+
it "should raise AuthError" do
|
318
|
+
expect { subject }.to raise_error(QueryError)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "with a 404 not found result" do
|
323
|
+
let(:fixture) { :not_found }
|
324
|
+
|
325
|
+
it "should raise QueryError" do
|
326
|
+
expect { subject }.to raise_error(QueryError)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
context "with a service unavailable result" do
|
331
|
+
let(:fixture) { :service_unavailable }
|
332
|
+
|
333
|
+
it "should raise ServerError" do
|
334
|
+
expect { subject }.to raise_error(ServerError)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "with a non XML response" do
|
339
|
+
let(:fixture) { :non_xml }
|
340
|
+
let(:expected_exception_message) do
|
341
|
+
/Failed to parse response: "<html>.*/
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should raise ServerError" do
|
345
|
+
expect { subject }
|
346
|
+
.to raise_error(ServerError, expected_exception_message)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Twingly::LiveFeed
|
4
|
+
describe Post do
|
5
|
+
it { should respond_to :id }
|
6
|
+
it { should respond_to :author }
|
7
|
+
it { should respond_to :url }
|
8
|
+
it { should respond_to :title }
|
9
|
+
it { should respond_to :text }
|
10
|
+
it { should respond_to :location_code }
|
11
|
+
it { should respond_to :language_code }
|
12
|
+
it { should respond_to :coordinates }
|
13
|
+
it { should respond_to :links }
|
14
|
+
it { should respond_to :tags }
|
15
|
+
it { should respond_to :images }
|
16
|
+
it { should respond_to :indexed_at }
|
17
|
+
it { should respond_to :published_at }
|
18
|
+
it { should respond_to :reindexed_at }
|
19
|
+
it { should respond_to :inlinks_count }
|
20
|
+
it { should respond_to :blog_id }
|
21
|
+
it { should respond_to :blog_name }
|
22
|
+
it { should respond_to :blog_url }
|
23
|
+
it { should respond_to :blog_rank }
|
24
|
+
it { should respond_to :authority }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Twingly::LiveFeed
|
4
|
+
describe Result do
|
5
|
+
it { should respond_to :posts }
|
6
|
+
it { should respond_to :ts }
|
7
|
+
it { should respond_to :from }
|
8
|
+
it { should respond_to :number_of_posts }
|
9
|
+
it { should respond_to :max_number_of_posts }
|
10
|
+
it { should respond_to :first_post }
|
11
|
+
it { should respond_to :last_post }
|
12
|
+
it { should respond_to :next_timestamp }
|
13
|
+
|
14
|
+
context "before query has populated responses" do
|
15
|
+
its(:posts) { should be_empty }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|