lita 3.3.1 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +3 -0
  4. data/lib/lita.rb +45 -97
  5. data/lib/lita/adapter.rb +38 -17
  6. data/lib/lita/adapters/shell.rb +5 -3
  7. data/lib/lita/authorization.rb +109 -60
  8. data/lib/lita/builder.rb +38 -0
  9. data/lib/lita/callback.rb +37 -0
  10. data/lib/lita/cli.rb +2 -0
  11. data/lib/lita/config.rb +1 -18
  12. data/lib/lita/configurable.rb +29 -0
  13. data/lib/lita/configuration.rb +179 -0
  14. data/lib/lita/configuration_validator.rb +66 -0
  15. data/lib/lita/daemon.rb +4 -11
  16. data/lib/lita/default_configuration.rb +146 -0
  17. data/lib/lita/errors.rb +9 -0
  18. data/lib/lita/handler.rb +5 -264
  19. data/lib/lita/handler/chat_router.rb +130 -0
  20. data/lib/lita/handler/common.rb +114 -0
  21. data/lib/lita/handler/event_router.rb +77 -0
  22. data/lib/lita/handler/http_router.rb +26 -0
  23. data/lib/lita/handlers/authorization.rb +13 -18
  24. data/lib/lita/handlers/deprecation_check.rb +24 -0
  25. data/lib/lita/handlers/help.rb +5 -3
  26. data/lib/lita/handlers/info.rb +2 -2
  27. data/lib/lita/http_callback.rb +29 -0
  28. data/lib/lita/http_route.rb +41 -26
  29. data/lib/lita/namespace.rb +23 -0
  30. data/lib/lita/rack_app.rb +29 -2
  31. data/lib/lita/registry.rb +133 -0
  32. data/lib/lita/robot.rb +58 -20
  33. data/lib/lita/route_validator.rb +12 -4
  34. data/lib/lita/rspec.rb +23 -14
  35. data/lib/lita/rspec/handler.rb +93 -23
  36. data/lib/lita/rspec/matchers/chat_route_matcher.rb +48 -0
  37. data/lib/lita/rspec/matchers/deprecated.rb +36 -0
  38. data/lib/lita/rspec/matchers/event_route_matcher.rb +27 -0
  39. data/lib/lita/rspec/matchers/http_route_matcher.rb +18 -60
  40. data/lib/lita/user.rb +0 -6
  41. data/lib/lita/util.rb +1 -8
  42. data/lib/lita/version.rb +1 -1
  43. data/lita.gemspec +1 -0
  44. data/spec/lita/adapter_spec.rb +25 -7
  45. data/spec/lita/adapters/shell_spec.rb +24 -4
  46. data/spec/lita/authorization_spec.rb +57 -38
  47. data/spec/lita/builder_spec.rb +39 -0
  48. data/spec/lita/config_spec.rb +0 -24
  49. data/spec/lita/configuration_spec.rb +222 -0
  50. data/spec/lita/configuration_validator_spec.rb +112 -0
  51. data/spec/lita/daemon_spec.rb +2 -2
  52. data/spec/lita/default_configuration_spec.rb +254 -0
  53. data/spec/lita/handler/chat_router_spec.rb +192 -0
  54. data/spec/lita/handler/common_spec.rb +272 -0
  55. data/spec/lita/handler/event_router_spec.rb +54 -0
  56. data/spec/lita/handler/http_router_spec.rb +106 -0
  57. data/spec/lita/handler_spec.rb +20 -291
  58. data/spec/lita/handlers/authorization_spec.rb +9 -11
  59. data/spec/lita/handlers/deprecation_check_spec.rb +21 -0
  60. data/spec/lita/handlers/help_spec.rb +31 -9
  61. data/spec/lita/handlers/info_spec.rb +2 -2
  62. data/spec/lita/handlers/room_spec.rb +5 -3
  63. data/spec/lita/robot_spec.rb +30 -11
  64. data/spec/lita/rspec_spec.rb +71 -31
  65. data/spec/lita/user_spec.rb +2 -2
  66. data/spec/lita_spec.rb +62 -4
  67. data/spec/spec_helper.rb +7 -0
  68. data/templates/locales/en.yml +56 -4
  69. data/templates/plugin/gemspec.tt +1 -0
  70. data/templates/plugin/spec/spec_helper.tt +4 -0
  71. metadata +54 -8
  72. data/lib/lita/rspec/matchers/event_subscription_matcher.rb +0 -67
  73. data/lib/lita/rspec/matchers/route_matcher.rb +0 -69
  74. data/spec/lita/rack_app_spec.rb +0 -92
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handler::EventRouter do
4
+ let(:robot) { instance_double("Lita::Robot", name: "Lita") }
5
+
6
+ subject do
7
+ Class.new do
8
+ extend Lita::Handler::EventRouter
9
+
10
+ def self.name
11
+ "Test"
12
+ end
13
+
14
+ on :connected, :greet
15
+
16
+ def greet(payload)
17
+ robot.send_message("Hi, #{payload[:name]}! Lita has started!")
18
+ end
19
+
20
+ on :block_test do |payload|
21
+ robot.send_message("#{payload[:data]} received via block!")
22
+ end
23
+
24
+ on :callable_test, lambda { |payload|
25
+ robot.send_message("#{payload[:data]} received via callable!")
26
+ }
27
+ end
28
+ end
29
+
30
+ describe ".trigger" do
31
+ it "invokes methods registered with .on and passes an arbitrary payload" do
32
+ expect(robot).to receive(:send_message).with(
33
+ "Hi, Carl! Lita has started!"
34
+ )
35
+ subject.trigger(robot, :connected, name: "Carl")
36
+ end
37
+
38
+ it "calls blocks that were passed to .on" do
39
+ expect(robot).to receive(:send_message).with("Data received via block!")
40
+ subject.trigger(robot, :block_test, data: "Data")
41
+ end
42
+
43
+ it "calls arbitrary callables that were passed to .on" do
44
+ expect(robot).to receive(:send_message).with("Data received via callable!")
45
+ subject.trigger(robot, :callable_test, data: "Data")
46
+ end
47
+
48
+ it "normalizes the event name" do
49
+ expect(robot).to receive(:send_message).twice
50
+ subject.trigger(robot, "connected")
51
+ subject.trigger(robot, " ConNected ")
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,106 @@
1
+ require "spec_helper"
2
+
3
+ handler = Class.new do
4
+ extend Lita::Handler::HTTPRouter
5
+
6
+ namespace "test"
7
+
8
+ http.get "web", :web
9
+ http.post "path/with/:id", :variable
10
+ http.link "foo", :foo
11
+ http.get "heres/*a/glob/in/a/path", :glob
12
+ http.get ":var/otherwise/identical/path", :constraint, var: /\d+/
13
+ http.get ":var/otherwise/identical/path", :no_constraint
14
+ http.get("block") { |_request, response| response.write("block") }
15
+ http.get "middleware", :middleware
16
+
17
+ def web(_request, response)
18
+ response.write("it worked")
19
+ end
20
+
21
+ def variable(request, response)
22
+ id = request.env["router.params"][:id]
23
+ response.write("id is #{id}")
24
+ end
25
+
26
+ def glob(request, response)
27
+ segments = request.env["router.params"][:a]
28
+ response.write(segments.join("/"))
29
+ end
30
+
31
+ def constraint(_request, response)
32
+ response.write("constraint")
33
+ end
34
+
35
+ def no_constraint(_request, response)
36
+ response.write("no constraint")
37
+ end
38
+
39
+ def middleware(request, response)
40
+ response.write("middleware worked") if request.env["custom_rack_middleware_working"]
41
+ end
42
+ end
43
+
44
+ describe handler, lita_handler: true do
45
+ it "responds to requests for simple paths" do
46
+ response = http.get("/web")
47
+ expect(response.status).to eq(200)
48
+ expect(response.body).to eq("it worked")
49
+ end
50
+
51
+ it "responds to requests with variable paths" do
52
+ response = http.post("/path/with/some_id")
53
+ expect(response.status).to eq(200)
54
+ expect(response.body).to eq("id is some_id")
55
+ end
56
+
57
+ it "responds to requests with globs in their paths" do
58
+ response = http.get("heres/a/giant/glob/in/a/path")
59
+ expect(response.status).to eq(200)
60
+ expect(response.body).to eq("a/giant")
61
+ end
62
+
63
+ it "responds to requests with variable path constraints" do
64
+ response = http.get("/123/otherwise/identical/path")
65
+ expect(response.status).to eq(200)
66
+ expect(response.body).to eq("constraint")
67
+
68
+ response = http.get("/an/otherwise/identical/path")
69
+ expect(response.status).to eq(200)
70
+ expect(response.body).to eq("no constraint")
71
+ end
72
+
73
+ it "responds to HEAD requests for GET routes" do
74
+ response = http.head("/web")
75
+ expect(response.status).to eq(204)
76
+ expect(response.body).to be_empty
77
+ end
78
+
79
+ it "allows route callbacks to be provided as blocks" do
80
+ response = http.get("/block")
81
+ expect(response.status).to eq(200)
82
+ expect(response.body).to eq("block")
83
+ end
84
+ end
85
+
86
+ describe handler, lita_handler: true do
87
+ let(:middleware) do
88
+ Class.new do
89
+ def initialize(app)
90
+ @app = app
91
+ end
92
+
93
+ def call(env)
94
+ env["custom_rack_middleware_working"] = true
95
+ @app.call(env)
96
+ end
97
+ end
98
+ end
99
+
100
+ prepend_before { registry.config.http.middleware.push(middleware) }
101
+
102
+ it "uses any custom middlewares registered" do
103
+ response = http.get("/middleware")
104
+ expect(response.body).to eq("middleware worked")
105
+ end
106
+ end
@@ -1,311 +1,40 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Lita::Handler, lita: true do
4
- let(:robot) { instance_double("Lita::Robot", name: "Lita") }
5
- let(:user) { instance_double("Lita::User", name: "Test User") }
3
+ describe Lita::Handler, lita_handler: true do
4
+ before { registry.handlers.delete(described_class) }
6
5
 
