lita 3.3.1 → 4.0.0.rc1

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.
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,192 @@
1
+ require "spec_helper"
2
+
3
+ handler = Class.new do
4
+ extend Lita::Handler::ChatRouter
5
+
6
+ def self.name
7
+ "Test"
8
+ end
9
+
10
+ route(/message/, :message)
11
+ route(/command/, :command, command: true)
12
+ route(/admin/, :admin, restrict_to: :admins)
13
+ route(/error/, :error)
14
+ route(/validate route hook/, :validate_route_hook, code_word: true)
15
+ route(/trigger route hook/, :trigger_route_hook, custom_data: "trigger route hook")
16
+
17
+ def message(response)
18
+ response.reply("message")
19
+ end
20
+
21
+ def command(response)
22
+ response.reply("command")
23
+ end
24
+
25
+ def admin(response)
26
+ response.reply("admin")
27
+ end
28
+
29
+ def error(_response)
30
+ raise
31
+ end
32
+
33
+ def validate_route_hook(response)
34
+ response.reply("validate route hook")
35
+ end
36
+
37
+ def trigger_route_hook(response)
38
+ response.reply(response.extensions[:custom_data])
39
+ end
40
+
41
+ route(/block/) do |response|
42
+ response.reply("block")
43
+ end
44
+ end
45
+
46
+ describe handler, lita_handler: true do
47
+ describe ".dispatch" do
48
+ it "routes a matching message to the supplied method" do
49
+ send_message("message")
50
+ expect(replies.last).to eq("message")
51
+ end
52
+
53
+ it "routes a matching message even if addressed to the robot" do
54
+ send_command("message")
55
+ expect(replies.last).to eq("message")
56
+ end
57
+
58
+ it "routes a command message to the supplied method" do
59
+ send_command("command")
60
+ expect(replies.last).to eq("command")
61
+ end
62
+
63
+ it "requires command routes to be addressed to the robot" do
64
+ send_message("command")
65
+ expect(replies).to be_empty
66
+ end
67
+
68
+ it "doesn't route messages that don't match anything" do
69
+ send_message("nothing")
70
+ expect(replies).to be_empty
71
+ end
72
+
73
+ it "dispatches to restricted routes if the user is in the auth group" do
74
+ allow(robot.auth).to receive(:user_is_admin?).with(user).and_return(true)
75
+ send_message("admin")
76
+ expect(replies.last).to eq("admin")
77
+ end
78
+
79
+ it "doesn't route unauthorized users' messages to restricted routes" do
80
+ send_message("admin")
81
+ expect(replies).to be_empty
82
+ end
83
+
84
+ it "ignores messages from itself" do
85
+ allow(user).to receive(:name).and_return(robot.name)
86
+ send_message("message")
87
+ expect(replies).to be_empty
88
+ end
89
+
90
+ it "allows route callbacks to be provided as blocks" do
91
+ send_message("block")
92
+ expect(replies.last).to eq("block")
93
+ end
94
+
95
+ it "logs exceptions without crashing" do
96
+ test_mode = Lita.test_mode?
97
+
98
+ begin
99
+ Lita.test_mode = false
100
+ expect(Lita.logger).to receive(:error).with(/Test crashed/)
101
+ send_message("error")
102
+ ensure
103
+ Lita.test_mode = test_mode
104
+ end
105
+ end
106
+
107
+ it "raises exceptions in test mode" do
108
+ expect { send_message("error") }.to raise_error(RuntimeError)
109
+ end
110
+
111
+ context "with a custom validate_route hook" do
112
+ let(:hook) do
113
+ proc do |payload|
114
+ if payload[:route].extensions[:code_word]
115
+ payload[:message].body.include?("code word")
116
+ else
117
+ true
118
+ end
119
+ end
120
+ end
121
+
122
+ before { registry.register_hook(:validate_route, hook) }
123
+
124
+ it "matches if the hook returns true" do
125
+ send_message("validate route hook - code word")
126
+ expect(replies.last).to eq("validate route hook")
127
+ end
128
+
129
+ it "does not match if the hook returns false" do
130
+ send_message("validate route hook")
131
+ expect(replies).to be_empty
132
+ end
133
+ end
134
+
135
+ context "with a custom trigger_route hook" do
136
+ let(:hook) do
137
+ proc do |payload|
138
+ payload[:response].extensions[:custom_data] = payload[:route].extensions[:custom_data]
139
+ end
140
+ end
141
+
142
+ before { registry.register_hook(:trigger_route, hook) }
143
+
144
+ it "adds data to the response's extensions" do
145
+ send_message("trigger route hook")
146
+ expect(replies.last).to eq("trigger route hook")
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ handler = Class.new do
153
+ extend Lita::Handler::ChatRouter
154
+ extend Lita::Handler::EventRouter
155
+
156
+ def self.name
157
+ "Test"
158
+ end
159
+
160
+ route(/one/) { |response| response.reply "got one" }
161
+ route(/two/) { |response| response.reply "got two" }
162
+
163
+ on :unhandled_message do |payload|
164
+ message = payload[:message]
165
+ robot.send_message(message.source, message.body)
166
+ end
167
+ end
168
+
169
+ describe handler, lita_handler: true do
170
+ it "triggers the unhandled message event if no route matches" do
171
+ send_message("this won't match any routes")
172
+ expect(replies.last).to eq("this won't match any routes")
173
+ end
174
+
175
+ it "doesn't stop checking routes when the first one matches" do
176
+ send_message("one two")
177
+ expect(replies.last).to eq("got two")
178
+ end
179
+
180
+ context "with another handler registered" do
181
+ before do
182
+ registry.register_handler(:test_2) do
183
+ route(/three/) { |response| response.reply "got three" }
184
+ end
185
+ end
186
+
187
+ it "doesn't stop dispatching to handlers when there is a matching route in one" do
188
+ send_message("two three")
189
+ expect(replies.last).to eq("got three")
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,272 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handler::Common, lita: true do
4
+ let(:robot) { Lita::Robot.new(registry) }
5
+
6
+ let(:handler) do
7
+ Class.new do
8
+ include Lita::Handler::Common
9
+
10
+ namespace "foo"
11
+ end
12
+ end
13
+
14
+ subject { handler.new(robot) }
15
+
16
+ describe ".config" do
17
+ it "sets configuration attributes" do
18
+ handler.config :foo
19
+
20
+ config = handler.configuration.finalize
21
+
22
+ expect(config.foo).to be_nil
23
+ config.foo = :bar
24
+ expect(config.foo).to eq(:bar)
25
+ end
26
+ end
27
+
28
+ describe ".configuration" do
29
+ it "returns a Configuration object" do
30
+ expect(handler.configuration).to be_a(Lita::Configuration)
31
+ end
32
+
33
+ it "is memoized" do
34
+ expect(handler.configuration).to equal(handler.configuration)
35
+ end
36
+ end
37
+
38
+ describe ".namespace" do
39
+ it "returns a snake cased namesapce for the handler based on class name" do
40
+ handler = Class.new do
41
+ include Lita::Handler::Common
42
+
43
+ def self.name
44
+ "Lita::Handlers::FooBarBaz"
45
+ end
46
+ end
47
+
48
+ expect(handler.namespace).to eq("foo_bar_baz")
49
+ end
50
+
51
+ it "allows the namespace to be set with a simple string" do
52
+ handler = Class.new do
53
+ include Lita::Handler::Common
54
+
55
+ namespace "common"
56
+ end
57
+
58
+ expect(handler.namespace).to eq("common")
59
+ end
60
+
61
+ it "allows the namespace to be set with the full path to an object as a string" do
62
+ handler = Class.new do
63
+ include Lita::Handler::Common
64
+
65
+ namespace "Lita::Handler::Common"
66
+ end
67
+
68
+ expect(handler.namespace).to eq("common")
69
+ end
70
+
71
+ it "allows the namespace to be set with an object" do
72
+ handler = Class.new do
73
+ include Lita::Handler::Common
74
+
75
+ namespace Lita::Handler::Common
76
+ end
77
+
78
+ expect(handler.namespace).to eq("common")
79
+ end
80
+
81
+ it "raises an exception if the handler doesn't have a name to derive the namespace from" do
82
+ handler = Class.new { include Lita::Handler::Common }
83
+ expect { handler.namespace }.to raise_error
84
+ end
85
+ end
86
+
87
+ describe "#config" do
88
+ before { registry.register_handler(handler) }
89
+
90
+ context "with old-style config" do
91
+ let(:handler) do
92
+ Class.new do
93
+ include Lita::Handler::Common
94
+
95
+ namespace "foo_bar_baz"
96
+
97
+ def self.default_config(config)
98
+ config.style = :old
99
+ end
100
+ end
101
+ end
102
+
103
+ it "returns the handler's config settings" do
104
+ expect(subject.config.style).to eq(:old)
105
+ end
106
+ end
107
+
108
+ context "with new-style config" do
109
+ let(:handler) do
110
+ Class.new do
111
+ include Lita::Handler::Common
112
+
113
+ namespace "foo_bar_baz"
114
+
115
+ config :style, default: :new
116
+ end
117
+ end
118
+
119
+ it "returns the handler's config settings" do
120
+ expect(subject.config.style).to eq(:new)
121
+ end
122
+ end
123
+
124
+ context "with both types of configuration" do
125
+ let(:handler) do
126
+ Class.new do
127
+ include Lita::Handler::Common
128
+
129
+ namespace "foo_bar_baz"
130
+
131
+ config :style, default: :new
132
+
133
+ def self.default_config(config)
134
+ config.style = :old
135
+ end
136
+ end
137
+ end
138
+
139
+ it "gives precedence to the new style" do
140
+ expect(subject.config.style).to eq(:new)
141
+ end
142
+ end
143
+
144
+ context "with no configuration" do
145
+ let(:handler) do
146
+ Class.new do
147
+ include Lita::Handler::Common
148
+
149
+ namespace "foo_bar_baz"
150
+ end
151
+ end
152
+
153
+ it "uses an old-style config object" do
154
+ subject.config.style = :old
155
+ expect(subject.config.style).to eq(:old)
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "#http" do
161
+ it "returns a Faraday connection" do
162
+ expect(subject.http).to be_a(Faraday::Connection)
163
+ end
164
+
165
+ it "sets a default user agent" do
166
+ expect(subject.http.headers["User-Agent"]).to eq("Lita v#{Lita::VERSION}")
167
+ end
168
+
169
+ it "merges in user-supplied options" do
170
+ connection = subject.http(headers: {
171
+ "User-Agent" => "Foo", "X-Bar" => "Baz"
172
+ })
173
+ expect(connection.headers["User-Agent"]).to eq("Foo")
174
+ expect(connection.headers["X-Bar"]).to eq("Baz")
175
+ end
176
+
177
+ it "passes blocks on to Faraday" do
178
+ connection = subject.http { |builder| builder.response :logger }
179
+ expect(connection.builder.handlers).to include(Faraday::Response::Logger)
180
+ end
181
+ end
182
+
183
+ describe "#log" do
184
+ it "returns the Lita logger" do
185
+ expect(subject.log).to eq(Lita.logger)
186
+ end
187
+ end
188
+
189
+ describe "timer methods" do
190
+ let(:queue) { Queue.new }
191
+
192
+ subject { handler.new(robot) }
193
+
194
+ before { allow_any_instance_of(Lita::Timer).to receive(:sleep) }
195
+
196
+ describe "#after" do
197
+ let(:handler) do
198
+ Class.new do
199
+ include Lita::Handler::Common
200
+
201
+ namespace "foo"
202
+
203
+ def after_test(queue)
204
+ after(2) { queue.push("Waited 2 seconds!") }
205
+ end
206
+ end
207
+ end
208
+
209
+ it "triggers the block after the given number of seconds" do
210
+ subject.after_test(queue)
211
+ expect(queue.pop).to eq("Waited 2 seconds!")
212
+ expect { queue.pop(true) }.to raise_error(ThreadError)
213
+ end
214
+ end
215
+
216
+ describe "#every" do
217
+ let(:handler) do
218
+ Class.new do
219
+ include Lita::Handler::Common
220
+
221
+ namespace "foo"
222
+
223
+ def every_test(queue)
224
+ array = [1, 2, 3]
225
+
226
+ every(2) do |timer|
227
+ value = array.shift
228
+
229
+ if value
230
+ queue.push(value)
231
+ else
232
+ timer.stop
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ it "triggers the block until the timer is stopped" do
240
+ subject.every_test(queue)
241
+ expect(queue.pop).to eq(1)
242
+ expect(queue.pop).to eq(2)
243
+ expect(queue.pop).to eq(3)
244
+ expect { queue.pop(true) }.to raise_error(ThreadError)
245
+ end
246
+ end
247
+
248
+ context "with an infinite timer" do
249
+ let(:response) { instance_double("Lita::Response") }
250
+
251
+ let(:handler) do
252
+ Class.new do
253
+ include Lita::Handler::Common
254
+
255
+ namespace "foo"
256
+
257
+ def infinite_every_test(response)
258
+ thread = every(5) { "Looping forever!" }
259
+ response.reply("Replying after timer!")
260
+ thread
261
+ end
262
+ end
263
+ end
264
+
265
+ it "doesn't block the handler's thread" do
266
+ expect(response).to receive(:reply)
267
+ thread = subject.infinite_every_test(response)
268
+ thread.kill
269
+ end
270
+ end
271
+ end
272
+ end