/spec/lib/gitlab/gitaly_client_spec.rb

https://gitlab.com/Ben305/gitlab-ce · Ruby · 283 lines · 241 code · 39 blank · 3 comment · 0 complexity · 6ce2d844101c2bb3f77ff37226f22f69 MD5 · raw file

  1. require 'spec_helper'
  2. # We stub Gitaly in `spec/support/gitaly.rb` for other tests. We don't want
  3. # those stubs while testing the GitalyClient itself.
  4. describe Gitlab::GitalyClient, skip_gitaly_mock: true do
  5. describe '.stub' do
  6. # Notice that this is referring to gRPC "stubs", not rspec stubs
  7. before do
  8. described_class.clear_stubs!
  9. end
  10. context 'when passed a UNIX socket address' do
  11. it 'passes the address as-is to GRPC' do
  12. address = 'unix:/tmp/gitaly.sock'
  13. allow(Gitlab.config.repositories).to receive(:storages).and_return({
  14. 'default' => { 'gitaly_address' => address }
  15. })
  16. expect(Gitaly::CommitService::Stub).to receive(:new).with(address, any_args)
  17. described_class.stub(:commit_service, 'default')
  18. end
  19. end
  20. context 'when passed a TCP address' do
  21. it 'strips tcp:// prefix before passing it to GRPC::Core::Channel initializer' do
  22. address = 'localhost:9876'
  23. prefixed_address = "tcp://#{address}"
  24. allow(Gitlab.config.repositories).to receive(:storages).and_return({
  25. 'default' => { 'gitaly_address' => prefixed_address }
  26. })
  27. expect(Gitaly::CommitService::Stub).to receive(:new).with(address, any_args)
  28. described_class.stub(:commit_service, 'default')
  29. end
  30. end
  31. end
  32. describe 'allow_n_plus_1_calls' do
  33. context 'when RequestStore is enabled', :request_store do
  34. it 'returns the result of the allow_n_plus_1_calls block' do
  35. expect(described_class.allow_n_plus_1_calls { "result" }).to eq("result")
  36. end
  37. end
  38. context 'when RequestStore is not active' do
  39. it 'returns the result of the allow_n_plus_1_calls block' do
  40. expect(described_class.allow_n_plus_1_calls { "something" }).to eq("something")
  41. end
  42. end
  43. end
  44. describe 'enforce_gitaly_request_limits?' do
  45. def call_gitaly(count = 1)
  46. (1..count).each do
  47. described_class.enforce_gitaly_request_limits(:test)
  48. end
  49. end
  50. context 'when RequestStore is enabled', :request_store do
  51. it 'allows up the maximum number of allowed calls' do
  52. expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error
  53. end
  54. context 'when the maximum number of calls has been reached' do
  55. before do
  56. call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS)
  57. end
  58. it 'fails on the next call' do
  59. expect { call_gitaly(1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError)
  60. end
  61. end
  62. it 'allows the maximum number of calls to be exceeded within an allow_n_plus_1_calls block' do
  63. expect do
  64. described_class.allow_n_plus_1_calls do
  65. call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1)
  66. end
  67. end.not_to raise_error
  68. end
  69. context 'when the maximum number of calls has been reached within an allow_n_plus_1_calls block' do
  70. before do
  71. described_class.allow_n_plus_1_calls do
  72. call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS)
  73. end
  74. end
  75. it 'allows up to the maximum number of calls outside of an allow_n_plus_1_calls block' do
  76. expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error
  77. end
  78. it 'does not allow the maximum number of calls to be exceeded outside of an allow_n_plus_1_calls block' do
  79. expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError)
  80. end
  81. end
  82. end
  83. context 'when RequestStore is not active' do
  84. it 'does not raise errors when the maximum number of allowed calls is exceeded' do
  85. expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 2) }.not_to raise_error
  86. end
  87. it 'does not fail when the maximum number of calls is exceeded within an allow_n_plus_1_calls block' do
  88. expect do
  89. described_class.allow_n_plus_1_calls do
  90. call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1)
  91. end
  92. end.not_to raise_error
  93. end
  94. end
  95. end
  96. describe 'get_request_count' do
  97. context 'when RequestStore is enabled', :request_store do
  98. context 'when enforce_gitaly_request_limits is called outside of allow_n_plus_1_calls blocks' do
  99. before do
  100. described_class.enforce_gitaly_request_limits(:call)
  101. end
  102. it 'counts gitaly calls' do
  103. expect(described_class.get_request_count).to eq(1)
  104. end
  105. end
  106. context 'when enforce_gitaly_request_limits is called inside and outside of allow_n_plus_1_calls blocks' do
  107. before do
  108. described_class.enforce_gitaly_request_limits(:call)
  109. described_class.allow_n_plus_1_calls do
  110. described_class.enforce_gitaly_request_limits(:call)
  111. end
  112. end
  113. it 'counts gitaly calls' do
  114. expect(described_class.get_request_count).to eq(2)
  115. end
  116. end
  117. context 'when reset_counts is called' do
  118. before do
  119. described_class.enforce_gitaly_request_limits(:call)
  120. described_class.reset_counts
  121. end
  122. it 'resets counts' do
  123. expect(described_class.get_request_count).to eq(0)
  124. end
  125. end
  126. end
  127. context 'when RequestStore is not active' do
  128. before do
  129. described_class.enforce_gitaly_request_limits(:call)
  130. end
  131. it 'returns zero' do
  132. expect(described_class.get_request_count).to eq(0)
  133. end
  134. end
  135. end
  136. describe 'feature_enabled?' do
  137. let(:feature_name) { 'my_feature' }
  138. let(:real_feature_name) { "gitaly_#{feature_name}" }
  139. context 'when Gitaly is disabled' do
  140. before do
  141. allow(described_class).to receive(:enabled?).and_return(false)
  142. end
  143. it 'returns false' do
  144. expect(described_class.feature_enabled?(feature_name)).to be(false)
  145. end
  146. end
  147. context 'when the feature status is DISABLED' do
  148. let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::DISABLED }
  149. it 'returns false' do
  150. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  151. end
  152. end
  153. context 'when the feature_status is OPT_IN' do
  154. let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::OPT_IN }
  155. context "when the feature flag hasn't been set" do
  156. it 'returns false' do
  157. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  158. end
  159. end
  160. context "when the feature flag is set to disable" do
  161. before do
  162. Feature.get(real_feature_name).disable
  163. end
  164. it 'returns false' do
  165. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  166. end
  167. end
  168. context "when the feature flag is set to enable" do
  169. before do
  170. Feature.get(real_feature_name).enable
  171. end
  172. it 'returns true' do
  173. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
  174. end
  175. end
  176. context "when the feature flag is set to a percentage of time" do
  177. before do
  178. Feature.get(real_feature_name).enable_percentage_of_time(70)
  179. end
  180. it 'bases the result on pseudo-random numbers' do
  181. expect(Random).to receive(:rand).and_return(0.3)
  182. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
  183. expect(Random).to receive(:rand).and_return(0.8)
  184. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  185. end
  186. end
  187. context "when a feature is not persisted" do
  188. it 'returns false when opt_into_all_features is off' do
  189. allow(Feature).to receive(:persisted?).and_return(false)
  190. allow(described_class).to receive(:opt_into_all_features?).and_return(false)
  191. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  192. end
  193. it 'returns true when the override is on' do
  194. allow(Feature).to receive(:persisted?).and_return(false)
  195. allow(described_class).to receive(:opt_into_all_features?).and_return(true)
  196. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
  197. end
  198. end
  199. end
  200. context 'when the feature_status is OPT_OUT' do
  201. let(:feature_status) { Gitlab::GitalyClient::MigrationStatus::OPT_OUT }
  202. context "when the feature flag hasn't been set" do
  203. it 'returns true' do
  204. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(true)
  205. end
  206. end
  207. context "when the feature flag is set to disable" do
  208. before do
  209. Feature.get(real_feature_name).disable
  210. end
  211. it 'returns false' do
  212. expect(described_class.feature_enabled?(feature_name, status: feature_status)).to be(false)
  213. end
  214. end
  215. end
  216. end
  217. describe 'timeouts' do
  218. context 'with default values' do
  219. before do
  220. stub_application_setting(gitaly_timeout_default: 55)
  221. stub_application_setting(gitaly_timeout_medium: 30)
  222. stub_application_setting(gitaly_timeout_fast: 10)
  223. end
  224. it 'returns expected values' do
  225. expect(described_class.default_timeout).to be(55)
  226. expect(described_class.medium_timeout).to be(30)
  227. expect(described_class.fast_timeout).to be(10)
  228. end
  229. end
  230. end
  231. end