7
- let(:message) do
8
- message = instance_double("Lita::Message", user: user, command?: false)
9
- allow(message).to receive(:match)
10
- message
11
- end
12
-
13
- let(:queue) { Queue.new }
14
-
15
- let(:guard_hook) do
16
- Class.new do
17
- def self.call(payload)
18
- if payload[:route].extensions[:guard]
19
- payload[:message].body.include?("code word")
20
- else
21
- true
22
- end
23
- end
24
- end
25
- end
26
-
27
- let(:response_hook) do
28
- Class.new do
29
- def self.call(payload)
30
- payload[:response].extensions[:data] = payload[:route].extensions[:data]
31
- end
32
- end
33
- end
34
-
35
- let(:handler_class) do
36
- Class.new(described_class) do
37
- route(/\w{3}/, :foo)
38
- route(/\w{4}/, :blah, command: true)
39
- route(/secret/, :secret, restrict_to: :admins)
40
- route(/danger/, :danger)
41
- route(/guard/, :guard, guard: true)
42
- route(/trigger route hook/, :trigger_route_hook, data: :foo)
43
-
44
- on :connected, :greet
45
- on :some_hook, :test_payload
46
-
47
- def self.default_config(config)
48
- config.foo = "bar"
49
- end
50
-
51
- def foo(_response)
52
- end
53
-
54
- def blah(_response)
55
- end
56
-
57
- def secret(_response)
58
- end
59
-
60
- def danger(_response)
61
- raise "The developer of this handler's got a bug in their code!"
62
- end
63
-
64
- def guard(_response)
65
- end
66
-
67
- def trigger_route_hook(_response)
68
- end
69
-
70
- def greet(payload)
71
- robot.send_message("Hi, #{payload[:name]}! Lita has started!")
72
- end
73
-
74
- def after_test(_response, queue)
75
- after(2) { queue.push("Waited 2 seconds!") }
76
- end
77
-
78
- def every_test(_response, queue)
79
- array = [1, 2, 3]
80
-
81
- every(2) do |timer|
82
- value = array.shift
83
-
84
- if value
85
- queue.push(value)
86
- else
87
- timer.stop
88
- end
89
- end
90
- end
91
-
92
- def infinite_every_test(response)
93
- thread = every(5) { "Looping forever!" }
94
- response.reply("Replying after timer!")
95
- thread
96
- end
97
-
98
- def self.name
99
- "Lita::Handlers::Test"
100
- end
101
- end
102
- end
103
-
104
- subject { described_class.new(robot) }
105
-
106
- describe ".dispatch" do
107
- it "routes a matching message to the supplied method" do
108
- allow(message).to receive(:body).and_return("bar")
109
- expect_any_instance_of(handler_class).to receive(:foo)
110
- handler_class.dispatch(robot, message)
111
- end
112
-
113
- it "routes a matching message even if addressed to the Robot" do
114
- allow(message).to receive(:body).and_return("#{robot.name}: bar")
115
- allow(message).to receive(:command?).and_return(true)
116
- expect_any_instance_of(handler_class).to receive(:foo)
117
- handler_class.dispatch(robot, message)
118
- end
119
-
120
- it "routes a command message to the supplied method" do
121
- allow(message).to receive(:body).and_return("#{robot.name}: bar")
122
- allow(message).to receive(:command?).and_return(true)
123
- expect_any_instance_of(handler_class).to receive(:blah)
124
- handler_class.dispatch(robot, message)
125
- end
126
-
127
- it "requires command routes to be addressed to the Robot" do
128
- allow(message).to receive(:body).and_return("blah")
129
- expect_any_instance_of(handler_class).not_to receive(:blah)
130
- handler_class.dispatch(robot, message)
131
- end
132
-
133
- it "doesn't route messages that don't match anything" do
134
- allow(message).to receive(:body).and_return("yo")
135
- expect_any_instance_of(handler_class).not_to receive(:foo)
136
- expect_any_instance_of(handler_class).not_to receive(:blah)
137
- handler_class.dispatch(robot, message)
138
- end
139
-
140
- it "dispatches to restricted routes if the user is in the auth group" do
141
- allow(message).to receive(:body).and_return("secret")
142
- allow(Lita::Authorization).to receive(:user_in_group?).and_return(true)
143
- expect_any_instance_of(handler_class).to receive(:secret)
144
- handler_class.dispatch(robot, message)
145
- end
146
-
147
- it "doesn't route unauthorized users' messages to restricted routes" do
148
- allow(message).to receive(:body).and_return("secret")
149
- allow(Lita::Authorization).to receive(:user_in_group?).and_return(false)
150
- expect_any_instance_of(handler_class).not_to receive(:secret)
151
- handler_class.dispatch(robot, message)
152
- end
153
-
154
- it "doesn't route messages from the bot back to the bot" do
155
- allow(message).to receive(:body).and_return("#{robot.name}: bar")
156
- allow(message).to receive(:command?).and_return(true)
157
- allow(message).to receive(:user).and_return(robot)
158
- expect_any_instance_of(handler_class).not_to receive(:blah)
159
- handler_class.dispatch(robot, message)
160
- end
161
-
162
- it "logs exceptions but doesn't crash the bot" do
163
- allow(message).to receive(:body).and_return("#{robot.name}: danger")
164
- allow(handler_class).to receive(:rspec_loaded?).and_return(false)
165
- expect(Lita.logger).to receive(:error).with(/Lita::Handlers::Test crashed/)
166
- expect { handler_class.dispatch(robot, message) }.not_to raise_error
167
- end
168
-
169
- it "re-raises exceptions when testing with RSpec" do
170
- allow(message).to receive(:body).and_return("#{robot.name}: danger")
171
- expect { handler_class.dispatch(robot, message) }.to raise_error
172
- end
173
-
174
- context "with a custom validate_route hook" do
175
- before { Lita.register_hook(:validate_route, guard_hook) }
176
- after { Lita.reset_hooks }
177
-
178
- it "matches if the hook returns true" do
179
- allow(message).to receive(:body).and_return("guard code word")
180
- expect_any_instance_of(handler_class).to receive(:guard)
181
- handler_class.dispatch(robot, message)
182
- end
183
-
184
- it "does not match if the hook returns false" do
185
- allow(message).to receive(:body).and_return("guard")
186
- expect_any_instance_of(handler_class).not_to receive(:guard)
187
- handler_class.dispatch(robot, message)
6
+ it "includes chat routes" do
7
+ registry.register_handler(:foo) do
8
+ route(/foo/) do |response|
9
+ response.reply("bar")
188
10
  end
