mongoid 7.1.0 → 7.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +6 -6
  5. data/README.md +1 -1
  6. data/Rakefile +14 -5
  7. data/lib/config/locales/en.yml +5 -5
  8. data/lib/mongoid/association/accessors.rb +37 -2
  9. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  10. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  11. data/lib/mongoid/association/proxy.rb +1 -1
  12. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -1
  13. data/lib/mongoid/association/referenced/belongs_to/eager.rb +38 -2
  14. data/lib/mongoid/association/referenced/eager.rb +29 -9
  15. data/lib/mongoid/association/referenced/has_one/proxy.rb +6 -1
  16. data/lib/mongoid/atomic.rb +13 -3
  17. data/lib/mongoid/clients/factory.rb +2 -2
  18. data/lib/mongoid/clients/options.rb +8 -8
  19. data/lib/mongoid/clients/sessions.rb +20 -4
  20. data/lib/mongoid/clients/storage_options.rb +5 -5
  21. data/lib/mongoid/config.rb +39 -9
  22. data/lib/mongoid/criteria.rb +23 -4
  23. data/lib/mongoid/criteria/modifiable.rb +2 -1
  24. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  25. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +6 -6
  26. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  27. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  28. data/lib/mongoid/criteria/queryable/pipeline.rb +3 -2
  29. data/lib/mongoid/criteria/queryable/selectable.rb +120 -13
  30. data/lib/mongoid/criteria/queryable/storable.rb +104 -99
  31. data/lib/mongoid/errors/eager_load.rb +2 -0
  32. data/lib/mongoid/errors/no_client_config.rb +2 -2
  33. data/lib/mongoid/errors/no_default_client.rb +1 -1
  34. data/lib/mongoid/extensions/hash.rb +4 -2
  35. data/lib/mongoid/extensions/regexp.rb +1 -1
  36. data/lib/mongoid/fields.rb +2 -1
  37. data/lib/mongoid/fields/validators/macro.rb +4 -1
  38. data/lib/mongoid/matchable/regexp.rb +2 -2
  39. data/lib/mongoid/persistable/pushable.rb +11 -2
  40. data/lib/mongoid/persistence_context.rb +6 -6
  41. data/lib/mongoid/query_cache.rb +61 -18
  42. data/lib/mongoid/serializable.rb +9 -3
  43. data/lib/mongoid/tasks/database.rb +38 -3
  44. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  45. data/lib/mongoid/version.rb +1 -1
  46. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +32 -23
  47. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  48. data/spec/app/models/coding.rb +4 -0
  49. data/spec/app/models/coding/pull_request.rb +12 -0
  50. data/spec/app/models/delegating_patient.rb +16 -0
  51. data/spec/app/models/passport.rb +1 -0
  52. data/spec/app/models/person.rb +2 -0
  53. data/spec/app/models/phone.rb +1 -0
  54. data/spec/app/models/publication.rb +5 -0
  55. data/spec/app/models/publication/encyclopedia.rb +12 -0
  56. data/spec/app/models/publication/review.rb +14 -0
  57. data/spec/app/models/series.rb +1 -0
  58. data/spec/app/models/wiki_page.rb +1 -0
  59. data/spec/integration/app_spec.rb +254 -0
  60. data/spec/integration/associations/embedded_spec.rb +54 -0
  61. data/spec/integration/associations/embeds_many_spec.rb +24 -0
  62. data/spec/integration/associations/embeds_one_spec.rb +24 -0
  63. data/spec/integration/associations/has_many_spec.rb +76 -0
  64. data/spec/integration/associations/has_one_spec.rb +76 -0
  65. data/spec/integration/bson_regexp_raw_spec.rb +20 -0
  66. data/spec/integration/criteria/date_field_spec.rb +41 -0
  67. data/spec/integration/criteria/logical_spec.rb +13 -0
  68. data/spec/integration/document_spec.rb +22 -0
  69. data/spec/integration/shardable_spec.rb +20 -4
  70. data/spec/lite_spec_helper.rb +12 -4
  71. data/spec/mongoid/association/accessors_spec.rb +238 -63
  72. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  73. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  74. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  75. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +193 -10
  76. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  77. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +105 -0
  78. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  79. data/spec/mongoid/clients/factory_spec.rb +8 -8
  80. data/spec/mongoid/clients/options_spec.rb +11 -11
  81. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  82. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  83. data/spec/mongoid/clients_spec.rb +2 -2
  84. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  85. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  86. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  87. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  88. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +1 -1
  89. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  90. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  91. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  92. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  93. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  94. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1051 -392
  95. data/spec/mongoid/criteria/queryable/selectable_spec.rb +52 -0
  96. data/spec/mongoid/criteria/queryable/storable_spec.rb +80 -2
  97. data/spec/mongoid/criteria_spec.rb +36 -2
  98. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  99. data/spec/mongoid/errors/no_client_config_spec.rb +2 -2
  100. data/spec/mongoid/errors/no_client_database_spec.rb +3 -3
  101. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -3
  102. data/spec/mongoid/fields_spec.rb +24 -1
  103. data/spec/mongoid/indexable_spec.rb +6 -4
  104. data/spec/mongoid/matchable/default_spec.rb +1 -1
  105. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  106. data/spec/mongoid/matchable_spec.rb +2 -2
  107. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  108. data/spec/mongoid/query_cache_spec.rb +77 -9
  109. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  110. data/spec/mongoid/scopable_spec.rb +2 -1
  111. data/spec/mongoid/serializable_spec.rb +129 -18
  112. data/spec/mongoid/shardable_models.rb +1 -1
  113. data/spec/mongoid/shardable_spec.rb +2 -2
  114. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  115. data/spec/mongoid/tasks/database_spec.rb +1 -1
  116. data/spec/shared/LICENSE +20 -0
  117. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  118. data/spec/shared/lib/mrss/cluster_config.rb +211 -0
  119. data/spec/shared/lib/mrss/constraints.rb +312 -0
  120. data/spec/shared/lib/mrss/lite_constraints.rb +175 -0
  121. data/spec/shared/lib/mrss/spec_organizer.rb +149 -0
  122. data/spec/spec_helper.rb +2 -31
  123. data/spec/support/child_process_helper.rb +76 -0
  124. data/spec/support/cluster_config.rb +3 -3
  125. data/spec/support/constraints.rb +26 -10
  126. data/spec/support/expectations.rb +3 -1
  127. data/spec/support/helpers.rb +11 -0
  128. data/spec/support/session_registry.rb +50 -0
  129. data/spec/support/spec_config.rb +12 -4
  130. metadata +520 -473
  131. metadata.gz.sig +0 -0
