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,48 @@
1
+ module Lita
2
+ module RSpec
3
+ module Matchers
4
+ # RSpec matchers for chat routes.
5
+ # @since 4.0.0
6
+ module ChatRouteMatcher
7
+ extend ::RSpec::Matchers::DSL
8
+
9
+ matcher :route do |message_body|
10
+ match do
11
+ message = Message.new(robot, message_body, source)
12
+
13
+ if defined?(@group) and @group.to_s.downcase == "admins"
14
+ robot.config.robot.admins = Array(robot.config.robot.admins) + [source.user.id]
15
+ elsif defined?(@group)
16
+ robot.auth.add_user_to_group!(source.user, @group)
17
+ end
18
+
19
+ matching_routes = described_class.routes.select do |route|
20
+ RouteValidator.new(described_class, route, message, robot).call
21
+ end
22
+
23
+ if defined?(@method_name)
24
+ matching_routes.any? { |route| route.callback.method_name == @method_name }
25
+ else
26
+ !matching_routes.empty?
27
+ end
28
+ end
29
+
30
+ chain :with_authorization_for do |group|
31
+ @group = group
32
+ end
33
+
34
+ chain :to do |method_name|
35
+ @method_name = method_name
36
+ end
37
+ end
38
+
39
+ # Sets an expectation that the provided message routes to a command.
40
+ # @param message_body [String] The body of the message.
41
+ # @return [void]
42
+ def route_command(message_body)
43
+ route("#{robot.mention_name} #{message_body}")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ module Lita
2
+ module RSpec
3
+ module Matchers
4
+ # Lita 3 versions of the routing matchers.
5
+ # @deprecated Will be removed in Lita 5.0. Use the +is_expected+ forms instead.
6
+ class Deprecated
7
+ # @param context [RSpec::ExampleGroup] The example group where the matcher was called.
8
+ # @param new_method_name [String, Symbol] The method that should be used instead.
9
+ # @param positive [Boolean] Whether or not a positive expectation is being made.
10
+ def initialize(context, new_method_name, positive, *args)
11
+ @context = context
12
+ @new_method_name = new_method_name
13
+ @expectation_method_name = positive ? :to : :not_to
14
+ @args = args
15
+
16
+ @context.instance_exec do
17
+ allow_any_instance_of(Authorization).to receive(:user_in_group?).and_return(true)
18
+ end
19
+ end
20
+
21
+ # Sets an expectation that the previously supplied message will route to the provided
22
+ # method.
23
+ # @param method_name [String, Symbol] The name of the method that should be routed to.
24
+ def to(method_name)
25
+ emn = @expectation_method_name
26
+ matcher = @context.public_send(@new_method_name, *@args)
27
+ matcher.to(method_name)
28
+
29
+ @context.instance_exec do
30
+ is_expected.public_send(emn, matcher)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ module Lita
2
+ module RSpec
3
+ module Matchers
4
+ # RSpec matchers for event routes.
5
+ # @since 4.0.0
6
+ module EventRouteMatcher
7
+ extend ::RSpec::Matchers::DSL
8
+
9
+ matcher :route_event do |event_name|
10
+ match do
11
+ callbacks = described_class.event_subscriptions_for(event_name)
12
+
13
+ if defined?(@method_name)
14
+ callbacks.any? { |callback| callback.method_name.equal?(@method_name) }
15
+ else
16
+ !callbacks.empty?
17
+ end
18
+ end
19
+
20
+ chain :to do |method_name|
21
+ @method_name = method_name
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,70 +2,28 @@ module Lita
2
2
  module RSpec
3
3
  # A namespace to hold all of Lita's RSpec matchers.
4
4
  module Matchers
