summaryrefslogtreecommitdiff
path: root/spec/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models/ci')
-rw-r--r--spec/models/ci/build_spec.rb387
-rw-r--r--spec/models/ci/commit_spec.rb307
-rw-r--r--spec/models/ci/project_services/hip_chat_message_spec.rb39
-rw-r--r--spec/models/ci/project_services/hip_chat_service_spec.rb73
-rw-r--r--spec/models/ci/project_services/mail_service_spec.rb191
-rw-r--r--spec/models/ci/project_services/slack_message_spec.rb43
-rw-r--r--spec/models/ci/project_services/slack_service_spec.rb57
-rw-r--r--spec/models/ci/project_spec.rb263
-rw-r--r--spec/models/ci/runner_project_spec.rb16
-rw-r--r--spec/models/ci/runner_spec.rb70
-rw-r--r--spec/models/ci/service_spec.rb48
-rw-r--r--spec/models/ci/trigger_spec.rb17
-rw-r--r--spec/models/ci/variable_spec.rb45
-rw-r--r--spec/models/ci/web_hook_spec.rb63
14 files changed, 1619 insertions, 0 deletions
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
new file mode 100644
index 00000000000..da56f6e31ae
--- /dev/null
+++ b/spec/models/ci/build_spec.rb
@@ -0,0 +1,387 @@
+# == Schema Information
+#
+# Table name: builds
+#
+# id :integer not null, primary key
+# project_id :integer
+# status :string(255)
+# finished_at :datetime
+# trace :text
+# created_at :datetime
+# updated_at :datetime
+# started_at :datetime
+# runner_id :integer
+# commit_id :integer
+# coverage :float
+# commands :text
+# job_id :integer
+# name :string(255)
+# deploy :boolean default(FALSE)
+# options :text
+# allow_failure :boolean default(FALSE), not null
+# stage :string(255)
+# trigger_request_id :integer
+#
+
+require 'spec_helper'
+
+describe Ci::Build do
+ let(:project) { FactoryGirl.create :ci_project }
+ let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
+ let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+ let(:build) { FactoryGirl.create :ci_build, commit: commit }
+ subject { build }
+
+ it { is_expected.to belong_to(:commit) }
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to validate_presence_of :status }
+ it { is_expected.to validate_presence_of :ref }
+
+ it { is_expected.to respond_to :success? }
+ it { is_expected.to respond_to :failed? }
+ it { is_expected.to respond_to :running? }
+ it { is_expected.to respond_to :pending? }
+ it { is_expected.to respond_to :trace_html }
+
+ describe :first_pending do
+ let(:first) { FactoryGirl.create :ci_build, commit: commit, status: 'pending', created_at: Date.yesterday }
+ let(:second) { FactoryGirl.create :ci_build, commit: commit, status: 'pending' }
+ before { first; second }
+ subject { Ci::Build.first_pending }
+
+ it { is_expected.to be_a(Ci::Build) }
+ it('returns with the first pending build') { is_expected.to eq(first) }
+ end
+
+ describe :create_from do
+ before do
+ build.status = 'success'
+ build.save
+ end
+ let(:create_from_build) { Ci::Build.create_from build }
+
+ it 'there should be a pending task' do
+ expect(Ci::Build.pending.count(:all)).to eq 0
+ create_from_build
+ expect(Ci::Build.pending.count(:all)).to be > 0
+ end
+ end
+
+ describe :started? do
+ subject { build.started? }
+
+ context 'without started_at' do
+ before { build.started_at = nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ %w(running success failed).each do |status|
+ context "if build status is #{status}" do
+ before { build.status = status }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(pending canceled).each do |status|
+ context "if build status is #{status}" do
+ before { build.status = status }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :active? do
+ subject { build.active? }
+
+ %w(pending running).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(success failed canceled).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :complete? do
+ subject { build.complete? }
+
+ %w(success failed canceled).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(pending running).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :ignored? do
+ subject { build.ignored? }
+
+ context 'if build is not allowed to fail' do
+ before { build.allow_failure = false }
+
+ context 'and build.status is success' do
+ before { build.status = 'success' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'and build.status is failed' do
+ before { build.status = 'failed' }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'if build is allowed to fail' do
+ before { build.allow_failure = true }
+
+ context 'and build.status is success' do
+ before { build.status = 'success' }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'and build.status is failed' do
+ before { build.status = 'failed' }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+
+ describe :trace do
+ subject { build.trace_html }
+
+ it { is_expected.to be_empty }
+
+ context 'if build.trace contains text' do
+ let(:text) { 'example output' }
+ before { build.trace = text }
+
+ it { is_expected.to include(text) }
+ it { expect(subject.length).to be >= text.length }
+ end
+
+ context 'if build.trace hides token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.project.update_attributes(token: token)
+ build.update_attributes(trace: token)
+ end
+
+ it { is_expected.to_not include(token) }
+ end
+ end
+
+ describe :timeout do
+ subject { build.timeout }
+
+ it { is_expected.to eq(commit.project.timeout) }
+ end
+
+ describe :duration do
+ subject { build.duration }
+
+ it { is_expected.to eq(120.0) }
+
+ context 'if the building process has not started yet' do
+ before do
+ build.started_at = nil
+ build.finished_at = nil
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'if the building process has started' do
+ before do
+ build.started_at = Time.now - 1.minute
+ build.finished_at = nil
+ end
+
+ it { is_expected.to be_a(Float) }
+ it { is_expected.to be > 0.0 }
+ end
+ end
+
+ describe :options do
+ let(:options) do
+ {
+ image: "ruby:2.1",
+ services: [
+ "postgres"
+ ]
+ }
+ end
+
+ subject { build.options }
+ it { is_expected.to eq(options) }
+ end
+
+ describe :sha do
+ subject { build.sha }
+
+ it { is_expected.to eq(commit.sha) }
+ end
+
+ describe :short_sha do
+ subject { build.short_sha }
+
+ it { is_expected.to eq(commit.short_sha) }
+ end
+
+ describe :allow_git_fetch do
+ subject { build.allow_git_fetch }
+
+ it { is_expected.to eq(project.allow_git_fetch) }
+ end
+
+ describe :project do
+ subject { build.project }
+
+ it { is_expected.to eq(commit.project) }
+ end
+
+ describe :project_id do
+ subject { build.project_id }
+
+ it { is_expected.to eq(commit.project_id) }
+ end
+
+ describe :project_name do
+ subject { build.project_name }
+
+ it { is_expected.to eq(project.name) }
+ end
+
+ describe :repo_url do
+ subject { build.repo_url }
+
+ it { is_expected.to eq(project.repo_url_with_auth) }
+ end
+
+ describe :extract_coverage do
+ context 'valid content & regex' do
+ subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') }
+
+ it { is_expected.to eq(98.29) }
+ end
+
+ context 'valid content & bad regex' do
+ subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'no coverage content & regex' do
+ subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'multiple results in content & regex' do
+ subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') }
+
+ it { is_expected.to eq(98.29) }
+ end
+ end
+
+ describe :variables do
+ context 'returns variables' do
+ subject { build.variables }
+
+ let(:variables) do
+ [
+ { key: :DB_NAME, value: 'postgres', public: true }
+ ]
+ end
+
+ it { is_expected.to eq(variables) }
+
+ context 'and secure variables' do
+ let(:secure_variables) do
+ [
+ { key: 'SECRET_KEY', value: 'secret_value', public: false }
+ ]
+ end
+
+ before do
+ build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
+ end
+
+ it { is_expected.to eq(variables + secure_variables) }
+
+ context 'and trigger variables' do
+ let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
+ let(:trigger_request) { FactoryGirl.create :ci_trigger_request_with_variables, commit: commit, trigger: trigger }
+ let(:trigger_variables) do
+ [
+ { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
+ ]
+ end
+
+ before do
+ build.trigger_request = trigger_request
+ end
+
+ it { is_expected.to eq(variables + secure_variables + trigger_variables) }
+ end
+ end
+ end
+ end
+
+ describe :project_recipients do
+ let(:pusher_email) { 'pusher@gitlab.test' }
+ let(:user) { User.new(notification_email: pusher_email) }
+ subject { build.project_recipients }
+
+ before do
+ build.update_attributes(user: user)
+ end
+
+ it 'should return pusher_email as only recipient when no additional recipients are given' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: '')
+ is_expected.to eq([pusher_email])
+ end
+
+ it 'should return pusher_email and additional recipients' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: 'rec1 rec2')
+ is_expected.to eq(['rec1', 'rec2', pusher_email])
+ end
+
+ it 'should return recipients' do
+ project.update_attributes(email_add_pusher: false,
+ email_recipients: 'rec1 rec2')
+ is_expected.to eq(['rec1', 'rec2'])
+ end
+
+ it 'should return unique recipients only' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: "rec1 rec1 #{pusher_email}")
+ is_expected.to eq(['rec1', pusher_email])
+ end
+ end
+end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
new file mode 100644
index 00000000000..acff1ddf0fc
--- /dev/null
+++ b/spec/models/ci/commit_spec.rb
@@ -0,0 +1,307 @@
+# == Schema Information
+#
+# Table name: commits
+#
+# id :integer not null, primary key
+# project_id :integer
+# ref :string(255)
+# sha :string(255)
+# before_sha :string(255)
+# push_data :text
+# created_at :datetime
+# updated_at :datetime
+# tag :boolean default(FALSE)
+# yaml_errors :text
+# committed_at :datetime
+#
+
+require 'spec_helper'
+
+describe Ci::Commit do
+ let(:project) { FactoryGirl.create :ci_project }
+ let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
+ let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+
+ it { is_expected.to belong_to(:gl_project) }
+ it { is_expected.to have_many(:builds) }
+ it { is_expected.to validate_presence_of :sha }
+
+ it { is_expected.to respond_to :git_author_name }
+ it { is_expected.to respond_to :git_author_email }
+ it { is_expected.to respond_to :short_sha }
+
+ describe :last_build do
+ subject { commit.last_build }
+ before do
+ @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday
+ @second = FactoryGirl.create :ci_build, commit: commit
+ end
+
+ it { is_expected.to be_a(Ci::Build) }
+ it('returns with the most recently created build') { is_expected.to eq(@second) }
+ end
+
+ describe :retry do
+ before do
+ @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday
+ @second = FactoryGirl.create :ci_build, commit: commit
+ end
+
+ it "creates new build" do
+ expect(commit.builds.count(:all)).to eq 2
+ commit.retry
+ expect(commit.builds.count(:all)).to eq 3
+ end
+ end
+
+ describe :valid_commit_sha do
+ context 'commit.sha can not start with 00000000' do
+ before do
+ commit.sha = '0' * 40
+ commit.valid_commit_sha
+ end
+
+ it('commit errors should not be empty') { expect(commit.errors).not_to be_empty }
+ end
+ end
+
+ describe :short_sha do
+ subject { commit.short_sha }
+
+ it 'has 8 items' do
+ expect(subject.size).to eq(8)
+ end
+ it { expect(commit.sha).to start_with(subject) }
+ end
+
+ describe :stage do
+ subject { commit.stage }
+
+ before do
+ @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending
+ @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending
+ end
+
+ it 'returns first running stage' do
+ is_expected.to eq('test')
+ end
+
+ context 'first build succeeded' do
+ before do
+ @first.update_attributes(status: :success)
+ end
+
+ it 'returns last running stage' do
+ is_expected.to eq('deploy')
+ end
+ end
+
+ context 'all builds succeeded' do
+ before do
+ @first.update_attributes(status: :success)
+ @second.update_attributes(status: :success)
+ end
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe :create_next_builds do
+ end
+
+ describe :create_builds do
+ let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+
+ def create_builds(trigger_request = nil)
+ commit.create_builds('master', false, nil, trigger_request)
+ end
+
+ def create_next_builds(trigger_request = nil)
+ commit.create_next_builds('master', false, nil, trigger_request)
+ end
+
+ it 'creates builds' do
+ expect(create_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+
+ expect(create_next_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(4)
+
+ expect(create_next_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(5)
+
+ expect(create_next_builds).to be_falsey
+ end
+
+ context 'for different ref' do
+ def create_develop_builds
+ commit.create_builds('develop', false, nil, nil)
+ end
+
+ it 'creates builds' do
+ expect(create_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+
+ expect(create_develop_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(4)
+ expect(commit.refs.size).to eq(2)
+ expect(commit.builds.pluck(:name).uniq.size).to eq(2)
+ end
+ end
+
+ context 'for build triggers' do
+ let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
+ let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
+
+ it 'creates builds' do
+ expect(create_builds(trigger_request)).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+ end
+
+ it 'rebuilds commit' do
+ expect(create_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+
+ expect(create_builds(trigger_request)).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(4)
+ end
+
+ it 'creates next builds' do
+ expect(create_builds(trigger_request)).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+
+ expect(create_next_builds(trigger_request)).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(4)
+ end
+
+ context 'for [ci skip]' do
+ before do
+ allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
+ end
+
+ it 'rebuilds commit' do
+ expect(commit.status).to eq('skipped')
+ expect(create_builds(trigger_request)).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+ expect(commit.status).to eq('pending')
+ end
+ end
+ end
+ end
+
+ describe "#finished_at" do
+ let(:commit) { FactoryGirl.create :ci_commit }
+
+ it "returns finished_at of latest build" do
+ build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60
+ FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120
+
+ expect(commit.finished_at.to_i).to eq(build.finished_at.to_i)
+ end
+
+ it "returns nil if there is no finished build" do
+ FactoryGirl.create :ci_not_started_build, commit: commit
+
+ expect(commit.finished_at).to be_nil
+ end
+ end
+
+ describe "coverage" do
+ let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" }
+ let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
+ let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+
+ it "calculates average when there are two builds with coverage" do
+ FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
+ FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
+ expect(commit.coverage).to eq("35.00")
+ end
+
+ it "calculates average when there are two builds with coverage and one with nil" do
+ FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
+ FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
+ FactoryGirl.create :ci_build, commit: commit
+ expect(commit.coverage).to eq("35.00")
+ end
+
+ it "calculates average when there are two builds with coverage and one is retried" do
+ FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
+ FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, commit: commit
+ FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
+ expect(commit.coverage).to eq("35.00")
+ end
+
+ it "calculates average when there is one build without coverage" do
+ FactoryGirl.create :ci_build, commit: commit
+ expect(commit.coverage).to be_nil
+ end
+ end
+
+ describe :should_create_next_builds? do
+ before do
+ @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success
+ @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed
+ @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed
+ @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success
+ end
+
+ context 'for success' do
+ it 'to create if all succeeded' do
+ expect(commit.should_create_next_builds?(@build4)).to be_truthy
+ end
+ end
+
+ context 'for failed' do
+ before do
+ @build4.update_attributes(status: :failed)
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+
+ context 'and ignore failures for current' do
+ before do
+ @build4.update_attributes(allow_failure: true)
+ end
+
+ it 'to create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_truthy
+ end
+ end
+ end
+
+ context 'for running' do
+ before do
+ @build4.update_attributes(status: :running)
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+ end
+
+ context 'for retried' do
+ before do
+ @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb
new file mode 100644
index 00000000000..e23d6ae2c28
--- /dev/null
+++ b/spec/models/ci/project_services/hip_chat_message_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Ci::HipChatMessage do
+ subject { Ci::HipChatMessage.new(build) }
+
+ let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
+
+ let(:build) do
+ commit.builds.first
+ end
+
+ context 'when all matrix builds succeed' do
+ it 'returns a successful message' do
+ commit.create_builds('master', false, nil)
+ commit.builds.update_all(status: "success")
+ commit.reload
+
+ expect(subject.status_color).to eq 'green'
+ expect(subject.notify?).to be_falsey
+ expect(subject.to_s).to match(/Commit #\d+/)
+ expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./)
+ end
+ end
+
+ context 'when at least one matrix build fails' do
+ it 'returns a failure message' do
+ commit.create_builds('master', false, nil)
+ first_build = commit.builds.first
+ second_build = commit.builds.last
+ first_build.update(status: "success")
+ second_build.update(status: "failed")
+
+ expect(subject.status_color).to eq 'red'
+ expect(subject.notify?).to be_truthy
+ expect(subject.to_s).to match(/Commit #\d+/)
+ expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./)
+ end
+ end
+end
diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb
new file mode 100644
index 00000000000..d9ccc855edf
--- /dev/null
+++ b/spec/models/ci/project_services/hip_chat_service_spec.rb
@@ -0,0 +1,73 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+#
+
+
+require 'spec_helper'
+
+describe Ci::HipChatService do
+
+ describe "Validations" do
+
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :hipchat_room }
+ it { is_expected.to validate_presence_of :hipchat_token }
+
+ end
+ end
+
+ describe "Execute" do
+
+ let(:service) { Ci::HipChatService.new }
+ let(:commit) { FactoryGirl.create :ci_commit }
+ let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' }
+ let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' }
+
+ before do
+ allow(service).to receive_messages(
+ project: commit.project,
+ project_id: commit.project_id,
+ notify_only_broken_builds: false,
+ hipchat_room: 123,
+ hipchat_token: 'a1b2c3d4e5f6'
+ )
+
+ WebMock.stub_request(:post, api_url)
+ end
+
+
+ it "should call the HipChat API" do
+ service.execute(build)
+ Ci::HipChatNotifierWorker.drain
+
+ expect( WebMock ).to have_requested(:post, api_url).once
+ end
+
+ it "calls the worker with expected arguments" do
+ expect( Ci::HipChatNotifierWorker ).to receive(:perform_async) \
+ .with(an_instance_of(String), hash_including(
+ token: 'a1b2c3d4e5f6',
+ room: 123,
+ server: 'https://api.hipchat.com',
+ color: 'red',
+ notify: true
+ ))
+
+ service.execute(build)
+ end
+ end
+end
diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
new file mode 100644
index 00000000000..04e870dce7f
--- /dev/null
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -0,0 +1,191 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+#
+
+require 'spec_helper'
+
+describe Ci::MailService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+ end
+ end
+
+ describe 'Sends email for' do
+ let(:mail) { Ci::MailService.new }
+ let(:user) { User.new(notification_email: 'git@example.com')}
+
+ describe 'failed build' do
+ let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email)
+ expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
+ end
+ end
+
+ describe 'successfull build' do
+ let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ expect(Ci::Notify).to receive(:build_success_email).with(build.id, email)
+ expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successfull build and project has email_recipients' do
+ let(:project) do
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: false,
+ email_recipients: "jeroen@example.com")
+ end
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ should_email("jeroen@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ expect(Ci::Notify).to receive(:build_success_email).with(build.id, email)
+ expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successful build and notify only broken builds' do
+ let(:project) do
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: true,
+ email_recipients: "jeroen@example.com")
+ end
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ end
+
+ it do
+ should_email(commit.git_author_email)
+ should_email("jeroen@example.com")
+ mail.execute(build) if mail.can_execute?(build)
+ end
+
+ def should_email(email)
+ expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
+ expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successful build and can test service' do
+ let(:project) do
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: false,
+ email_recipients: "jeroen@example.com")
+ end
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ build
+ end
+
+ it do
+ expect(mail.can_test?).to eq(true)
+ end
+ end
+
+ describe 'retried build should not receive email' do
+ let(:project) do
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: true,
+ email_recipients: "jeroen@example.com")
+ end
+ let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+
+ before do
+ allow(mail).to receive_messages(
+ project: project
+ )
+ end
+
+ it do
+ Ci::Build.retry(build)
+ should_email(commit.git_author_email)
+ should_email("jeroen@example.com")
+ mail.execute(build) if mail.can_execute?(build)
+ end
+
+ def should_email(email)
+ expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
+ expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb
new file mode 100644
index 00000000000..8adda6c86cc
--- /dev/null
+++ b/spec/models/ci/project_services/slack_message_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe Ci::SlackMessage do
+ subject { Ci::SlackMessage.new(commit) }
+
+ let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
+
+ context 'when all matrix builds succeeded' do
+ let(:color) { 'good' }
+
+ it 'returns a message with success' do
+ commit.create_builds('master', false, nil)
+ commit.builds.update_all(status: "success")
+ commit.reload
+
+ expect(subject.color).to eq(color)
+ expect(subject.fallback).to include('Commit')
+ expect(subject.fallback).to include("\##{commit.id}")
+ expect(subject.fallback).to include('succeeded')
+ expect(subject.attachments.first[:fields]).to be_empty
+ end
+ end
+
+ context 'when one of matrix builds failed' do
+ let(:color) { 'danger' }
+
+ it 'returns a message with information about failed build' do
+ commit.create_builds('master', false, nil)
+ first_build = commit.builds.first
+ second_build = commit.builds.last
+ first_build.update(status: "success")
+ second_build.update(status: "failed")
+
+ expect(subject.color).to eq(color)
+ expect(subject.fallback).to include('Commit')
+ expect(subject.fallback).to include("\##{commit.id}")
+ expect(subject.fallback).to include('failed')
+ expect(subject.attachments.first[:fields].size).to eq(1)
+ expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
+ expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
+ end
+ end
+end
diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb
new file mode 100644
index 00000000000..1ac7dfe568d
--- /dev/null
+++ b/spec/models/ci/project_services/slack_service_spec.rb
@@ -0,0 +1,57 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+#
+
+require 'spec_helper'
+
+describe Ci::SlackService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :webhook }
+ end
+ end
+
+ describe "Execute" do
+ let(:slack) { Ci::SlackService.new }
+ let(:commit) { FactoryGirl.create :ci_commit }
+ let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' }
+ let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
+ let(:notify_only_broken_builds) { false }
+
+ before do
+ allow(slack).to receive_messages(
+ project: commit.project,
+ project_id: commit.project_id,
+ webhook: webhook_url,
+ notify_only_broken_builds: notify_only_broken_builds
+ )
+
+ WebMock.stub_request(:post, webhook_url)
+ end
+
+ it "should call Slack API" do
+ slack.execute(build)
+ Ci::SlackNotifierWorker.drain
+
+ expect(WebMock).to have_requested(:post, webhook_url).once
+ end
+ end
+end
diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb
new file mode 100644
index 00000000000..dec4720a711
--- /dev/null
+++ b/spec/models/ci/project_spec.rb
@@ -0,0 +1,263 @@
+# == Schema Information
+#
+# Table name: projects
+#
+# id :integer not null, primary key
+# name :string(255) not null
+# timeout :integer default(3600), not null
+# created_at :datetime
+# updated_at :datetime
+# token :string(255)
+# default_ref :string(255)
+# path :string(255)
+# always_build :boolean default(FALSE), not null
+# polling_interval :integer
+# public :boolean default(FALSE), not null
+# ssh_url_to_repo :string(255)
+# gitlab_id :integer
+# allow_git_fetch :boolean default(TRUE), not null
+# email_recipients :string(255) default(""), not null
+# email_add_pusher :boolean default(TRUE), not null
+# email_only_broken_builds :boolean default(TRUE), not null
+# skip_refs :string(255)
+# coverage_regex :string(255)
+# shared_runners_enabled :boolean default(FALSE)
+# generated_yaml_config :text
+#
+
+require 'spec_helper'
+
+describe Ci::Project do
+ let(:gl_project) { FactoryGirl.create :empty_project }
+ let(:project) { FactoryGirl.create :ci_project, gl_project: gl_project }
+ subject { project }
+
+ it { is_expected.to have_many(:runner_projects) }
+ it { is_expected.to have_many(:runners) }
+ it { is_expected.to have_many(:web_hooks) }
+ it { is_expected.to have_many(:events) }
+ it { is_expected.to have_many(:variables) }
+ it { is_expected.to have_many(:triggers) }
+ it { is_expected.to have_many(:services) }
+
+ it { is_expected.to validate_presence_of :timeout }
+ it { is_expected.to validate_presence_of :gitlab_id }
+
+ describe 'before_validation' do
+ it 'should set an random token if none provided' do
+ project = FactoryGirl.create :ci_project_without_token
+ expect(project.token).not_to eq("")
+ end
+
+ it 'should not set an random toke if one provided' do
+ project = FactoryGirl.create :ci_project
+ expect(project.token).to eq("iPWx6WM4lhHNedGfBpPJNP")
+ end
+ end
+
+ describe :name_with_namespace do
+ subject { project.name_with_namespace }
+
+ it { is_expected.to eq(project.name) }
+ it { is_expected.to eq(gl_project.name_with_namespace) }
+ end
+
+ describe :path_with_namespace do
+ subject { project.path_with_namespace }
+
+ it { is_expected.to eq(project.path) }
+ it { is_expected.to eq(gl_project.path_with_namespace) }
+ end
+
+ describe :path_with_namespace do
+ subject { project.web_url }
+
+ it { is_expected.to eq(gl_project.web_url) }
+ end
+
+ describe :web_url do
+ subject { project.web_url }
+
+ it { is_expected.to eq(project.gitlab_url) }
+ it { is_expected.to eq(gl_project.web_url) }
+ end
+
+ describe :http_url_to_repo do
+ subject { project.http_url_to_repo }
+
+ it { is_expected.to eq(gl_project.http_url_to_repo) }
+ end
+
+ describe :ssh_url_to_repo do
+ subject { project.ssh_url_to_repo }
+
+ it { is_expected.to eq(gl_project.ssh_url_to_repo) }
+ end
+
+ describe :commits do
+ subject { project.commits }
+
+ before do
+ FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project
+ end
+
+ it { is_expected.to eq(gl_project.ci_commits) }
+ end
+
+ describe :builds do
+ subject { project.builds }
+
+ before do
+ commit = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project
+ FactoryGirl.create :ci_build, commit: commit
+ end
+
+ it { is_expected.to eq(gl_project.ci_builds) }
+ end
+
+ describe "ordered_by_last_commit_date" do
+ it "returns ordered projects" do
+ newest_project = FactoryGirl.create :empty_project
+ newest_ci_project = newest_project.ensure_gitlab_ci_project
+ oldest_project = FactoryGirl.create :empty_project
+ oldest_ci_project = oldest_project.ensure_gitlab_ci_project
+ project_without_commits = FactoryGirl.create :empty_project
+ ci_project_without_commits = project_without_commits.ensure_gitlab_ci_project
+
+ FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project
+ FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project
+
+ expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_ci_project, oldest_ci_project, ci_project_without_commits])
+ end
+ end
+
+ describe 'ordered commits' do
+ let(:project) { FactoryGirl.create :empty_project }
+
+ it 'returns ordered list of commits' do
+ commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
+ commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
+ expect(project.ci_commits).to eq([commit2, commit1])
+ end
+
+ it 'returns commits ordered by committed_at and id, with nulls last' do
+ commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
+ commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
+ commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
+ commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
+ expect(project.ci_commits).to eq([commit2, commit4, commit3, commit1])
+ end
+ end
+
+ context :valid_project do
+ let(:commit) { FactoryGirl.create(:ci_commit) }
+
+ context :project_with_commit_and_builds do
+ let(:project) { commit.project }
+
+ before do
+ FactoryGirl.create(:ci_build, commit: commit)
+ end
+
+ it { expect(project.status).to eq('pending') }
+ it { expect(project.last_commit).to be_kind_of(Ci::Commit) }
+ it { expect(project.human_status).to eq('pending') }
+ end
+ end
+
+ describe '#email_notification?' do
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: true
+ expect(project.email_notification?).to eq(true)
+ end
+
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'test tesft'
+ expect(project.email_notification?).to eq(true)
+ end
+
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: ''
+ expect(project.email_notification?).to eq(false)
+ end
+ end
+
+ describe '#broken_or_success?' do
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: true
+ allow(project).to receive(:broken?).and_return(true)
+ allow(project).to receive(:success?).and_return(true)
+ expect(project.broken_or_success?).to eq(true)
+ end
+
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: true
+ allow(project).to receive(:broken?).and_return(true)
+ allow(project).to receive(:success?).and_return(false)
+ expect(project.broken_or_success?).to eq(true)
+ end
+
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: true
+ allow(project).to receive(:broken?).and_return(false)
+ allow(project).to receive(:success?).and_return(true)
+ expect(project.broken_or_success?).to eq(true)
+ end
+
+ it do
+ project = FactoryGirl.create :ci_project, email_add_pusher: true
+ allow(project).to receive(:broken?).and_return(false)
+ allow(project).to receive(:success?).and_return(false)
+ expect(project.broken_or_success?).to eq(false)
+ end
+ end
+
+ describe 'Project.parse' do
+ let(:project) { FactoryGirl.create :project }
+
+ subject { Ci::Project.parse(project) }
+
+ it { is_expected.to be_valid }
+ it { is_expected.to be_kind_of(Ci::Project) }
+ it { expect(subject.name).to eq(project.name_with_namespace) }
+ it { expect(subject.gitlab_id).to eq(project.id) }
+ it { expect(subject.gitlab_url).to eq(project.web_url) }
+ end
+
+ describe :repo_url_with_auth do
+ let(:project) { FactoryGirl.create :ci_project }
+ subject { project.repo_url_with_auth }
+
+ it { is_expected.to be_a(String) }
+ it { is_expected.to end_with(".git") }
+ it { is_expected.to start_with(project.gitlab_url[0..6]) }
+ it { is_expected.to include(project.token) }
+ it { is_expected.to include('gitlab-ci-token') }
+ it { is_expected.to include(project.gitlab_url[7..-1]) }
+ end
+
+ describe :any_runners do
+ it "there are no runners available" do
+ project = FactoryGirl.create(:ci_project)
+ expect(project.any_runners?).to be_falsey
+ end
+
+ it "there is a specific runner" do
+ project = FactoryGirl.create(:ci_project)
+ project.runners << FactoryGirl.create(:ci_specific_runner)
+ expect(project.any_runners?).to be_truthy
+ end
+
+ it "there is a shared runner" do
+ project = FactoryGirl.create(:ci_project, shared_runners_enabled: true)
+ FactoryGirl.create(:ci_shared_runner)
+ expect(project.any_runners?).to be_truthy
+ end
+
+ it "there is a shared runner, but they are prohibited to use" do
+ project = FactoryGirl.create(:ci_project)
+ FactoryGirl.create(:ci_shared_runner)
+ expect(project.any_runners?).to be_falsey
+ end
+ end
+end
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
new file mode 100644
index 00000000000..0218d484130
--- /dev/null
+++ b/spec/models/ci/runner_project_spec.rb
@@ -0,0 +1,16 @@
+# == Schema Information
+#
+# Table name: runner_projects
+#
+# id :integer not null, primary key
+# runner_id :integer not null
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+#
+
+require 'spec_helper'
+
+describe Ci::RunnerProject do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
new file mode 100644
index 00000000000..757593a7ab8
--- /dev/null
+++ b/spec/models/ci/runner_spec.rb
@@ -0,0 +1,70 @@
+# == Schema Information
+#
+# Table name: runners
+#
+# id :integer not null, primary key
+# token :string(255)
+# created_at :datetime
+# updated_at :datetime
+# description :string(255)
+# contacted_at :datetime
+# active :boolean default(TRUE), not null
+# is_shared :boolean default(FALSE)
+# name :string(255)
+# version :string(255)
+# revision :string(255)
+# platform :string(255)
+# architecture :string(255)
+#
+
+require 'spec_helper'
+
+describe Ci::Runner do
+ describe '#display_name' do
+ it 'should return the description if it has a value' do
+ runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
+ expect(runner.display_name).to eq 'Linux/Ruby-1.9.3-p448'
+ end
+
+ it 'should return the token if it does not have a description' do
+ runner = FactoryGirl.create(:ci_runner)
+ expect(runner.display_name).to eq runner.description
+ end
+
+ it 'should return the token if the description is an empty string' do
+ runner = FactoryGirl.build(:ci_runner, description: '')
+ expect(runner.display_name).to eq runner.token
+ end
+ end
+
+ describe :assign_to do
+ let!(:project) { FactoryGirl.create :ci_project }
+ let!(:shared_runner) { FactoryGirl.create(:ci_shared_runner) }
+
+ before { shared_runner.assign_to(project) }
+
+ it { expect(shared_runner).to be_specific }
+ it { expect(shared_runner.projects).to eq([project]) }
+ it { expect(shared_runner.only_for?(project)).to be_truthy }
+ end
+
+ describe "belongs_to_one_project?" do
+ it "returns false if there are two projects runner assigned to" do
+ runner = FactoryGirl.create(:ci_specific_runner)
+ project = FactoryGirl.create(:ci_project)
+ project1 = FactoryGirl.create(:ci_project)
+ project.runners << runner
+ project1.runners << runner
+
+ expect(runner.belongs_to_one_project?).to be_falsey
+ end
+
+ it "returns true" do
+ runner = FactoryGirl.create(:ci_specific_runner)
+ project = FactoryGirl.create(:ci_project)
+ project.runners << runner
+
+ expect(runner.belongs_to_one_project?).to be_truthy
+ end
+ end
+end
diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb
new file mode 100644
index 00000000000..2df70e88888
--- /dev/null
+++ b/spec/models/ci/service_spec.rb
@@ -0,0 +1,48 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+#
+
+require 'spec_helper'
+
+describe Ci::Service do
+
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ end
+
+ describe "Mass assignment" do
+ end
+
+ describe "Test Button" do
+ before do
+ @service = Ci::Service.new
+ end
+
+ describe "Testable" do
+ let(:commit) { FactoryGirl.create :ci_commit }
+ let(:build) { FactoryGirl.create :ci_build, commit: commit }
+
+ before do
+ allow(@service).to receive_messages(
+ project: commit.project
+ )
+ build
+ @testable = @service.can_test?
+ end
+
+ describe :can_test do
+ it { expect(@testable).to eq(true) }
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
new file mode 100644
index 00000000000..19c14ef2da2
--- /dev/null
+++ b/spec/models/ci/trigger_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Ci::Trigger do
+ let(:project) { FactoryGirl.create :ci_project }
+
+ describe 'before_validation' do
+ it 'should set an random token if none provided' do
+ trigger = FactoryGirl.create :ci_trigger_without_token, project: project
+ expect(trigger.token).not_to be_nil
+ end
+
+ it 'should not set an random token if one provided' do
+ trigger = FactoryGirl.create :ci_trigger, project: project
+ expect(trigger.token).to eq('token')
+ end
+ end
+end
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
new file mode 100644
index 00000000000..d034a6c7b9f
--- /dev/null
+++ b/spec/models/ci/variable_spec.rb
@@ -0,0 +1,45 @@
+# == Schema Information
+#
+# Table name: variables
+#
+# id :integer not null, primary key
+# project_id :integer not null
+# key :string(255)
+# value :text
+# encrypted_value :text
+# encrypted_value_salt :string(255)
+# encrypted_value_iv :string(255)
+#
+
+require 'spec_helper'
+
+describe Ci::Variable do
+ subject { Ci::Variable.new }
+
+ let(:secret_value) { 'secret' }
+
+ before :each do
+ subject.value = secret_value
+ end
+
+ describe :value do
+ it 'stores the encrypted value' do
+ expect(subject.encrypted_value).not_to be_nil
+ end
+
+ it 'stores an iv for value' do
+ expect(subject.encrypted_value_iv).not_to be_nil
+ end
+
+ it 'stores a salt for value' do
+ expect(subject.encrypted_value_salt).not_to be_nil
+ end
+
+ it 'fails to decrypt if iv is incorrect' do
+ subject.encrypted_value_iv = nil
+ subject.instance_variable_set(:@value, nil)
+ expect { subject.value }.
+ to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
+ end
+ end
+end
diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb
new file mode 100644
index 00000000000..bf9481ab81d
--- /dev/null
+++ b/spec/models/ci/web_hook_spec.rb
@@ -0,0 +1,63 @@
+# == Schema Information
+#
+# Table name: web_hooks
+#
+# id :integer not null, primary key
+# url :string(255) not null
+# project_id :integer not null
+# created_at :datetime
+# updated_at :datetime
+#
+
+require 'spec_helper'
+
+describe Ci::WebHook do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ end
+
+ describe "Validations" do
+ it { is_expected.to validate_presence_of(:url) }
+
+ context "url format" do
+ it { is_expected.to allow_value("http://example.com").for(:url) }
+ it { is_expected.to allow_value("https://excample.com").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) }
+ it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) }
+
+ it { is_expected.not_to allow_value("example.com").for(:url) }
+ it { is_expected.not_to allow_value("ftp://example.com").for(:url) }
+ it { is_expected.not_to allow_value("herp-and-derp").for(:url) }
+ end
+ end
+
+ describe "execute" do
+ before(:each) do
+ @web_hook = FactoryGirl.create(:ci_web_hook)
+ @project = @web_hook.project
+ @data = { before: 'oldrev', after: 'newrev', ref: 'ref' }
+
+ WebMock.stub_request(:post, @web_hook.url)
+ end
+
+ it "POSTs to the web hook URL" do
+ @web_hook.execute(@data)
+ expect(WebMock).to have_requested(:post, @web_hook.url).once
+ end
+
+ it "POSTs the data as JSON" do
+ json = @data.to_json
+
+ @web_hook.execute(@data)
+ expect(WebMock).to have_requested(:post, @web_hook.url).with(body: json).once
+ end
+
+ it "catches exceptions" do
+ expect(Ci::WebHook).to receive(:post).and_raise("Some HTTP Post error")
+
+ expect{ @web_hook.execute(@data) }.
+ to raise_error(RuntimeError, 'Some HTTP Post error')
+ end
+ end
+end