@@ -19,7 +19,7 @@ class SmActor
19
19
 
20
20
  # This is not a usable shard configuration for the server.
21
21
  # We just have it for unit tests.
22
- shard_key age: 1, 'gender' => :hashed
22
+ shard_key age: 1, 'gender' => :hashed, 'hello' => :hashed
23
23
  end
24
24
 
25
25
  class SmAssistant
@@ -48,12 +48,12 @@ describe Mongoid::Shardable do
48
48
 
49
49
  context 'with string value' do
50
50
  it 'sets shard key fields to symbol value' do
51
- SmActor.shard_key_fields.should == %i(age gender)
51
+ SmActor.shard_key_fields.should == %i(age gender hello)
52
52
  end
53
53
 
54
54
  it 'sets shard config' do
55
55
  SmActor.shard_config.should == {
56
- key: {age: 1, gender: 'hashed'},
56
+ key: {age: 1, gender: 'hashed', hello: 'hashed'},
57
57
  options: {},
58
58
  }
59
59
  end
@@ -48,7 +48,7 @@ shared_context "rails rake task" do
48
48
  end
49
49
  end
50
50
 
51
- describe "db:drop", if: non_legacy_server? do
51
+ describe "db:drop" do
52
52
  include_context "rake task"
53
53
  include_context "rails rake task"
54
54
 
@@ -61,7 +61,7 @@ describe "db:drop", if: non_legacy_server? do
61
61
  end
62
62
  end
63
63
 
64
- describe "db:purge", if: non_legacy_server? do
64
+ describe "db:purge" do
65
65
  include_context "rake task"
66
66
  include_context "rails rake task"
67
67
 
@@ -74,7 +74,7 @@ describe "db:purge", if: non_legacy_server? do
74
74
  end
75
75
  end
76
76
 
77
- describe "db:seed", if: non_legacy_server? do
77
+ describe "db:seed" do
78
78
  include_context "rake task"
79
79
  include_context "rails rake task"
