rita 0.1.0 → 5.0.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +51 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +16 -0
  6. data/CONTRIBUTING.md +18 -0
  7. data/Gemfile +5 -0
  8. data/{LICENSE.txt → LICENSE} +1 -3
  9. data/README.md +17 -24
  10. data/Rakefile +5 -5
  11. data/bin/lita +7 -0
  12. data/lib/lita/adapter.rb +147 -0
  13. data/lib/lita/adapters/shell.rb +126 -0
  14. data/lib/lita/adapters/test.rb +62 -0
  15. data/lib/lita/authorization.rb +112 -0
  16. data/lib/lita/callback.rb +39 -0
  17. data/lib/lita/cli.rb +218 -0
  18. data/lib/lita/configurable.rb +47 -0
  19. data/lib/lita/configuration_builder.rb +247 -0
  20. data/lib/lita/configuration_validator.rb +95 -0
  21. data/lib/lita/default_configuration.rb +122 -0
  22. data/lib/lita/errors.rb +25 -0
  23. data/lib/lita/handler/chat_router.rb +141 -0
  24. data/lib/lita/handler/common.rb +208 -0
  25. data/lib/lita/handler/event_router.rb +84 -0
  26. data/lib/lita/handler/http_router.rb +31 -0
  27. data/lib/lita/handler.rb +15 -0
  28. data/lib/lita/handlers/authorization.rb +129 -0
  29. data/lib/lita/handlers/help.rb +171 -0
  30. data/lib/lita/handlers/info.rb +66 -0
  31. data/lib/lita/handlers/room.rb +36 -0
  32. data/lib/lita/handlers/users.rb +37 -0
  33. data/lib/lita/http_callback.rb +46 -0
  34. data/lib/lita/http_route.rb +83 -0
  35. data/lib/lita/logger.rb +43 -0
  36. data/lib/lita/message.rb +124 -0
  37. data/lib/lita/middleware_registry.rb +39 -0
  38. data/lib/lita/namespace.rb +29 -0
  39. data/lib/lita/plugin_builder.rb +43 -0
  40. data/lib/lita/rack_app.rb +100 -0
  41. data/lib/lita/registry.rb +164 -0
  42. data/lib/lita/response.rb +65 -0
  43. data/lib/lita/robot.rb +273 -0
  44. data/lib/lita/room.rb +119 -0
  45. data/lib/lita/route_validator.rb +82 -0
  46. data/lib/lita/rspec/handler.rb +127 -0
  47. data/lib/lita/rspec/matchers/chat_route_matcher.rb +53 -0
  48. data/lib/lita/rspec/matchers/event_route_matcher.rb +29 -0
  49. data/lib/lita/rspec/matchers/http_route_matcher.rb +34 -0
  50. data/lib/lita/rspec.rb +48 -0
  51. data/lib/lita/source.rb +81 -0
  52. data/lib/lita/store.rb +23 -0
  53. data/lib/lita/target.rb +3 -0
  54. data/lib/lita/template.rb +71 -0
  55. data/lib/lita/template_resolver.rb +52 -0
  56. data/lib/lita/timer.rb +49 -0
  57. data/lib/lita/user.rb +157 -0
  58. data/lib/lita/util.rb +31 -0
  59. data/lib/lita/version.rb +6 -0
  60. data/lib/lita.rb +166 -0
  61. data/lib/rita.rb +2 -7
  62. data/rita.gemspec +50 -0
  63. data/spec/lita/adapter_spec.rb +54 -0
  64. data/spec/lita/adapters/shell_spec.rb +99 -0
  65. data/spec/lita/authorization_spec.rb +122 -0
  66. data/spec/lita/configuration_builder_spec.rb +247 -0
  67. data/spec/lita/configuration_validator_spec.rb +114 -0
  68. data/spec/lita/default_configuration_spec.rb +242 -0
  69. data/spec/lita/handler/chat_router_spec.rb +236 -0
  70. data/spec/lita/handler/common_spec.rb +289 -0
  71. data/spec/lita/handler/event_router_spec.rb +121 -0
  72. data/spec/lita/handler/http_router_spec.rb +155 -0
  73. data/spec/lita/handler_spec.rb +62 -0
  74. data/spec/lita/handlers/authorization_spec.rb +111 -0
  75. data/spec/lita/handlers/help_spec.rb +124 -0
  76. data/spec/lita/handlers/info_spec.rb +67 -0
  77. data/spec/lita/handlers/room_spec.rb +24 -0
  78. data/spec/lita/handlers/users_spec.rb +35 -0
  79. data/spec/lita/logger_spec.rb +28 -0
  80. data/spec/lita/message_spec.rb +178 -0
  81. data/spec/lita/plugin_builder_spec.rb +41 -0
  82. data/spec/lita/response_spec.rb +62 -0
  83. data/spec/lita/robot_spec.rb +285 -0
  84. data/spec/lita/room_spec.rb +136 -0
  85. data/spec/lita/rspec/handler_spec.rb +33 -0
  86. data/spec/lita/rspec_spec.rb +162 -0
  87. data/spec/lita/source_spec.rb +68 -0
  88. data/spec/lita/store_spec.rb +23 -0
  89. data/spec/lita/template_resolver_spec.rb +42 -0
  90. data/spec/lita/template_spec.rb +52 -0
  91. data/spec/lita/timer_spec.rb +32 -0
  92. data/spec/lita/user_spec.rb +167 -0
  93. data/spec/lita/util_spec.rb +18 -0
  94. data/spec/lita_spec.rb +227 -0
  95. data/spec/spec_helper.rb +35 -0
  96. data/spec/templates/basic.erb +1 -0
  97. data/spec/templates/basic.irc.erb +1 -0
  98. data/spec/templates/helpers.erb +1 -0
  99. data/spec/templates/interpolated.erb +1 -0
  100. data/templates/locales/en.yml +137 -0
  101. data/templates/plugin/Gemfile +5 -0
  102. data/templates/plugin/README.tt +29 -0
  103. data/templates/plugin/Rakefile +8 -0
  104. data/templates/plugin/gemspec.tt +27 -0
  105. data/templates/plugin/gitignore +18 -0
  106. data/templates/plugin/lib/lita/plugin_type/plugin.tt +19 -0
  107. data/templates/plugin/lib/plugin.tt +16 -0
  108. data/templates/plugin/locales/en.yml.tt +4 -0
  109. data/templates/plugin/spec/lita/plugin_type/plugin_spec.tt +6 -0
  110. data/templates/plugin/spec/spec_helper.tt +8 -0
  111. data/templates/plugin/templates/gitkeep +0 -0
  112. data/templates/robot/Gemfile +5 -0
  113. data/templates/robot/lita_config.rb +28 -0
  114. metadata +386 -20
  115. data/.standard.yml +0 -3
  116. data/CHANGELOG.md +0 -5
  117. data/CODE_OF_CONDUCT.md +0 -132
  118. data/lib/rita/version.rb +0 -5
  119. data/sig/rita.rbs +0 -4
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ handler_class = Class.new(Lita::Handler) do
6
+ namespace "testclass"
7
+
8
+ def self.name
9
+ "Lita::Handlers::Test"
10
+ end
11
+ end
12
+
13
+ additional_handler_class = Class.new(Lita::Handler) do
14
+ namespace "testclass"
15
+
16
+ config :test_property, type: String, default: "a string"
17
+
18
+ def self.name
19
+ "Lita::Handlers::TestBase"
20
+ end
21
+ end
22
+
23
+ describe handler_class, lita_handler: true, additional_lita_handlers: additional_handler_class do
24
+ context 'when the "additional_lita_handlers" metadata is provided' do
25
+ it "loads additional handlers into the registry" do
26
+ expect(registry.handlers).to include(additional_handler_class)
27
+ end
28
+
29
+ it "populates config from additional handlers" do
30
+ expect(registry.config.handlers.testclass.test_property).to eq("a string")
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ handler_class = Class.new(Lita::Handler) do
6
+ route(/^message$/, :message)
7
+ route(/^channel$/, :channel)
8
+ route(/^private message$/, :private_message)
9
+ route(/^command$/, :command, command: true)
10
+ route("restricted", :restricted, restrict_to: :some_group)
11
+ route("admins only", :admins_only, restrict_to: :admins)
12
+
13
+ http.get "web", :web
14
+
15
+ on :connected, :greet
16
+
17
+ def message(response)
18
+ response.reply(response.user.name)
19
+ end
20
+
21
+ def channel(response)
22
+ if (room = response.message.source.room_object)
23
+ response.reply(room.id)
24
+ response.reply(room.name)
25
+ else
26
+ response.reply("No room")
27
+ end
28
+ end
29
+
30
+ def private_message(response)
31
+ if response.private_message?
32
+ response.reply("Private")
33
+ else
34
+ response.reply("Public")
35
+ end
36
+ end
37
+
38
+ def command(response)
39
+ response.reply("a", "command")
40
+ end
41
+
42
+ def restricted(_response); end
43
+
44
+ def web(_request, _response); end
45
+
46
+ def greet(_payload); end
47
+
48
+ def self.name
49
+ "Lita::Handlers::Test"
50
+ end
51
+ end
52
+
53
+ describe handler_class, lita_handler: true do
54
+ describe "routing messages" do
55
+ it { is_expected.to route("message") }
56
+ it { is_expected.to route("message").to(:message) }
57
+ it { is_expected.not_to route("message").to(:not_a_message) }
58
+ end
59
+
60
+ describe "routing channels" do
61
+ it { is_expected.to route("channel") }
62
+ it { is_expected.to route("channel").to(:channel) }
63
+ it { is_expected.not_to route("channel").to(:not_a_channel) }
64
+ end
65
+
66
+ describe "routing commands" do
67
+ it { is_expected.to route_command("command") }
68
+ it { is_expected.not_to route("command") }
69
+ it { is_expected.not_to route_command("not a command") }
70
+ it { is_expected.to route_command("command").to(:command) }
71
+ it { is_expected.not_to route_command("command").to(:not_a_command) }
72
+ end
73
+
74
+ describe "routing to restricted routes" do
75
+ it { is_expected.not_to route("restricted") }
76
+ it { is_expected.to route("restricted").with_authorization_for(:some_group) }
77
+ it { is_expected.not_to route("restricted").with_authorization_for(:wrong_group) }
78
+ it { is_expected.to route("admins only").with_authorization_for(:admins) }
79
+ it { is_expected.to route("restricted").with_authorization_for(:some_group).to(:restricted) }
80
+ it { is_expected.not_to route("restricted").with_authorization_for(:some_group).to(:nothing) }
81
+ end
82
+
83
+ describe "routing HTTP routes" do
84
+ it { is_expected.to route_http(:get, "web") }
85
+ it { is_expected.to route_http(:get, "web").to(:web) }
86
+ it { is_expected.not_to route_http(:get, "web").to(:message) }
87
+ it { is_expected.not_to route_http(:post, "web") }
88
+ end
89
+
90
+ describe "routing events" do
91
+ it { is_expected.to route_event(:connected) }
92
+ it { is_expected.to route_event(:connected).to(:greet) }
93
+ it { is_expected.not_to route_event(:not_an_event) }
94
+ it { is_expected.not_to route_event(:connected).to(:message) }
95
+ end
96
+
97
+ describe "#message" do
98
+ it "replies with a string" do
99
+ send_message("message")
100
+ expect(replies).to eq(["Test User"])
101
+ end
102
+
103
+ it "does not memoize #replies on first access" do
104
+ replies
105
+ send_message("message")
106
+ expect(replies).to eq(["Test User"])
107
+ end
108
+ end
109
+
110
+ describe "#channel" do
111
+ it "replies with channel id if sent from room" do
112
+ room = Lita::Room.create_or_update(1, name: "Room")
113
+ send_message("channel", from: room)
114
+ expect(replies).to eq(%w[1 Room])
115
+ end
116
+
117
+ it "replies with no channel if not sent from room" do
118
+ send_message("channel")
119
+ expect(replies).to eq(["No room"])
120
+ end
121
+ end
122
+
123
+ describe "#private_message" do
124
+ let(:another_user) do
125
+ Lita::User.create(2, name: "Another User")
126
+ end
127
+
128
+ let(:room) do
129
+ Lita::Room.create_or_update(1, name: "Room")
130
+ end
131
+
132
+ it "replies with Private in response to a private message" do
133
+ send_message("private message", as: another_user, privately: true)
134
+ expect(source).to be_a_private_message
135
+ expect(replies.last).to eq("Private")
136
+ end
137
+
138
+ it "replies with Private in response to a private command" do
139
+ send_command("private message", as: another_user, privately: true)
140
+ expect(source).to be_a_private_message
141
+ expect(replies.last).to eq("Private")
142
+ end
143
+
144
+ it "replies with Public in response to a public message" do
145
+ send_message("private message", as: another_user, from: room)
146
+ expect(replies.last).to eq("Public")
147
+ end
148
+ end
149
+
150
+ describe "#command" do
151
+ it "replies with two strings" do
152
+ send_command("command")
153
+ expect(replies).to eq(%w[a command])
154
+ end
155
+ end
156
+
157
+ it "allows the sending user to be specified" do
158
+ another_user = Lita::User.create(2, name: "Another User")
159
+ send_message("message", as: another_user)
160
+ expect(replies.last).to eq("Another User")
161
+ end
162
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::Source do
6
+ subject { described_class.new(user: user, room: room, private_message: pm) }
7
+
8
+ let(:pm) { false }
9
+ let(:room) { Lita::Room.new(1) }
10
+ let(:user) { Lita::User.new(1) }
11
+
12
+ describe "#room" do
13
+ it "returns the room as a string" do
14
+ expect(subject.room).to eq("1")
15
+ end
16
+ end
17
+
18
+ describe "#room_object" do
19
+ it "returns the room as a Lita::Room" do
20
+ expect(subject.room_object).to eq(room)
21
+ end
22
+ end
23
+
24
+ describe "#user" do
25
+ it "returns the user object" do
26
+ expect(subject.user).to eq(user)
27
+ end
28
+ end
29
+
30
+ context "when the private_message argument is true" do
31
+ let(:pm) { true }
32
+
33
+ it "is marked as a private message" do
34
+ expect(subject).to be_a_private_message
35
+ end
36
+ end
37
+
38
+ it "can be manually marked as private" do
39
+ subject.private_message!
40
+
41
+ expect(subject).to be_a_private_message
42
+ end
43
+
44
+ context "with a string for the room argument" do
45
+ let(:room) { "#channel" }
46
+
47
+ it "sets #room to the string" do
48
+ expect(subject.room).to eq(room)
49
+ end
50
+
51
+ it "sets #room_object to a Lita::Room with the string as its ID" do
52
+ expect(subject.room_object).to eq(Lita::Room.new(room))
53
+ end
54
+
55
+ context "room exists in database" do
56
+ let(:name) { "example" }
57
+
58
+ it "finds room by its ID" do
59
+ Lita::Room.create_or_update(room, name: name)
60
+ expect(subject.room_object.name).to eq(name)
61
+ end
62
+ end
63
+ end
64
+
65
+ it "requires either a user or a room" do
66
+ expect { described_class.new }.to raise_error(ArgumentError)
67
+ end
68
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::Store do
6
+ it "has nil values by default" do
7
+ expect(subject[:foo]).to be_nil
8
+ end
9
+
10
+ it "sets and gets values" do
11
+ subject[:foo] = :bar
12
+
13
+ expect(subject[:foo]).to eq(:bar)
14
+ end
15
+
16
+ it "allows a custom internal store" do
17
+ subject = described_class.new(Hash.new { |h, k| h[k] = described_class.new })
18
+
19
+ subject[:foo][:bar] = :baz
20
+
21
+ expect(subject[:foo][:bar]).to eq(:baz)
22
+ end
23
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::TemplateResolver do
6
+ subject do
7
+ described_class.new(template_root, template_name, adapter_name)
8
+ end
9
+
10
+ let(:adapter_name) { :shell }
11
+ let(:generic_template) { File.join(template_root, "basic.erb") }
12
+ let(:irc_template) { File.join(template_root, "basic.irc.erb") }
13
+ let(:template_name) { "basic" }
14
+ let(:template_root) { File.expand_path(File.join("..", "..", "templates"), __FILE__) }
15
+
16
+ describe "#resolve" do
17
+ context "when there is a template for the adapter" do
18
+ let(:adapter_name) { :irc }
19
+
20
+ it "returns the path to the adapter-specific template" do
21
+ expect(subject.resolve).to eq(irc_template)
22
+ end
23
+ end
24
+
25
+ context "when there is no template for the adapter" do
26
+ it "returns the path for the generic template" do
27
+ expect(subject.resolve).to eq(generic_template)
28
+ end
29
+ end
30
+
31
+ context "when there is no template with the given name" do
32
+ let(:template_name) { "nonexistent" }
33
+
34
+ it "raises an exception" do
35
+ expect { subject.resolve }.to raise_error(
36
+ Lita::MissingTemplateError,
37
+ %r{templates/nonexistent\.erb}
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::Template do
6
+ describe ".from_file" do
7
+ context "with a path to an ERB template" do
8
+ subject do
9
+ described_class.from_file(File.expand_path("../templates/basic.erb", __dir__))
10
+ end
11
+
12
+ it "uses the source in the file" do
13
+ expect(subject.render).to eq("Template rendered from a file!")
14
+ end
15
+ end
16
+ end
17
+
18
+ describe "#add_helper" do
19
+ subject { described_class.new("<%= reverse_name(@first, @last) %>") }
20
+ let(:helper) do
21
+ Module.new do
22
+ def reverse_name(first, last)
23
+ "#{last}, #{first}"
24
+ end
25
+ end
26
+ end
27
+
28
+ it "adds the helper to the evaluation context" do
29
+ subject.add_helper(helper)
30
+
31
+ expect(subject.render(first: "Carl", last: "Pug")).to eq("Pug, Carl")
32
+ end
33
+ end
34
+
35
+ describe "#render" do
36
+ context "with a static source template" do
37
+ subject { described_class.new("Hello, Lita!") }
38
+
39
+ it "renders the text" do
40
+ expect(subject.render).to eq("Hello, Lita!")
41
+ end
42
+ end
43
+
44
+ context "with interpolation variables" do
45
+ subject { described_class.new("Hello, <%= @name %>!") }
46
+
47
+ it "renders the text with interpolated values" do
48
+ expect(subject.render(name: "Carl")).to eq("Hello, Carl!")
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::Timer do
6
+ let(:queue) { Queue.new }
7
+
8
+ before { allow(subject).to receive(:sleep) }
9
+
10
+ after { subject.stop }
11
+
12
+ it "runs single timers" do
13
+ subject = described_class.new { queue.push(true) }
14
+ expect(subject).to receive(:sleep).with(0).once
15
+ subject.start
16
+ expect(queue.pop(true)).to be(true)
17
+ expect { queue.pop(true) }.to raise_error(ThreadError)
18
+ end
19
+
20
+ it "runs recurring timers" do
21
+ halt = false
22
+ subject = described_class.new(interval: 1, recurring: true) do |timer|
23
+ queue.push(true)
24
+ timer.stop if halt
25
+ halt = true
26
+ end
27
+ expect(subject).to receive(:sleep).with(1).twice
28
+ subject.start
29
+ 2.times { expect(queue.pop(true)).to be(true) }
30
+ expect { queue.pop(true) }.to raise_error(ThreadError)
31
+ end
32
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::User, lita: true do
6
+ describe ".create" do
7
+ it "creates and returns new users" do
8
+ user = described_class.create(1, name: "Carl")
9
+ expect(user.id).to eq("1")
10
+ expect(user.name).to eq("Carl")
11
+ persisted_user = described_class.find_by_id(1)
12
+ expect(user).to eq(persisted_user)
13
+ end
14
+
15
+ it "returns existing users" do
16
+ described_class.create(1, name: "Carl")
17
+ user = described_class.find_by_id(1)
18
+ expect(user.id).to eq("1")
19
+ expect(user.name).to eq("Carl")
20
+ end
21
+
22
+ it "merges and saves new metadata for existing users" do
23
+ described_class.create(1, name: "Carl")
24
+ described_class.create(1, name: "Mr. Carl", foo: "bar")
25
+ user = described_class.find_by_id(1)
26
+ expect(user.name).to eq("Mr. Carl")
27
+ expect(user.metadata["foo"]).to eq("bar")
28
+ end
29
+ end
30
+
31
+ describe ".find_by_id" do
32
+ it "finds users with no metadata stored" do
33
+ described_class.create(1)
34
+ user = described_class.find_by_id(1)
35
+ expect(user.id).to eq("1")
36
+ end
37
+ end
38
+
39
+ describe ".find_by_mention_name" do
40
+ it "returns nil if no user matches the provided mention name" do
41
+ expect(described_class.find_by_mention_name("carlthepug")).to be_nil
42
+ end
43
+
44
+ it "returns a user that matches the provided mention name" do
45
+ described_class.create(1, mention_name: "carlthepug")
46
+ user = described_class.find_by_mention_name("carlthepug")
47
+ expect(user.id).to eq("1")
48
+ end
49
+ end
50
+
51
+ describe ".find_by_name" do
52
+ it "returns nil if no user matches the provided name" do
53
+ expect(described_class.find_by_name("Carl")).to be_nil
54
+ end
55
+
56
+ it "returns existing users" do
57
+ described_class.create(1, name: "Carl")
58
+ user = described_class.find_by_name("Carl")
59
+ expect(user.id).to eq("1")
60
+ end
61
+ end
62
+
63
+ describe ".find_by_partial_name" do
64
+ before { described_class.create(1, name: "José Vicente Cuadra") }
65
+
66
+ it "finds users by partial name match" do
67
+ user = described_class.find_by_partial_name("José")
68
+ expect(user.id).to eq("1")
69
+ end
70
+
71
+ it "returns nil if no users' names start with the provided string" do
72
+ expect(described_class.find_by_partial_name("Foo")).to be_nil
73
+ end
74
+
75
+ it "returns nil if more than one match was found" do
76
+ described_class.create(2, name: "José Contreras")
77
+ expect(described_class.find_by_partial_name("José")).to be_nil
78
+ end
79
+ end
80
+
81
+ describe ".fuzzy_find" do
82
+ let!(:user) { described_class.create(1, name: "Carl the Pug", mention_name: "carlthepug") }
83
+
84
+ it "finds by ID" do
85
+ expect(described_class.fuzzy_find(1)).to eq(user)
86
+ end
87
+
88
+ it "finds by mention name" do
89
+ expect(described_class.fuzzy_find("carlthepug")).to eq(user)
90
+ end
91
+
92
+ it "finds by name" do
93
+ expect(described_class.fuzzy_find("Carl the Pug")).to eq(user)
94
+ end
95
+
96
+ it "finds by partial mention name" do
97
+ expect(described_class.fuzzy_find("Carl")).to eq(user)
98
+ end
99
+ end
100
+
101
+ describe "#mention_name" do
102
+ it "returns the user's mention name from metadata" do
103
+ subject = described_class.new(1, name: "Carl", mention_name: "carlthepug")
104
+ expect(subject.mention_name).to eq("carlthepug")
105
+ end
106
+
107
+ it "returns the user's name if there is no mention name in the metadata" do
108
+ subject = described_class.new(1, name: "Carl")
109
+ expect(subject.mention_name).to eq("Carl")
110
+ end
111
+ end
112
+
113
+ describe "#save" do
114
+ subject { described_class.new(1, name: "Carl", mention_name: "carlthepug") }
115
+
116
+ it "saves an ID to name mapping for the user in Redis" do
117
+ subject.save
118
+ expect(described_class.redis.hgetall("id:1")).to include("name" => "Carl")
119
+ end
120
+
121
+ it "saves a name to ID mapping for the user in Redis" do
122
+ subject.save
123
+ expect(described_class.redis.get("name:Carl")).to eq("1")
124
+ end
125
+
126
+ it "saves a mention name to ID mapping for the user in Redis" do
127
+ subject.save
128
+ expect(described_class.redis.get("mention_name:carlthepug")).to eq("1")
129
+ end
130
+
131
+ context "when a key is deleted" do
132
+ before do
133
+ subject.metadata["example"] = "hello"
134
+ subject.save
135
+ expect(described_class.redis.hkeys("id:1")).to include("example")
136
+ subject.metadata.delete("example")
137
+ subject.save
138
+ end
139
+
140
+ it "deletes that key from redis" do
141
+ expect(described_class.redis.hkeys("id:1")).not_to include("example")
142
+ end
143
+ end
144
+ end
145
+
146
+ describe "equality" do
147
+ it "considers two users equal if they share an ID and name" do
148
+ user_1 = described_class.new(1, name: "Carl")
149
+ user_2 = described_class.new(1, name: "Carl")
150
+ expect(user_1).to eq(user_2)
151
+ expect(user_1).to eql(user_2)
152
+ end
153
+
154
+ it "doesn't assume the comparison object is a Lita::User" do
155
+ user = described_class.new(1, name: "Carl")
156
+ expect(user).not_to eq("not a Lita::User object")
157
+ expect(user).not_to eql("not a Lita::User object")
158
+ end
159
+
160
+ it "consistently hashes equal users" do
161
+ user_1 = described_class.new(1, name: "Carl")
162
+ user_2 = described_class.new(1, name: "Carl")
163
+
164
+ expect(user_1.hash).to eq(user_2.hash)
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Lita::Util do
6
+ describe ".stringify_keys" do
7
+ it "converts symbol hash keys to strings" do
8
+ stringified = described_class.stringify_keys(foo: "bar")
9
+ expect(stringified).to eq("foo" => "bar")
10
+ end
11
+ end
12
+
13
+ describe ".underscore" do
14
+ it "converts camel cased strings into snake case" do
15
+ expect(described_class.underscore("FooBarBaz")).to eq("foo_bar_baz")
16
+ end
17
+ end
18
+ end