5
- # Used to complete an HTTP routing test chain.
6
- class HTTPRouteMatcher
7
- attr_accessor :context, :http_method, :expectation, :path
8
- attr_reader :expected_route
9
-
10
- def initialize(context, http_method, path, expectation: true)
11
- self.context = context
12
- self.http_method = http_method
13
- self.path = path
14
- self.expectation = expectation
15
- set_description
16
- end
17
-
18
- # Sets an expectation that an HTTP route will or will not be triggered,
19
- # then makes an HTTP request against the app with the HTTP request
20
- # method and path originally provided.
21
- # @param route [Symbol] The name of the method that should or should not
22
- # be triggered.
23
- # @return [void]
24
- def to(route)
25
- self.expected_route = route
26
-
27
- e = expectation
28
- m = http_method.upcase
29
- p = path
30
- i = i18n_key
31
-
32
- context.instance_eval do
33
- called = false
34
- allow(subject).to receive(route) { called = true }
35
- env = Rack::MockRequest.env_for(p, method: m)
36
- robot.app.call(env)
37
- expect(called).to be(e), I18n.t(i, method: m, path: p, route: route)
38
- end
39
- end
40
-
41
- private
42
-
43
- def description_prefix
44
- if expectation
45
- "routes"
46
- else
47
- "doesn't route"
5
+ # RSpec matchers for HTTP routes.
6
+ # @since 4.0.0
7
+ module HTTPRouteMatcher
8
+ extend ::RSpec::Matchers::DSL
9
+
10
+ matcher :route_http do |http_method, path|
11
+ match do
12
+ env = Rack::MockRequest.env_for(path, method: http_method)
13
+
14
+ matching_routes = robot.app.recognize(env)
15
+
16
+ if defined?(@method_name)
17
+ matching_routes.include?(@method_name)
18
+ else
19
+ !matching_routes.empty?
20
+ end
48
21
  end
49
- end
50
22
 
51
- def expected_route=(route)
52
- @expected_route = route
53
- set_description
54
- end
55
-
56
- def i18n_key
57
- if expectation
58
- "lita.rspec.http_route_failure"
59
- else
60
- "lita.rspec.negative_http_route_failure"
23
+ chain :to do |method_name|
24
+ @method_name = method_name
61
25
  end
62
26
  end
63
-
64
- def set_description
65
- description = "#{description_prefix} #{http_method.upcase} #{path}"
66
- description << " to :#{expected_route}" if expected_route
67
- ::RSpec.current_example.metadata[:description] = description
68
- end
69
27
  end
70
28
  end
71
29
  end
@@ -23,12 +23,6 @@ module Lita
23
23
  user
24
24
  end
25
25
 
26
- # @deprecated Use {.create} instead.
27
- def find(id, metadata = {})
28
- Lita.logger.warn I18n.t("lita.user.find_deprecated")
29
- create(id, metadata)
30
- end
31
-
32
26
  # Finds a user by ID.
33
27
  # @param id [Integer, String] The user's unique ID.
34
28
  # @return [Lita::User, nil] The user or +nil+ if no such user is known.
@@ -1,9 +1,6 @@
1
1
  module Lita
2
2
  # Handy utilities used by other Lita classes.
3
3
  module Util
4
- # A regular expression for acronyms.
5
- ACRONYM_REGEX = /(?=a)b/
6
-
7
4
  class << self
8
5
  # Returns a hash with any symbol keys converted to strings.
9
6
  # @param hash [Hash] The hash to convert.
@@ -14,16 +11,12 @@ module Lita
14
11
  result
15
12
  end
16
13
 
17
- # Transforms a camel-cased string into a snaked-cased string. Taken from
18
- # +ActiveSupport.+
14
+ # Transforms a camel-cased string into a snaked-cased string. Taken from +ActiveSupport.+
19
15
  # @param camel_cased_word [String] The word to transform.
20
16
  # @return [String] The transformed word.
21
17
  def underscore(camel_cased_word)
22
18
  word = camel_cased_word.to_s.dup
23
19
  word.gsub!("::", "/")