80
80
 
@@ -88,7 +88,7 @@ describe "db:seed", if: non_legacy_server? do
88
88
  end
89
89
  end
90
90
 
91
- describe "db:setup", if: non_legacy_server? do
91
+ describe "db:setup" do
92
92
  include_context "rake task"
93
93
  include_context "rails rake task"
94
94
 
@@ -120,7 +120,7 @@ describe "db:setup", if: non_legacy_server? do
120
120
  end
121
121
  end
122
122
 
123
- describe "db:reset", if: non_legacy_server? do
123
+ describe "db:reset" do
124
124
  include_context "rake task"
125
125
  include_context "rails rake task"
126
126
 
@@ -138,7 +138,7 @@ describe "db:reset", if: non_legacy_server? do
138
138
  end
139
139
  end
140
140
 
141
- describe "db:create", if: non_legacy_server? do
141
+ describe "db:create" do
142
142
  include_context "rake task"
143
143
  include_context "rails rake task"
144
144
 
@@ -147,7 +147,7 @@ describe "db:create", if: non_legacy_server? do
147
147
  end
148
148
  end
149
149
 
150
- describe "db:migrate", if: non_legacy_server? do
150
+ describe "db:migrate" do
151
151
  include_context "rake task"
152
152
  include_context "rails rake task"
153
153
 
@@ -156,7 +156,7 @@ describe "db:migrate", if: non_legacy_server? do
156
156
  end
157
157
  end
158
158
 
159
- describe "db:test:prepare", if: non_legacy_server? do
159
+ describe "db:test:prepare" do
160
160
  include_context "rake task"
161
161
  include_context "rails rake task"
162
162
 
@@ -177,7 +177,7 @@ describe "db:test:prepare", if: non_legacy_server? do
177
177
  end
178
178
  end
179
179
 
180
- describe "db:mongoid:create_indexes", if: non_legacy_server? do
180
+ describe "db:mongoid:create_indexes" do
181
181
  include_context "rake task"
182
182
 
183
183
  it_behaves_like "create_indexes"
@@ -201,7 +201,7 @@ describe "db:mongoid:create_indexes", if: non_legacy_server? do
201
201
  end
202
202
  end
203
203
 
204
- describe "db:mongoid:remove_undefined_indexes", if: non_legacy_server? do
204
+ describe "db:mongoid:remove_undefined_indexes" do
205
205
  include_context "rake task"
206
206
 
207
207
  it "receives remove_undefined_indexes" do
@@ -227,7 +227,7 @@ describe "db:mongoid:remove_undefined_indexes", if: non_legacy_server? do
227
227
  end
228
228
  end
229
229
 
230
- describe "db:mongoid:remove_indexes", if: non_legacy_server? do
230
+ describe "db:mongoid:remove_indexes" do
231
231
  include_context "rake task"
232
232
 
233
233
  it "receives remove_indexes" do
@@ -253,7 +253,7 @@ describe "db:mongoid:remove_indexes", if: non_legacy_server? do
253
253
  end
254
254
  end
255
255
 
256
- describe "db:mongoid:drop", if: non_legacy_server? do
256
+ describe "db:mongoid:drop" do
257
257
  include_context "rake task"
258
258
 
259
259
  it "works" do
@@ -269,7 +269,7 @@ describe "db:mongoid:drop", if: non_legacy_server? do
269
269
  end
270
270
  end
271
271
 
272
- describe "db:mongoid:purge", if: non_legacy_server? do
272
+ describe "db:mongoid:purge" do
273
273
  include_context "rake task"
274
274
 
275
275
  it "receives a purge" do
@@ -136,7 +136,7 @@ describe "Mongoid::Tasks::Database" do
136
136
  expect(removed_indexes).to be_empty
137
137
  end
138
138
 
139
- context 'when the index is a text index', if: non_legacy_server? do
139
+ context 'when the index is a text index' do
140
140
 
141
141
  before do