189
11
  end
190
12
 
191
- context "with a custom trigger_route hook" do
192
- before { Lita.register_hook(:trigger_route, response_hook) }
193
- after { Lita.reset_hooks }
13
+ send_message("foo")
194
14
 
195
- it "adds data to the response's extensions" do
196
- allow(message).to receive(:body).and_return("trigger route hook")
197
- allow_any_instance_of(handler_class).to receive(:trigger_route_hook) do |_robot, response|
198
- expect(response.extensions[:data]).to eq(:foo)
199
- end
200
- handler_class.dispatch(robot, message)
201
- end
202
- end
15
+ expect(replies.last).to include("bar")
203
16
  end
204
17
 
205
- describe ".namespace" do
206
- it "provides a snake cased namespace for the handler" do
207
- handler_class = Class.new(described_class) do
208
- def self.name
209
- "Lita::Handlers::FooBarBaz"
210
- end
18
+ it "includes HTTP routes" do
19
+ registry.register_handler(:foo) do
20
+ http.get "foo" do |_request, response|
21
+ response.write("bar")
211
22
  end
212
- expect(handler_class.namespace).to eq("foo_bar_baz")
213
- end
214
-
215
- it "raises an exception if the handler doesn't define self.name" do
216
- handler_class = Class.new(described_class)
217
- expect { handler_class.namespace }.to raise_error
218
23
  end