24
- word.gsub!(/(?:([A-Za-z\d])|^)(#{ACRONYM_REGEX})(?=\b|[^a-z])/) do
25
- "#{Regexp.last_match[1]}#{Regexp.last_match[1] && "_"}#{Regexp.last_match[2].downcase}"
26
- end
27
20
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
28
21
  word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
29
22
  word.tr!("-", "_")
@@ -1,4 +1,4 @@
1
1
  module Lita
2
2
  # The current version of Lita.
3
- VERSION = "3.3.1"
3
+ VERSION = "4.0.0.rc1"
4
4
  end
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency "thor", ">= 0.18.1"
34
34
 
35
35
  spec.add_development_dependency "rake"
36
+ spec.add_development_dependency "rack-test"
36
37
  spec.add_development_dependency "rspec", ">= 3.0.0"
37
38
  spec.add_development_dependency "simplecov"
38
39
  spec.add_development_dependency "coveralls"
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Lita::Adapter do
4
- let(:robot) { instance_double("Lita::Robot") }
3
+ describe Lita::Adapter, lita: true do
4
+ let(:robot) { Lita::Robot.new(registry) }
5
5
 
6
6
  let(:required_methods) { [:join, :part, :run, :send_messages, :set_topic, :shut_down] }
7
7
 
@@ -33,13 +33,31 @@ describe Lita::Adapter do
33
33
  expect(Lita.logger).to receive(:fatal).with(/foo, bar, baz, blah, bleh/)
34
34
  expect { subject }.to raise_error(SystemExit)
35
35
  end
36
+
37
+ it "logs a deprecation warning when the adapter is initialized" do
38
+ expect(Lita.logger).to receive(:warn).with(/Use Lita::Adapter\.config instead/)
39
+
40
+ expect { adapter_class.new(robot) }.to raise_error(SystemExit)
41
+ end
36
42
  end
37
43
 
38
- describe ".namespace" do
39
- it "raises an exception if self.name is not implemented" do
40
- expect do
41
- Class.new(Lita::Adapter).namespace
42
- end.to raise_error(RuntimeError)
44
+ describe "#config" do
45
+ let(:adapter) do
46
+ Class.new(described_class) do
47
+ namespace "test"
48
+
49
+ config :foo, default: :bar
50
+ end
51
+ end
52
+
53
+ let(:robot) { Lita::Robot.new(registry) }
54
+
55
+ before { registry.register_adapter(:test, adapter) }
56
+
57
+ subject { adapter.new(robot) }
58
+
59
+ it "provides access to the adapter's configuration object" do
60
+ expect(subject.config.foo).to eq(:bar)
43
61
  end
44
62
  end
45
63
 
@@ -1,12 +1,21 @@
1
- describe Lita::Adapters::Shell do
1
+ require "spec_helper"
2
+
3
+ describe Lita::Adapters::Shell, lita: true do
2
4
  let(:robot) do
3
- instance_double("Lita::Robot", name: "Lita", mention_name: "LitaBot", alias: "/")
5
+ instance_double(
6
+ "Lita::Robot",
7
+ name: "Lita",
8
+ mention_name: "LitaBot",
9
+ alias: "/",
10
+ config: registry.config
11
+ )
4
12
  end
5
13
 
6
14
  subject { described_class.new(robot) }
7
15
 
8
16
  describe "#run" do
9
17
  before do
18
+ registry.register_adapter(:shell, described_class)
10
19
  allow(subject).to receive(:puts)
11
20
  allow(Readline).to receive(:readline).and_return("foo", "exit")
12
21
  allow(robot).to receive(:trigger)
@@ -19,8 +28,8 @@ describe Lita::Adapters::Shell do
19
28
  subject.run
20
29
  end
21
30
 
22
- it "marks messages as commands if config.adapter.private_chat is true" do
23
- Lita.config.adapter.private_chat = true
31
+ it "marks messages as commands if config.adapters.shell.private_chat is true" do
32
+ registry.config.adapters.shell.private_chat = true
24
33
  expect_any_instance_of(Lita::Message).to receive(:command!)
25
34
  subject.run
26
35
  end
@@ -29,6 +38,17 @@ describe Lita::Adapters::Shell do
29
38
  expect(robot).to receive(:trigger).with(:connected)
30
39
  subject.run
31
40
  end
41
+
42
+ it "exits cleanly when EOF is received" do
43
+ allow(Readline).to receive(:readline).and_return(nil)
44
+ subject.run
45
+ end
46
+
47
+ it "removes empty input from readline history" do
48
+ allow(Readline).to receive(:readline).and_return("", "exit")
49
+ expect(Readline::HISTORY).to receive(:pop)
50
+ subject.run
51
+ end
32
52
  end
33
53
 
34
54
  describe "#send_message" do
@@ -4,112 +4,131 @@ describe Lita::Authorization, lita: true do
4
4
  let(:requesting_user) { instance_double("Lita::User", id: "1") }
5
5
  let(:user) { instance_double("Lita::User", id: "2") }
6
6
 
7
+ subject { described_class.new(registry.config) }
8
+
7
9
  before do
8
- Lita.config.robot.admins = ["1"]
10
+ registry.config.robot.admins = ["1"]
9
11
  end
10
12
 
11
- describe ".add_user_to_group" do
13
+ describe "deprecated class methods" do
14
+ {
15
+ add_user_to_group: 3,
16
+ remove_user_from_group: 3,
17
+ user_in_group?: 2,
18
+ user_is_admin?: 1,
19
+ groups: 0,
20
+ groups_with_users: 0
21
+ }.each do |deprecated_method, arity|
22
+ it "logs a warning and calls the instance method version of .#{deprecated_method}" do
23
+ expect(Lita.logger).to receive(:warn).with(
24
+ /Lita::Authorization\.#{Regexp.escape(deprecated_method)} will be removed in Lita 5\.0/
25
+ )
26
+ expect_any_instance_of(described_class).to receive(deprecated_method)
27
+ described_class.public_send(deprecated_method, *arity.times.map { Object.new })
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#add_user_to_group" do
12
33
  it "adds users to an auth group" do
13
- described_class.add_user_to_group(requesting_user, user, "employees")
14
- expect(described_class.user_in_group?(user, "employees")).to be true
34
+ subject.add_user_to_group(requesting_user, user, "employees")
35
+ expect(subject.user_in_group?(user, "employees")).to be true
15
36
  end
16
37
 
17
38
  it "can only be called by admins" do
18
- Lita.config.robot.admins = nil
19
- result = described_class.add_user_to_group(
39
+ registry.config.robot.admins = nil
40
+ result = subject.add_user_to_group(
20
41
  requesting_user,
21
42
  user,
22
43
  "employees"
23
44
  )
24
45
  expect(result).to eq(:unauthorized)
25
- expect(described_class.user_in_group?(user, "employees")).to be false
46
+ expect(subject.user_in_group?(user, "employees")).to be false
26
47
  end
27
48
 
28
49
  it "normalizes the group name" do
29
- described_class.add_user_to_group(requesting_user, user, "eMPLoYeeS")
30
- expect(described_class.user_in_group?(user, " EmplOyEEs ")).to be true
50
+ subject.add_user_to_group(requesting_user, user, "eMPLoYeeS")
51
+ expect(subject.user_in_group?(user, " EmplOyEEs ")).to be true
31
52
  end
32
53
  end
33
54
 
34
- describe ".remove_user_from_group" do
55
+ describe "#remove_user_from_group" do
35
56
  it "removes users from an auth group" do
36
- described_class.add_user_to_group(requesting_user, user, "employees")
37
- described_class.remove_user_from_group(requesting_user, user, "employees")
38
- expect(described_class.user_in_group?(user, "employees")).to be false
57
+ subject.add_user_to_group(requesting_user, user, "employees")
58
+ subject.remove_user_from_group(requesting_user, user, "employees")
59
+ expect(subject.user_in_group?(user, "employees")).to be false
39
60
  end
40
61
 
41
62
  it "can only be called by admins" do
42
- described_class.add_user_to_group(requesting_user, user, "employees")
43
- Lita.config.robot.admins = nil
44
- result = described_class.remove_user_from_group(
63
+ subject.add_user_to_group(requesting_user, user, "employees")
64
+ registry.config.robot.admins = nil
65
+ result = subject.remove_user_from_group(
45
66
  requesting_user,
46
67
  user,
47
68
  "employees"
48
69
  )
49
70
  expect(result).to eq(:unauthorized)
50
- expect(described_class.user_in_group?(user, "employees")).to be true
71
+ expect(subject.user_in_group?(user, "employees")).to be true
51
72
  end
52
73
 
53
74
  it "normalizes the group name" do
54
- described_class.add_user_to_group(requesting_user, user, "eMPLoYeeS")
55
- described_class.remove_user_from_group(requesting_user, user, "EmployeeS")
56
- expect(described_class.user_in_group?(user, " EmplOyEEs ")).to be false
75
+ subject.add_user_to_group(requesting_user, user, "eMPLoYeeS")
76
+ subject.remove_user_from_group(requesting_user, user, "EmployeeS")
77
+ expect(subject.user_in_group?(user, " EmplOyEEs ")).to be false
57
78
  end
58
79
  end
59
80
 
60
- describe ".user_in_group?" do
81
+ describe "#user_in_group?" do
61
82
  it "returns false if the user is in the group" do
62
- expect(described_class.user_in_group?(user, "employees")).to be false
83
+ expect(subject.user_in_group?(user, "employees")).to be false
63
84
  end
64
85
 
65
86
  it "delegates to .user_is_admin? if the group is admins" do
66
- expect(described_class).to receive(:user_is_admin?)
67
- described_class.user_in_group?(user, "admins")
87
+ expect(subject).to receive(:user_is_admin?)
88
+ subject.user_in_group?(user, "admins")
68
89
  end
69
90
  end
70
91
 
71
- describe ".user_is_admin?" do
92
+ describe "#user_is_admin?" do
72
93
  it "returns true if the user's ID is in the config" do
73
- expect(described_class.user_is_admin?(requesting_user)).to be true
94
+ expect(subject.user_is_admin?(requesting_user)).to be true
74
95
  end
75
96
 
76
97
  it "returns false if the user's ID is not in the config" do
77
- Lita.config.robot.admins = nil
78
- expect(described_class.user_is_admin?(user)).to be false
98
+ registry.config.robot.admins = nil
99
+ expect(subject.user_is_admin?(user)).to be false
79
100
  end
80
101
  end
81
102
 
82
- describe ".groups" do
103
+ describe "#groups" do
83
104
  before do
84
105
  %i(foo bar baz).each do |group|
85
- described_class.add_user_to_group(requesting_user, user, group)
106
+ subject.add_user_to_group(requesting_user, user, group)
86
107
  end
87
108
  end
88
109
 
89
110
  it "returns a list of all authorization groups" do
90
- expect(described_class.groups).to match_array(%i(foo bar baz))
111
+ expect(subject.groups).to match_array(%i(foo bar baz))
91
112
  end
92
113
  end
93
114
 
94
- describe ".groups_with_users" do
115
+ describe "#groups_with_users" do
95
116
  before do
96
117
  %i(foo bar baz).each do |group|
97
- described_class.add_user_to_group(requesting_user, user, group)
98
- described_class.add_user_to_group(
118
+ subject.add_user_to_group(requesting_user, user, group)
119
+ subject.add_user_to_group(
99
120
  requesting_user,
100
121
  requesting_user,
101
122
  group
102
123
  )
103
124
  end
104
- allow(Lita::User).to receive(:find_by_id).with("1").and_return(
105
- requesting_user
106
- )
125
+ allow(Lita::User).to receive(:find_by_id).with("1").and_return(requesting_user)
107
126
  allow(Lita::User).to receive(:find_by_id).with("2").and_return(user)
108
127
  end
109
128
 
110
129
  it "returns a hash of all authorization groups and their members" do
111
130
  groups = %i(foo bar baz)
112
- groups_with_users = described_class.groups_with_users
131
+ groups_with_users = subject.groups_with_users
113
132
  expect(groups_with_users.keys).to match_array(groups)
114
133
  groups.each do |group|
115
134
  expect(groups_with_users[group]).to match_array([user, requesting_user])