142
142
  class Band
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 MongoDB, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :ChildProcess, 'childprocess'
5
+ autoload :Tempfile, 'tempfile'
6
+
7
+ module Mrss
8
+ module ChildProcessHelper
9
+ class SpawnError < StandardError; end
10
+
11
+ module_function def call(cmd, env: nil, cwd: nil)
12
+ process = ChildProcess.new(*cmd)
13
+ process.io.inherit!
14
+ if cwd
15
+ process.cwd = cwd
16
+ end
17
+ if env
18
+ env.each do |k, v|
19
+ process.environment[k.to_s] = v
20
+ end
21
+ end
22
+ process.start
23
+ process.wait
24
+ process
25
+ end
26
+
27
+ module_function def check_call(cmd, env: nil, cwd: nil)
28
+ process = call(cmd, env: env, cwd: cwd)
29
+ unless process.exit_code == 0
30
+ raise SpawnError, "Failed to execute: #{cmd}"
31
+ end
32
+ end
33
+
34
+ module_function def get_output(cmd, env: nil, cwd: nil)
35
+ process = ChildProcess.new(*cmd)
36
+ process.io.inherit!
37
+ if cwd
38
+ process.cwd = cwd
39
+ end
40
+ if env
41
+ env.each do |k, v|
42
+ process.environment[k.to_s] = v
43
+ end
44
+ end
45
+
46
+ output = ''
47
+ r, w = IO.pipe
48
+
49
+ begin
50
+ process.io.stdout = w
51
+ process.start
52
+ w.close
53
+
54
+ thread = Thread.new do
55
+ begin
56
+ loop do
57
+ output << r.readpartial(16384)
58
+ end
59
+ rescue EOFError
60
+ end
61
+ end
62
+
63
+ process.wait
64
+ thread.join
65
+ ensure
66
+ r.close
67
+ end
68
+
69
+ [process, output]
70
+ end
71
+
72
+ module_function def check_output(*args)
73
+ process, output = get_output(*args)
74
+ unless process.exit_code == 0
75
+ raise SpawnError,"Failed to execute: #{args}"
76
+ end
77
+ output
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,211 @@
1
+ # ClusterConfig requires ClientRegistry class provided by the host project.
2
+
3
+ require 'singleton'
4
+
5
+ module Mrss
6
+ class ClusterConfig
7
+ include Singleton
8
+ include RSpec::Core::Pending
9
+
10
+ def single_server?
11
+ determine_cluster_config
12
+ @single_server
13
+ end
14
+
15
+ def replica_set_name
16
+ determine_cluster_config
17
+ @replica_set_name
18
+ end
19
+
20
+ def server_version
21
+ determine_cluster_config
22
+ @server_version
23
+ end
24
+
25
+ def enterprise?
26
+ determine_cluster_config
27
+ @enterprise
28
+ end
29
+
30
+ def short_server_version
31
+ server_version.split('.')[0..1].join('.')
32
+ end
33
+
34
+ def fcv
35
+ determine_cluster_config
36
+ @fcv
37
+ end
38
+
39
+ # Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
40
+ # in sharded topologies is annoying. Also, FCV doesn't exist in servers
41
+ # less than 3.4. This method returns FCV on 3.4+ servers when in single
42
+ # or RS topologies, and otherwise returns the major.minor server version.
43
+ def fcv_ish
44
+ if server_version.nil?
45
+ raise "Deployment server version not known - check that connection to deployment succeeded"
46
+ end
47
+
48
+ if server_version >= '3.4' && topology != :sharded
49
+ fcv
50
+ else
51
+ if short_server_version == '4.1'
52
+ '4.2'
53
+ else
54
+ short_server_version
55
+ end
56
+ end
57
+ end
58
+
59
+ # @return [ Mongo::Address ] The address of the primary in the deployment.
60
+ def primary_address
61
+ determine_cluster_config
62
+ @primary_address
63
+ end
64
+
65
+ def primary_address_str
66
+ determine_cluster_config
67
+ @primary_address.seed
68
+ end
69
+
70
+ def primary_address_host
71
+ both = primary_address_str
72
+ both.split(':').first
73
+ end
74
+
75
+ def primary_address_port
76
+ both = primary_address_str
77
+ both.split(':')[1] || 27017
78
+ end
79
+
80
+ def primary_description
81
+ determine_cluster_config
82
+ @primary_description
83
+ end
84
+
85
+ # Try running a command on the admin database to see if the mongod was
86
+ # started with auth.
87
+ def auth_enabled?
88
+ if @auth_enabled.nil?
89
+ @auth_enabled = begin
90
+ basic_client.use(:admin).command(getCmdLineOpts: 1).first["argv"].include?("--auth")
91
+ rescue => e
92
+ e.message =~ /(not authorized)|(unauthorized)|(no users authenticated)|(requires authentication)/
93
+ end
94
+ end
95
+ @auth_enabled
96
+ end
97
+
98
+ def topology
99
+ determine_cluster_config
100
+ @topology
101
+ end
102
+
103
+ def storage_engine
104
+ @storage_engine ||= begin
105
+ # 2.6 does not have wired tiger
106
+ if short_server_version == '2.6'
107
+ :mmapv1
108
+ else
109
+ client = ClientRegistry.instance.global_client('root_authorized')
110
+ if topology == :sharded
111
+ shards = client.use(:admin).command(listShards: 1).first
112
+ if shards['shards'].empty?
113
+ raise 'Shards are empty'
114
+ end
115
+ shard = shards['shards'].first
116
+ address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
117
+ client = ClusterTools.instance.direct_client(address_str,
118
+ SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
119
+ end
120
+ rv = client.use(:admin).command(serverStatus: 1).first
121
+ rv = rv['storageEngine']['name']
122
+ rv_map = {
123
+ 'wiredTiger' => :wired_tiger,
124
+ 'mmapv1' => :mmapv1,
125
+ }
126
+ rv_map[rv] || rv
127
+ end
128
+ end
129
+ end
130
+
131
+ # This method returns an alternate address for connecting to the configured
132
+ # deployment. For example, if the replica set is configured with nodes at
133
+ # of localhost:27017 and so on, this method will return 127.0.0.:27017.
134
+ #
135
+ # Note that the "alternate" refers to replica set configuration, not the
136
+ # addresses specified in test suite configuration. If the deployment topology
137
+ # is not a replica set, "alternate" refers to test suite configuration as
138
+ # this is the only configuration available.
139
+ def alternate_address
140
+ @alternate_address ||= begin
141
+ address = primary_address_host
142
+ str = case address
143
+ when '127.0.0.1'
144
+ 'localhost'
145
+ when /^(\d+\.){3}\d+$/
146
+ skip 'This test requires a hostname or 127.0.0.1 as address'
147
+ else
148
+ # We don't know if mongod is listening on ipv4 or ipv6, in principle.
149
+ # Our tests use ipv4, so hardcode that for now.
150
+ # To support both we need to try both addresses which will make this
151
+ # test more complicated.
152
+ #
153
+ # JRuby chokes on primary_address_port as the port (e.g. 27017).
154
+ # Since the port does not actually matter, use a common port like 80.
155
+ resolved_address = Addrinfo.getaddrinfo(address, 80, Socket::PF_INET).first.ip_address
156
+ if resolved_address.include?(':')
157
+ "[#{resolved_address}]"
158
+ else
159
+ resolved_address
160
+ end
161
+ end + ":#{primary_address_port}"
162
+ Mongo::Address.new(str)
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def determine_cluster_config
169
+ return if @primary_address
170
+
171
+ # Run all commands to figure out the cluster configuration from the same
172
+ # client. This is somewhat wasteful when running a single test, but reduces
173
+ # test runtime for the suite overall because all commands are sent on the
174
+ # same connection rather than each command connecting to the cluster by
175
+ # itself.
176
+ client = ClientRegistry.instance.global_client('root_authorized')
177
+
178
+ primary = client.cluster.next_primary
179
+ @primary_address = primary.address
180
+ @primary_description = primary.description
181
+ @replica_set_name = client.cluster.topology.replica_set_name
182
+
183
+ @topology ||= begin
184
+ topology = client.cluster.topology.class.name.sub(/.*::/, '')
185
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
186
+ if topology =~ /^replica_set/
187
+ topology = 'replica_set'
188
+ end
189
+ topology.to_sym
190
+ end
191
+
192
+ @single_server = client.cluster.servers_list.length == 1
193
+
194
+ build_info = client.database.command(buildInfo: 1).first
195
+
196
+ @server_version = build_info['version']
197
+ @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
198
+
199
+ if @topology != :sharded && short_server_version >= '3.4'
200
+ rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
201
+ @fcv = rv['version'] || rv
202
+ end
203
+ end
204
+
205
+ def basic_client
206
+ # Do not cache the result here so that if the client gets closed,
207
+ # client registry reconnects it in subsequent tests
208
+ ClientRegistry.instance.global_client('basic')
209
+ end
210
+ end
211
+ end