219
- end
220
24
 
221
- describe ".trigger" do
222
- it "invokes methods registered with .on and passes an arbitrary payload" do
223
- expect(robot).to receive(:send_message).with(
224
- "Hi, Carl! Lita has started!"
225
- )
226
- handler_class.trigger(robot, :connected, name: "Carl")
227
- end
25
+ http_client = Faraday::Connection.new { |c| c.adapter(:rack, Lita::RackApp.new(robot)) }
26
+ response = http_client.get("/foo")
228
27
 
229
- it "normalizes the event name" do
230
- expect(robot).to receive(:send_message).twice
231
- handler_class.trigger(robot, "connected")
232
- handler_class.trigger(robot, " ConNected ")
233
- end
28
+ expect(response.body).to eq("bar")
234
29
  end
235
30
 
236
- describe "#config" do
237
- before { Lita.register_handler(handler_class) }
238
- subject { handler_class.new(robot) }
239
-
240
- it "returns a Lita config" do
241
- expect(subject.config).to be_a(Lita::Config)
31
+ it "includes event routes" do
32
+ registry.register_handler(:foo) do
33
+ on(:some_event) { robot.send_message("payload received") }
242
34
  end
243
35
 
244
- it "contains the handler's config settings" do
245
- expect(subject.config.foo).to eq("bar")
246
- end
247
- end
248
-
249
- describe "#http" do
250
- it "returns a Faraday connection" do
251
- expect(subject.http).to be_a(Faraday::Connection)
252
- end
253
-
254
- it "sets a default user agent" do
255
- expect(subject.http.headers["User-Agent"]).to eq("Lita v#{Lita::VERSION}")
256
- end
257
-
258
- it "merges in user-supplied options" do
259
- connection = subject.http(headers: {
260
- "User-Agent" => "Foo", "X-Bar" => "Baz"
261
- })
262
- expect(connection.headers["User-Agent"]).to eq("Foo")
263
- expect(connection.headers["X-Bar"]).to eq("Baz")
264
- end
265
-
266
- it "passes blocks on to Faraday" do
267
- connection = subject.http { |builder| builder.response :logger }
268
- expect(connection.builder.handlers).to include(Faraday::Response::Logger)
269
- end
270
- end
271
-
272
- describe "#log" do
273
- it "returns the Lita logger" do
274
- expect(subject.log).to eq(Lita.logger)
275
- end
276
- end
277
-
278
- describe "timer methods" do
279
- let(:response) { instance_double("Lita::Response") }
280
-
281
- subject { handler_class.new(robot) }
282
-
283
- before { allow_any_instance_of(Lita::Timer).to receive(:sleep) }
284
-
285
- describe "#after" do
286
- it "triggers the block after the given number of seconds" do
287
- subject.after_test(response, queue)
288
- expect(queue.pop).to eq("Waited 2 seconds!")
289
- expect { queue.pop(true) }.to raise_error(ThreadError)
290
- end
291
- end
292
-
293
- describe "#every" do
294
- it "triggers the block until the timer is stopped" do
295
- subject.every_test(response, queue)
296
- expect(queue.pop).to eq(1)
297
- expect(queue.pop).to eq(2)
298
- expect(queue.pop).to eq(3)
299
- expect { queue.pop(true) }.to raise_error(ThreadError)
300
- end
301
- end
36
+ expect(robot).to receive(:send_message).with("payload received")
302
37
 
303
- context "with an infinite timer" do
304
- it "doesn't block the handler's thread" do
305
- expect(response).to receive(:reply)
306
- thread = subject.infinite_every_test(response)
307
- thread.kill
308
- end
309
- end
38
+ robot.trigger(:some_event)
310
39
  end
311
40
  end