diff options
Diffstat (limited to 'spec/support')
-rw-r--r-- | spec/support/api/repositories_shared_context.rb | 10 | ||||
-rw-r--r-- | spec/support/api/status_shared_examples.rb | 42 | ||||
-rw-r--r-- | spec/support/chat_slash_commands_shared_examples.rb | 97 | ||||
-rw-r--r-- | spec/support/controllers/githubish_import_controller_shared_context.rb | 10 | ||||
-rw-r--r-- | spec/support/controllers/githubish_import_controller_shared_examples.rb | 232 | ||||
-rw-r--r-- | spec/support/kubernetes_helpers.rb | 52 | ||||
-rw-r--r-- | spec/support/query_recorder.rb | 40 | ||||
-rw-r--r-- | spec/support/reactive_caching_helpers.rb | 38 | ||||
-rw-r--r-- | spec/support/slack_mattermost_notifications_shared_examples.rb (renamed from spec/support/slack_mattermost_shared_examples.rb) | 2 |
9 files changed, 522 insertions, 1 deletions
diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb new file mode 100644 index 00000000000..ea38fe4f5b8 --- /dev/null +++ b/spec/support/api/repositories_shared_context.rb @@ -0,0 +1,10 @@ +shared_context 'disabled repository' do + before do + project.project_feature.update_attributes!( + repository_access_level: ProjectFeature::DISABLED, + merge_requests_access_level: ProjectFeature::DISABLED, + builds_access_level: ProjectFeature::DISABLED + ) + expect(project.feature_available?(:repository, current_user)).to be false + end +end diff --git a/spec/support/api/status_shared_examples.rb b/spec/support/api/status_shared_examples.rb new file mode 100644 index 00000000000..3481749a7f0 --- /dev/null +++ b/spec/support/api/status_shared_examples.rb @@ -0,0 +1,42 @@ +# Specs for status checking. +# +# Requires an API request: +# let(:request) { get api("/projects/#{project.id}/repository/branches", user) } +shared_examples_for '400 response' do + before do + # Fires the request + request + end + + it 'returns 400' do + expect(response).to have_http_status(400) + end +end + +shared_examples_for '403 response' do + before do + # Fires the request + request + end + + it 'returns 403' do + expect(response).to have_http_status(403) + end +end + +shared_examples_for '404 response' do + let(:message) { nil } + before do + # Fires the request + request + end + + it 'returns 404' do + expect(response).to have_http_status(404) + expect(json_response).to be_an Object + + if message.present? + expect(json_response['message']).to eq(message) + end + end +end diff --git a/spec/support/chat_slash_commands_shared_examples.rb b/spec/support/chat_slash_commands_shared_examples.rb new file mode 100644 index 00000000000..4dfa29849ee --- /dev/null +++ b/spec/support/chat_slash_commands_shared_examples.rb @@ -0,0 +1,97 @@ +RSpec.shared_examples 'chat slash commands service' do + describe "Associations" do + it { is_expected.to respond_to :token } + it { is_expected.to have_many :chat_names } + end + + describe '#valid_token?' do + subject { described_class.new } + + context 'when the token is empty' do + it 'is false' do + expect(subject.valid_token?('wer')).to be_falsey + end + end + + context 'when there is a token' do + before do + subject.token = '123' + end + + it 'accepts equal tokens' do + expect(subject.valid_token?('123')).to be_truthy + end + end + end + + describe '#trigger' do + subject { described_class.new } + + context 'no token is passed' do + let(:params) { Hash.new } + + it 'returns nil' do + expect(subject.trigger(params)).to be_nil + end + end + + context 'with a token passed' do + let(:project) { create(:empty_project) } + let(:params) { { token: 'token' } } + + before do + allow(subject).to receive(:token).and_return('token') + end + + context 'no user can be found' do + context 'when no url can be generated' do + it 'responds with the authorize url' do + response = subject.trigger(params) + + expect(response[:response_type]).to eq :ephemeral + expect(response[:text]).to start_with ":sweat_smile: Couldn't identify you" + end + end + + context 'when an auth url can be generated' do + let(:params) do + { + team_domain: 'http://domain.tld', + team_id: 'T3423423', + user_id: 'U234234', + user_name: 'mepmep', + token: 'token' + } + end + + let(:service) do + project.create_mattermost_slash_commands_service( + properties: { token: 'token' } + ) + end + + it 'generates the url' do + response = service.trigger(params) + + expect(response[:text]).to start_with(':wave: Hi there!') + end + end + end + + context 'when the user is authenticated' do + let!(:chat_name) { create(:chat_name, service: subject) } + let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } } + + subject do + described_class.create(project: project, properties: { token: 'token' }) + end + + it 'triggers the command' do + expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute) + + subject.trigger(params) + end + end + end + end +end diff --git a/spec/support/controllers/githubish_import_controller_shared_context.rb b/spec/support/controllers/githubish_import_controller_shared_context.rb new file mode 100644 index 00000000000..e71994edec6 --- /dev/null +++ b/spec/support/controllers/githubish_import_controller_shared_context.rb @@ -0,0 +1,10 @@ +shared_context 'a GitHub-ish import controller' do + let(:user) { create(:user) } + let(:token) { "asdasd12345" } + let(:access_params) { { github_access_token: token } } + + before do + sign_in(user) + allow(controller).to receive(:"#{provider}_import_enabled?").and_return(true) + end +end diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb new file mode 100644 index 00000000000..d0fd2d52004 --- /dev/null +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -0,0 +1,232 @@ +# Specifications for behavior common to all objects with an email attribute. +# Takes a list of email-format attributes and requires: +# - subject { "the object with a attribute= setter" } +# Note: You have access to `email_value` which is the email address value +# being currently tested). + +def assign_session_token(provider) + session[:"#{provider}_access_token"] = 'asdasd12345' +end + +shared_examples 'a GitHub-ish import controller: POST personal_access_token' do + let(:status_import_url) { public_send("status_import_#{provider}_url") } + + it "updates access token" do + token = 'asdfasdf9876' + + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:user).and_return(true) + + post :personal_access_token, personal_access_token: token + + expect(session[:"#{provider}_access_token"]).to eq(token) + expect(controller).to redirect_to(status_import_url) + end +end + +shared_examples 'a GitHub-ish import controller: GET new' do + let(:status_import_url) { public_send("status_import_#{provider}_url") } + + it "redirects to status if we already have a token" do + assign_session_token(provider) + allow(controller).to receive(:logged_in_with_provider?).and_return(false) + + get :new + + expect(controller).to redirect_to(status_import_url) + end + + it "renders the :new page if no token is present in session" do + get :new + + expect(response).to render_template(:new) + end +end + +shared_examples 'a GitHub-ish import controller: GET status' do + let(:new_import_url) { public_send("new_import_#{provider}_url") } + let(:user) { create(:user) } + let(:repo) { OpenStruct.new(login: 'vim', full_name: 'asd/vim') } + let(:org) { OpenStruct.new(login: 'company') } + let(:org_repo) { OpenStruct.new(login: 'company', full_name: 'company/repo') } + let(:extra_assign_expectations) { {} } + + before do + assign_session_token(provider) + end + + it "assigns variables" do + project = create(:empty_project, import_type: provider, creator_id: user.id) + stub_client(repos: [repo, org_repo], orgs: [org], org_repos: [org_repo]) + + get :status + + expect(assigns(:already_added_projects)).to eq([project]) + expect(assigns(:repos)).to eq([repo, org_repo]) + extra_assign_expectations.each do |key, value| + expect(assigns(key)).to eq(value) + end + end + + it "does not show already added project" do + project = create(:empty_project, import_type: provider, creator_id: user.id, import_source: 'asd/vim') + stub_client(repos: [repo], orgs: []) + + get :status + + expect(assigns(:already_added_projects)).to eq([project]) + expect(assigns(:repos)).to eq([]) + end + + it "handles an invalid access token" do + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:repos).and_raise(Octokit::Unauthorized) + + get :status + + expect(session[:"#{provider}_access_token"]).to be_nil + expect(controller).to redirect_to(new_import_url) + expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.") + end +end + +shared_examples 'a GitHub-ish import controller: POST create' do + let(:user) { create(:user) } + let(:provider_username) { user.username } + let(:provider_user) { OpenStruct.new(login: provider_username) } + let(:provider_repo) do + OpenStruct.new( + name: 'vim', + full_name: "#{provider_username}/vim", + owner: OpenStruct.new(login: provider_username) + ) + end + + before do + stub_client(user: provider_user, repo: provider_repo) + assign_session_token(provider) + end + + context "when the repository owner is the provider user" do + context "when the provider user and GitLab user's usernames match" do + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + + context "when the provider user and GitLab user's usernames don't match" do + let(:provider_username) { "someone_else" } + + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context "when the repository owner is not the provider user" do + let(:other_username) { "someone_else" } + + before do + provider_repo.owner = OpenStruct.new(login: other_username) + assign_session_token(provider) + end + + context "when a namespace with the provider user's username already exists" do + let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) } + + context "when the namespace is owned by the GitLab user" do + it "takes the existing namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + + context "when the namespace is not owned by the GitLab user" do + before do + existing_namespace.owner = create(:user) + existing_namespace.save + end + + it "creates a project using user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context "when a namespace with the provider user's username doesn't exist" do + context "when current user can create namespaces" do + it "creates the namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).and_return(double(execute: true)) + + expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1) + end + + it "takes the new namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, target_namespace: provider_repo.name, format: :js + end + end + + context "when current user can't create namespaces" do + before do + user.update_attribute(:can_create_group, false) + end + + it "doesn't create the namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).and_return(double(execute: true)) + + expect { post :create, format: :js }.not_to change(Namespace, :count) + end + + it "takes the current user's namespace" do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, format: :js + end + end + end + + context 'user has chosen a namespace and name for the project' do + let(:test_namespace) { create(:namespace, name: 'test_namespace', owner: user) } + let(:test_name) { 'test_name' } + + it 'takes the selected namespace and name' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js } + end + + it 'takes the selected name and default namespace' do + expect(Gitlab::GithubImport::ProjectCreator). + to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider). + and_return(double(execute: true)) + + post :create, { new_name: test_name, format: :js } + end + end + end +end diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb new file mode 100644 index 00000000000..6c4c246a68b --- /dev/null +++ b/spec/support/kubernetes_helpers.rb @@ -0,0 +1,52 @@ +module KubernetesHelpers + include Gitlab::Kubernetes + + def kube_discovery_body + { "kind" => "APIResourceList", + "resources" => [ + { "name" => "pods", "namespaced" => true, "kind" => "Pod" }, + ], + } + end + + def kube_pods_body(*pods) + { "kind" => "PodList", + "items" => [ kube_pod ], + } + end + + # This is a partial response, it will have many more elements in reality but + # these are the ones we care about at the moment + def kube_pod(app: "valid-pod-label") + { "metadata" => { + "name" => "kube-pod", + "creationTimestamp" => "2016-11-25T19:55:19Z", + "labels" => { "app" => app }, + }, + "spec" => { + "containers" => [ + { "name" => "container-0" }, + { "name" => "container-1" }, + ], + }, + "status" => { "phase" => "Running" }, + } + end + + def kube_terminals(service, pod) + pod_name = pod['metadata']['name'] + containers = pod['spec']['containers'] + + containers.map do |container| + terminal = { + selectors: { pod: pod_name, container: container['name'] }, + url: container_exec_url(service.api_url, service.namespace, pod_name, container['name']), + subprotocols: ['channel.k8s.io'], + headers: { 'Authorization' => ["Bearer #{service.token}"] }, + created_at: DateTime.parse(pod['metadata']['creationTimestamp']) + } + terminal[:ca_pem] = service.ca_pem if service.ca_pem.present? + terminal + end + end +end diff --git a/spec/support/query_recorder.rb b/spec/support/query_recorder.rb new file mode 100644 index 00000000000..e40d5ebd9a8 --- /dev/null +++ b/spec/support/query_recorder.rb @@ -0,0 +1,40 @@ +module ActiveRecord + class QueryRecorder + attr_reader :log + + def initialize(&block) + @log = [] + ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block) + end + + def callback(name, start, finish, message_id, values) + return if %w(CACHE SCHEMA).include?(values[:name]) + @log << values[:sql] + end + + def count + @log.count + end + + def log_message + @log.join("\n\n") + end + end +end + +RSpec::Matchers.define :exceed_query_limit do |expected| + supports_block_expectations + + match do |block| + query_count(&block) > expected + end + + failure_message_when_negated do |actual| + "Expected a maximum of #{expected} queries, got #{@recorder.count}:\n\n#{@recorder.log_message}" + end + + def query_count(&block) + @recorder = ActiveRecord::QueryRecorder.new(&block) + @recorder.count + end +end diff --git a/spec/support/reactive_caching_helpers.rb b/spec/support/reactive_caching_helpers.rb new file mode 100644 index 00000000000..279db3c5748 --- /dev/null +++ b/spec/support/reactive_caching_helpers.rb @@ -0,0 +1,38 @@ +module ReactiveCachingHelpers + def reactive_cache_key(subject, *qualifiers) + ([subject.class.reactive_cache_key.call(subject)].flatten + qualifiers).join(':') + end + + def stub_reactive_cache(subject = nil, data = nil) + allow(ReactiveCachingWorker).to receive(:perform_async) + allow(ReactiveCachingWorker).to receive(:perform_in) + write_reactive_cache(subject, data) if data + end + + def read_reactive_cache(subject) + Rails.cache.read(reactive_cache_key(subject)) + end + + def write_reactive_cache(subject, data) + start_reactive_cache_lifetime(subject) + Rails.cache.write(reactive_cache_key(subject), data) + end + + def reactive_cache_alive?(subject) + Rails.cache.read(reactive_cache_key(subject, 'alive')) + end + + def invalidate_reactive_cache(subject) + Rails.cache.delete(reactive_cache_key(subject, 'alive')) + end + + def start_reactive_cache_lifetime(subject) + Rails.cache.write(reactive_cache_key(subject, 'alive'), true) + end + + def expect_reactive_cache_update_queued(subject) + expect(ReactiveCachingWorker). + to receive(:perform_in). + with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id) + end +end diff --git a/spec/support/slack_mattermost_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb index 56d4965f74d..8582aea5fe5 100644 --- a/spec/support/slack_mattermost_shared_examples.rb +++ b/spec/support/slack_mattermost_notifications_shared_examples.rb @@ -1,6 +1,6 @@ Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f } -RSpec.shared_examples 'slack or mattermost' do +RSpec.shared_examples 'slack or mattermost notifications' do let(:chat_service) { described_class.new } let(:webhook_url) { 'https://example.gitlab.com/' } |