lita-forecast 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe LitaForecast::Mixins do
5
+ include LitaForecast::Mixins
6
+
7
+ describe '#geo_location' do
8
+ let(:geocoder) do
9
+ {
10
+ 'address_components' => [
11
+ { 'long_name' => 'San Francisco', 'types' => %w(locality) },
12
+ { 'short_name' => 'CA', 'types' => %w(administrative_area_level_1) }
13
+ ]
14
+ }
15
+ end
16
+
17
+ context 'when given more than one arg' do
18
+ it 'should raise ArgumentError' do
19
+ expect { geo_location(nil, nil) }.to raise_error ArgumentError
20
+ end
21
+ end
22
+
23
+ context 'when given less than one arg' do
24
+ it 'should raise ArgumentError' do
25
+ expect { geo_location }.to raise_error ArgumentError
26
+ end
27
+ end
28
+
29
+ context 'when result is a Geocoder hash' do
30
+ subject { geo_location(geocoder) }
31
+
32
+ it { should be_an_instance_of Hash }
33
+
34
+ it 'should set the city to "San Francisco"' do
35
+ expect(subject[:city]).to eql 'San Francisco'
36
+ end
37
+
38
+ it 'should set the state to CA' do
39
+ expect(subject[:state]).to eql 'CA'
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#units' do
45
+ let(:forecast_us) { { 'flags' => { 'units' => 'us' } } }
46
+ let(:forecast_ca) { { 'flags' => { 'units' => 'ca' } } }
47
+
48
+ context 'when given more than one arg' do
49
+ it 'should raise ArgumentError' do
50
+ expect { units(nil, nil) }.to raise_error ArgumentError
51
+ end
52
+ end
53
+
54
+ context 'when given less than one arg' do
55
+ it 'should raise ArgumentError' do
56
+ expect { units }.to raise_error ArgumentError
57
+ end
58
+ end
59
+
60
+ context 'when passed a US ForecastIO response' do
61
+ before do
62
+ @unit = units(forecast_us)
63
+ end
64
+ subject { @unit }
65
+
66
+ it { should be_an_instance_of Hash }
67
+
68
+ context 'when the temp (:t) key' do
69
+ subject { @unit[:t] }
70
+
71
+ it { should be_an_instance_of String }
72
+
73
+ it { should eql 'F' }
74
+ end
75
+
76
+ context 'when the wind (:w) key' do
77
+ subject { @unit[:w] }
78
+
79
+ it { should be_an_instance_of String }
80
+
81
+ it { should eql 'mph' }
82
+ end
83
+
84
+ context 'when the visibility (:v) key' do
85
+ subject { @unit[:v] }
86
+
87
+ it { should be_an_instance_of String }
88
+
89
+ it { should eql 'mi' }
90
+ end
91
+ end
92
+
93
+ context 'when passed a CA ForecastIO response' do
94
+ before do
95
+ @unit = units(forecast_ca)
96
+ end
97
+ subject { @unit }
98
+
99
+ it { should be_an_instance_of Hash }
100
+
101
+ context 'when the temp (:t) key' do
102
+ subject { @unit[:t] }
103
+
104
+ it { should be_an_instance_of String }
105
+
106
+ it { should eql 'C' }
107
+ end
108
+
109
+ context 'when the wind (:w) key' do
110
+ subject { @unit[:w] }
111
+
112
+ it { should be_an_instance_of String }
113
+
114
+ it { should eql 'kmph' }
115
+ end
116
+
117
+ context 'when the visibility (:v) key' do
118
+ subject { @unit[:v] }
119
+
120
+ it { should be_an_instance_of String }
121
+
122
+ it { should eql 'km' }
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,27 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'spec_helper'
3
+ require 'redis-namespace'
4
+
5
+ describe LitaForecast do
6
+ describe '::FORECAST_NAMESPACE' do
7
+ subject { LitaForecast::FORECAST_NAMESPACE }
8
+
9
+ it { should be_an_instance_of String }
10
+
11
+ it { should eql 'handlers:forecast' }
12
+ end
13
+
14
+ describe '.redis' do
15
+ it 'should not take any args' do
16
+ expect do
17
+ LitaForecast.redis(nil)
18
+ end.to raise_error ArgumentError
19
+ end
20
+
21
+ context 'under normal conditions' do
22
+ subject { LitaForecast.redis }
23
+
24
+ it { should be_an_instance_of Redis::Namespace }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,115 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe LitaForecast::Response do
5
+ let(:forecast) do
6
+ {
7
+ 'currently' => {
8
+ 'temperature' => 100.11,
9
+ 'apparentTemperature' => 102.11,
10
+ 'summary' => 'weather',
11
+ 'windBearing' => 0,
12
+ 'windSpeed' => 5.11,
13
+ 'humidity' => 0.10,
14
+ 'dewPoint' => 20.11,
15
+ 'pressure' => 1020.11,
16
+ 'cloudCover' => 0.01
17
+ },
18
+ 'daily' => {
19
+ 'summary' => 'daily weather',
20
+ 'data' => [
21
+ {
22
+ 'summary' => 'weather today!',
23
+ 'temperatureMax' => 68.0,
24
+ 'temperatureMin' => 60.2,
25
+ 'precipProbability' => 0,
26
+ 'windSpeed' => 0,
27
+ 'windBearing' => 0
28
+ },
29
+ {
30
+ 'summary' => 'weather tomorrow!',
31
+ 'temperatureMax' => 70.0,
32
+ 'temperatureMin' => 62.1,
33
+ 'precipProbability' => 1,
34
+ 'precipType' => 'rain',
35
+ 'windSpeed' => 10,
36
+ 'windBearing' => 270
37
+ }
38
+ ]
39
+ },
40
+ 'minutely' => { 'summary' => 'minutely weather' },
41
+ 'hourly' => { 'summary' => 'hourly weather' },
42
+ 'flags' => { 'units' => 'us' }
43
+ }
44
+ end
45
+ let(:location) { 'San Francisco, CA' }
46
+
47
+ before do
48
+ @response = LitaForecast::Response.new(forecast, location)
49
+ end
50
+
51
+ describe '.new' do
52
+ context 'when given more than two args' do
53
+ it 'should raise ArgumentError' do
54
+ expect do
55
+ LitaForecast::Response.new(nil, nil, nil)
56
+ end.to raise_error ArgumentError
57
+ end
58
+ end
59
+
60
+ context 'when given less than two args' do
61
+ it 'should raise ArgumentError' do
62
+ expect do
63
+ LitaForecast::Response.new(nil)
64
+ end.to raise_error ArgumentError
65
+ end
66
+ end
67
+
68
+ context 'when given forecast and location' do
69
+ subject { LitaForecast::Response.new(forecast, location) }
70
+
71
+ it { should be_an_instance_of LitaForecast::Response }
72
+
73
+ it 'should set the @forecast instance variable' do
74
+ i = subject.instance_variable_get(:@forecast)
75
+ expect(i).to eql forecast
76
+ end
77
+
78
+ it 'should set the @location instance variable' do
79
+ i = subject.instance_variable_get(:@location)
80
+ expect(i).to eql location
81
+ end
82
+ end
83
+ end
84
+
85
+ describe '.generate' do
86
+ let(:fc_s) do
87
+ 'Now: 100.1F (feels like 102.1F) weather; Winds N 5.1mph; Humidity 10%' \
88
+ "; Dew Pt 20.1F; Pressure 1020.1mb; Cloud cover 1%.\n" \
89
+ 'Next hour: minutely weather Next 24h: hourly weather'
90
+ end
91
+ let(:ff_s) do
92
+ "Summary: daily weather\n" \
93
+ "Today: weather today! High 68F, Low 60F. 0% chance of " \
94
+ "precipitation. Winds N 0mph.\n" \
95
+ "Tomorrow: weather tomorrow! High 70F, Low 62F. 100% chance of " \
96
+ "rain. Winds W 10mph.\n"
97
+ end
98
+
99
+ context 'given more than one arg' do
100
+ it 'should raise ArgumentError' do
101
+ expect do
102
+ @response.generate(nil)
103
+ end.to raise_error ArgumentError
104
+ end
105
+ end
106
+
107
+ context 'when called' do
108
+ subject { @response.generate }
109
+
110
+ it { should be_an_instance_of String }
111
+
112
+ it { should eql "San Francisco, CA:\n\n#{fc_s}\n\n#{ff_s}" }
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,147 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'spec_helper'
3
+
4
+ # Class for stubbing Redis class
5
+ #
6
+ class MockRedisClass
7
+ def self.hgetall(search)
8
+ Hashie::Mash.new({})
9
+ end
10
+ end
11
+
12
+ # Class for mocking LitaResponse object
13
+ #
14
+ class MockResponseClass
15
+ def self.args
16
+ []
17
+ end
18
+ end
19
+
20
+ describe LitaForecast::Weather do
21
+ include LitaForecast::Weather
22
+ let(:forecast_h) do
23
+ {
24
+ 'currently' => {
25
+ 'temperature' => 100.11,
26
+ 'apparentTemperature' => 102.11,
27
+ 'summary' => 'weather',
28
+ 'windBearing' => 0,
29
+ 'windSpeed' => 5.11,
30
+ 'humidity' => 0.10,
31
+ 'dewPoint' => 20.11,
32
+ 'pressure' => 1020.11,
33
+ 'cloudCover' => 0.01
34
+ },
35
+ 'daily' => {
36
+ 'summary' => 'daily weather',
37
+ 'data' => [
38
+ {
39
+ 'summary' => 'weather today!',
40
+ 'temperatureMax' => 68.0,
41
+ 'temperatureMin' => 60.2,
42
+ 'precipProbability' => 0,
43
+ 'windSpeed' => 0,
44
+ 'windBearing' => 0
45
+ },
46
+ {
47
+ 'summary' => 'weather tomorrow!',
48
+ 'temperatureMax' => 70.0,
49
+ 'temperatureMin' => 62.1,
50
+ 'precipProbability' => 1,
51
+ 'precipType' => 'rain',
52
+ 'windSpeed' => 10,
53
+ 'windBearing' => 270
54
+ }
55
+ ]
56
+ },
57
+ 'minutely' => { 'summary' => 'minutely weather' },
58
+ 'hourly' => { 'summary' => 'hourly weather' },
59
+ 'flags' => { 'units' => 'us' }
60
+ }
61
+ end
62
+ let(:good_string) do
63
+ "San Francisco, CA:\n\nNow: 100.1F (feels like 102.1F) weather; Winds " \
64
+ "N 5.1mph; Humidity 10%; Dew Pt 20.1F; Pressure 1020.1mb; Cloud cover " \
65
+ "1%.\nNext hour: minutely weather Next 24h: hourly weather\n\nSummary: " \
66
+ "daily weather\nToday: weather today! High 68F, Low 60F. 0% chance of " \
67
+ "precipitation. Winds N 0mph.\nTomorrow: weather tomorrow! High 70F, " \
68
+ "Low 62F. 100% chance of rain. Winds W 10mph.\n"
69
+ end
70
+
71
+ describe '#weather_forecast' do
72
+ context 'when given more than three args' do
73
+ it 'should raise ArgumentError' do
74
+ expect do
75
+ weather_forecast(nil, nil, nil)
76
+ end.to raise_error ArgumentError
77
+ end
78
+ end
79
+
80
+ context 'when given less than one arg' do
81
+ it 'should raise ArgumentError' do
82
+ expect do
83
+ weather_forecast
84
+ end.to raise_error ArgumentError
85
+ end
86
+ end
87
+
88
+ context 'when passed an API key and location hash' do
89
+ before do
90
+ allow(ForecastIO).to receive(:api_key=)
91
+ .with(any_args).and_return(nil)
92
+ allow(ForecastIO).to receive(:forecast)
93
+ .with(any_args).and_return(forecast_h)
94
+ end
95
+
96
+ let(:l_h) do
97
+ {
98
+ params: {},
99
+ lat: 37.7749295,
100
+ lng: -122.4194155
101
+ }
102
+ end
103
+ subject { weather_forecast('', l_h) }
104
+
105
+ it { should be_an_instance_of Hash }
106
+
107
+ it { should eql forecast_h }
108
+ end
109
+ end
110
+
111
+ describe '#weather' do
112
+ context 'when given more than three args' do
113
+ it 'should raise ArgumentError' do
114
+ expect do
115
+ weather(nil, nil, nil, nil)
116
+ end.to raise_error ArgumentError
117
+ end
118
+ end
119
+
120
+ context 'when given less than two args' do
121
+ it 'should raise ArgumentError' do
122
+ expect do
123
+ weather(nil)
124
+ end.to raise_error ArgumentError
125
+ end
126
+ end
127
+
128
+ context 'when given response and api_key' do
129
+ before do
130
+ allow(LitaForecast).to receive(:redis).and_return(MockRedisClass)
131
+ allow(self).to receive(:weather_forecast)
132
+ .and_return(forecast_h)
133
+ allow_any_instance_of(LitaForecast::Location)
134
+ .to receive(:find_location).with(any_args)
135
+ .and_return(
136
+ lat: 37.7749295, lng: -122.4194155, desc: 'San Francisco, CA'
137
+ )
138
+ end
139
+
140
+ subject { weather(MockResponseClass, '') }
141
+
142
+ it { should be_an_instance_of String }
143
+
144
+ it { should eql good_string }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,296 @@
1
+ # -*- coding: UTF-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe Lita::Handlers::ForecastLocations, lita_handler: true do
5
+
6
+ it { routes_command('wadd place 42 42').to(:location_add) }
7
+
8
+ it { routes_command('wadd place 42 42 theplace').to(:location_add) }
9
+
10
+ it { routes_command('wrm place').to(:location_rm) }
11
+
12
+ it { routes_command('wl').to(:locations) }
13
+
14
+ describe '.saved_location_hash' do
15
+ context 'when given a description' do
16
+ let(:location) { ['home', '42', '42', 'my place'] }
17
+
18
+ it 'should return a location hash with unique description' do
19
+ l = { name: 'home', lat: '42', lng: '42', desc: 'my place' }
20
+
21
+ expect(subject.send(:saved_location_hash, location)).to eql l
22
+ end
23
+ end
24
+
25
+ context 'when not given a description' do
26
+ let(:location) { %w(home 42 42) }
27
+
28
+ it 'should return a location hash with the alias as the desc.' do
29
+ l = { name: 'home', lat: '42', lng: '42', desc: 'home' }
30
+
31
+ expect(subject.send(:saved_location_hash, location)).to eql l
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '.rm_from_cache' do
37
+ context 'when passed more than one arg' do
38
+ it 'should raise ArgumentError' do
39
+ expect do
40
+ subject.send(:rm_from_cache, nil, nil)
41
+ end.to raise_error ArgumentError
42
+ end
43
+ end
44
+
45
+ context 'when passed less than one arg' do
46
+ it 'should raise ArgumentError' do
47
+ expect do
48
+ subject.send(:rm_from_cache)
49
+ end.to raise_error ArgumentError
50
+ end
51
+ end
52
+
53
+ context 'when passed a key, "alias:sf"' do
54
+ before do
55
+ allow(LitaForecast.redis).to receive(:del).and_return(nil)
56
+ end
57
+ it 'should call LitaForecast.redis.del to delete the key' do
58
+ key = 'alias:sf'
59
+ expect(LitaForecast.redis).to receive(:del).with(key).once
60
+ expect(subject.send(:rm_from_cache, key)).to be_nil
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '.add_to_cache' do
66
+ context 'when passed more than one arg' do
67
+ it 'should raise ArgumentError' do
68
+ expect do
69
+ subject.send(:add_to_cache, nil, nil)
70
+ end.to raise_error ArgumentError
71
+ end
72
+ end
73
+
74
+ context 'when passed less than one arg' do
75
+ it 'should raise ArgumentError' do
76
+ expect do
77
+ subject.send(:add_to_cache)
78
+ end.to raise_error ArgumentError
79
+ end
80
+ end
81
+
82
+ context 'when passed a location' do
83
+ before do
84
+ @l_h = { name: 'sf', desc: 'sf', lat: 37.7830503, lng: -122.3933962 }
85
+ allow(LitaForecast.redis).to receive(:hset).and_return(nil)
86
+ allow(LitaForecast.redis).to receive(:pipelined).and_yield
87
+ end
88
+
89
+ it 'should add the keys to the cache' do
90
+ expect(LitaForecast.redis).to receive(:hset)
91
+ .with('alias:sf', 'lat', 37.7830503).once
92
+ expect(LitaForecast.redis).to receive(:hset)
93
+ .with('alias:sf', 'lng', -122.3933962).once
94
+ expect(LitaForecast.redis).to receive(:hset)
95
+ .with('alias:sf', 'desc', 'sf').once
96
+ subject.send(:add_to_cache, @l_h)
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '.already_exists?' do
102
+ context 'when given more than one arg' do
103
+ it 'should raise ArgumentError' do
104
+ expect do
105
+ subject.send(:alias_exists?, nil, nil)
106
+ end.to raise_error ArgumentError
107
+ end
108
+ end
109
+
110
+ context 'when given less than one arg' do
111
+ it 'should raise ArgumentError' do
112
+ expect do
113
+ subject.send(:alias_exists?)
114
+ end.to raise_error ArgumentError
115
+ end
116
+ end
117
+
118
+ context 'when key exists' do
119
+ before do
120
+ @h = { desc: 'sf', lat: 37.7830503, lng: -122.3933962 }
121
+ @key = 'alias:sf'
122
+ allow(LitaForecast.redis).to receive(:hgetall).with(@key)
123
+ .and_return(@h)
124
+ end
125
+
126
+ it 'should return true' do
127
+ expect(subject.send(:alias_exists?, @key)).to be_truthy
128
+ end
129
+ end
130
+
131
+ context 'when key does not exist' do
132
+ before do
133
+ @key = 'alias:toronto' # sorry Canadia
134
+ allow(LitaForecast.redis).to receive(:hgetall).with(@key)
135
+ .and_return({})
136
+ end
137
+
138
+ it 'should return true' do
139
+ expect(subject.send(:alias_exists?, @key)).to be_falsey
140
+ end
141
+ end
142
+ end
143
+
144
+ describe '.locations' do
145
+ context 'when given more than one arg' do
146
+ it 'should raise ArgumentError' do
147
+ expect do
148
+ subject.locations(nil, nil)
149
+ end.to raise_error ArgumentError
150
+ end
151
+ end
152
+
153
+ context 'when given more than one arg' do
154
+ it 'should raise ArgumentError' do
155
+ expect do
156
+ subject.locations
157
+ end.to raise_error ArgumentError
158
+ end
159
+ end
160
+
161
+ context 'when aliases exist' do
162
+ before do
163
+ allow(LitaForecast.redis).to receive(:keys).with('alias:*')
164
+ .and_return(%w(alias:sf alias:toronto))
165
+ end
166
+
167
+ it 'should return a list of aliases' do
168
+ send_command('wl')
169
+ expect(replies.last).to eql 'Known weather aliases: sf, toronto'
170
+ end
171
+ end
172
+
173
+ context 'when aliases exist' do
174
+ before do
175
+ allow(LitaForecast.redis).to receive(:keys).with('alias:*')
176
+ .and_return([])
177
+ end
178
+
179
+ it 'should return a list of aliases' do
180
+ send_command('wl')
181
+ expect(replies.last).to eql 'There are no weather aliases'
182
+ end
183
+ end
184
+ end
185
+
186
+ describe '.location_rm' do
187
+ context 'when given more than one arg' do
188
+ it 'should raise ArgumentError' do
189
+ expect do
190
+ subject.location_rm(nil, nil)
191
+ end.to raise_error ArgumentError
192
+ end
193
+ end
194
+
195
+ context 'when given less than one arg' do
196
+ it 'should raise ArgumentError' do
197
+ expect do
198
+ subject.location_rm
199
+ end
200
+ end
201
+ end
202
+
203
+ context 'when the alias does not exist' do
204
+ before do
205
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
206
+ .to receive(:alias_exists?).with('alias:sf').and_return(false)
207
+ end
208
+
209
+ it 'should return that there was no alias' do
210
+ send_command('wrm sf')
211
+ expect(replies.last).to eql 'Alias not found'
212
+ end
213
+ end
214
+
215
+ context 'when the alias exists and is removed' do
216
+ before do
217
+ key = 'alias:sf'
218
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
219
+ .to receive(:alias_exists?).with(key).and_return(true)
220
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
221
+ .to receive(:rm_from_cache).with(key).and_return(nil)
222
+ allow(LitaForecast.redis).to receive(:keys).with(key)
223
+ .and_return([])
224
+ end
225
+
226
+ it 'should return that there was no alias' do
227
+ send_command('wrm sf')
228
+ expect(replies.last).to eql 'Alias removed!'
229
+ end
230
+ end
231
+
232
+ context 'when the alias exists and fails to remove' do
233
+ before do
234
+ key = 'alias:sf'
235
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
236
+ .to receive(:alias_exists?).with(key).and_return(true)
237
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
238
+ .to receive(:rm_from_cache).with(key).and_return(nil)
239
+ allow(LitaForecast.redis).to receive(:keys).with(key)
240
+ .and_return(['alias:sf'])
241
+ end
242
+
243
+ it 'should return that there was no alias' do
244
+ send_command('wrm sf')
245
+ expect(replies.last).to eql 'Somehow that failed... I need an adult!'
246
+ end
247
+ end
248
+ end
249
+
250
+ describe '.location_add' do
251
+ context 'when given more than one arg' do
252
+ it 'should raise ArgumentError' do
253
+ expect do
254
+ subject.location_add(nil, nil)
255
+ end.to raise_error ArgumentError
256
+ end
257
+ end
258
+
259
+ context 'when given less than one arg' do
260
+ it 'should raise ArgumentError' do
261
+ expect do
262
+ subject.location_add
263
+ end.to raise_error ArgumentError
264
+ end
265
+ end
266
+
267
+ context 'when the alias already exists' do
268
+ before do
269
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
270
+ .to receive(:alias_exists?).and_return(true)
271
+ end
272
+
273
+ it 'should return "Already there!"' do
274
+ send_command('wadd sf 37.7830503 -122.3933962')
275
+ expect(replies.last).to eql 'Already there!'
276
+ end
277
+ end
278
+
279
+ context 'when alias does not exist' do
280
+ before do
281
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
282
+ .to receive(:alias_exists?).and_return(false)
283
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
284
+ .to receive(:saved_location_hash).and_return(nil)
285
+ allow_any_instance_of(Lita::Handlers::ForecastLocations)
286
+ .to receive(:add_to_cache).and_return(nil)
287
+ end
288
+
289
+ it 'should return that it was added to the cache' do
290
+ send_command('wadd sf 37.7830503 -122.3933962')
291
+ expect(replies.last)
292
+ .to eql 'sf added to the cache at [37.7830503, -122.3933962]'
293
+ end
294
+ end
295
+ end
296
+ end