summaryrefslogtreecommitdiff
path: root/spec/models
diff options
context:
space:
mode:
Diffstat (limited to 'spec/models')
-rw-r--r--spec/models/ci/build_spec.rb350
-rw-r--r--spec/models/ci/commit_spec.rb264
-rw-r--r--spec/models/ci/mail_service_spec.rb184
-rw-r--r--spec/models/ci/network_spec.rb54
-rw-r--r--spec/models/ci/project_services/hip_chat_message_spec.rb74
-rw-r--r--spec/models/ci/project_services/hip_chat_service_spec.rb75
-rw-r--r--spec/models/ci/project_services/slack_message_spec.rb84
-rw-r--r--spec/models/ci/project_services/slack_service_spec.rb58
-rw-r--r--spec/models/ci/project_spec.rb185
-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.rb49
-rw-r--r--spec/models/ci/trigger_spec.rb17
-rw-r--r--spec/models/ci/user_spec.rb100
-rw-r--r--spec/models/ci/variable_spec.rb44
-rw-r--r--spec/models/ci/web_hook_spec.rb64
16 files changed, 1688 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..d1e58438f7b
--- /dev/null
+++ b/spec/models/ci/build_spec.rb
@@ -0,0 +1,350 @@
+# == 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(:commit) { FactoryGirl.create :ci_commit, project: project }
+ let(:build) { FactoryGirl.create :ci_build, commit: commit }
+
+ it { should belong_to(:commit) }
+ it { should validate_presence_of :status }
+
+ it { should respond_to :success? }
+ it { should respond_to :failed? }
+ it { should respond_to :running? }
+ it { should respond_to :pending? }
+ it { should 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 { should be_a(Ci::Build) }
+ it('returns with the first pending build') { should 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 { should be_falsey }
+ end
+
+ %w(running success failed).each do |status|
+ context "if build status is #{status}" do
+ before { build.status = status }
+
+ it { should be_truthy }
+ end
+ end
+
+ %w(pending canceled).each do |status|
+ context "if build status is #{status}" do
+ before { build.status = status }
+
+ it { should 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 { should be_truthy }
+ end
+ end
+
+ %w(success failed canceled).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { should 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 { should be_truthy }
+ end
+ end
+
+ %w(pending running).each do |state|
+ context "if build.status is #{state}" do
+ before { build.status = state }
+
+ it { should 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 { should be_falsey }
+ end
+
+ context 'and build.status is failed' do
+ before { build.status = 'failed' }
+
+ it { should 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 { should be_falsey }
+ end
+
+ context 'and build.status is failed' do
+ before { build.status = 'failed' }
+
+ it { should be_truthy }
+ end
+ end
+ end
+
+ describe :trace do
+ subject { build.trace_html }
+
+ it { should be_empty }
+
+ context 'if build.trace contains text' do
+ let(:text) { 'example output' }
+ before { build.trace = text }
+
+ it { should include(text) }
+ it { should have_at_least(text.length).items }
+ end
+ end
+
+ describe :timeout do
+ subject { build.timeout }
+
+ it { should eq(commit.project.timeout) }
+ end
+
+ describe :duration do
+ subject { build.duration }
+
+ it { should 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 { should 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 { should be_a(Float) }
+ it { should > 0.0 }
+ end
+ end
+
+ describe :options do
+ let(:options) {
+ {
+ :image => "ruby:2.1",
+ :services => [
+ "postgres"
+ ]
+ }
+ }
+
+ subject { build.options }
+ it { should eq(options) }
+ end
+
+ describe :ref do
+ subject { build.ref }
+
+ it { should eq(commit.ref) }
+ end
+
+ describe :sha do
+ subject { build.sha }
+
+ it { should eq(commit.sha) }
+ end
+
+ describe :short_sha do
+ subject { build.short_sha }
+
+ it { should eq(commit.short_sha) }
+ end
+
+ describe :before_sha do
+ subject { build.before_sha }
+
+ it { should eq(commit.before_sha) }
+ end
+
+ describe :allow_git_fetch do
+ subject { build.allow_git_fetch }
+
+ it { should eq(project.allow_git_fetch) }
+ end
+
+ describe :project do
+ subject { build.project }
+
+ it { should eq(commit.project) }
+ end
+
+ describe :project_id do
+ subject { build.project_id }
+
+ it { should eq(commit.project_id) }
+ end
+
+ describe :project_name do
+ subject { build.project_name }
+
+ it { should eq(project.name) }
+ end
+
+ describe :repo_url do
+ subject { build.repo_url }
+
+ it { should 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 { should eq(98.29) }
+ end
+
+ context 'valid content & bad regex' do
+ subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') }
+
+ it { should be_nil }
+ end
+
+ context 'no coverage content & regex' do
+ subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') }
+
+ it { should be_nil }
+ end
+
+ context 'multiple results in content & regex' do
+ subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') }
+
+ it { should eq(98.29) }
+ end
+ end
+
+ describe :variables do
+ context 'returns variables' do
+ subject { build.variables }
+
+ let(:variables) {
+ [
+ {key: :DB_NAME, value: 'postgres', public: true}
+ ]
+ }
+
+ it { should eq(variables) }
+
+ context 'and secure variables' do
+ let(:secure_variables) {
+ [
+ {key: 'SECRET_KEY', value: 'secret_value', public: false}
+ ]
+ }
+
+ before do
+ build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
+ end
+
+ it { should 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) {
+ [
+ {key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false}
+ ]
+ }
+
+ before do
+ build.trigger_request = trigger_request
+ end
+
+ it { should eq(variables + secure_variables + trigger_variables) }
+ end
+ end
+ 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..6d5b0597e13
--- /dev/null
+++ b/spec/models/ci/commit_spec.rb
@@ -0,0 +1,264 @@
+# == 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(:commit) { FactoryGirl.create :ci_commit, project: project }
+ let(:commit_with_project) { FactoryGirl.create :ci_commit, project: project }
+ let(:config_processor) { GitlabCiYamlProcessor.new(gitlab_ci_yaml) }
+
+ it { should belong_to(:project) }
+ it { should have_many(:builds) }
+ it { should validate_presence_of :before_sha }
+ it { should validate_presence_of :sha }
+ it { should validate_presence_of :ref }
+ it { should validate_presence_of :push_data }
+
+ it { should respond_to :git_author_name }
+ it { should respond_to :git_author_email }
+ it { should 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 { should be_a(Ci::Build) }
+ it('returns with the most recently created build') { should 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 :project_recipients do
+
+ context 'always sending notification' do
+ it 'should return commit_pusher_email as only recipient when no additional recipients are given' do
+ project = FactoryGirl.create :ci_project,
+ email_add_pusher: true,
+ email_recipients: ''
+ commit = FactoryGirl.create :ci_commit, project: project
+ expected = 'commit_pusher_email'
+ commit.stub(:push_data) { { user_email: expected } }
+ commit.project_recipients.should == [expected]
+ end
+
+ it 'should return commit_pusher_email and additional recipients' do
+ project = FactoryGirl.create :ci_project,
+ email_add_pusher: true,
+ email_recipients: 'rec1 rec2'
+ commit = FactoryGirl.create :ci_commit, project: project
+ expected = 'commit_pusher_email'
+ commit.stub(:push_data) { { user_email: expected } }
+ commit.project_recipients.should == ['rec1', 'rec2', expected]
+ end
+
+ it 'should return recipients' do
+ project = FactoryGirl.create :ci_project,
+ email_add_pusher: false,
+ email_recipients: 'rec1 rec2'
+ commit = FactoryGirl.create :ci_commit, project: project
+ commit.project_recipients.should == ['rec1', 'rec2']
+ end
+
+ it 'should return unique recipients only' do
+ project = FactoryGirl.create :ci_project,
+ email_add_pusher: true,
+ email_recipients: 'rec1 rec1 rec2'
+ commit = FactoryGirl.create :ci_commit, project: project
+ expected = 'rec2'
+ commit.stub(:push_data) { { user_email: expected } }
+ commit.project_recipients.should == ['rec1', 'rec2']
+ end
+ 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') { commit.errors.should_not be_empty }
+ end
+ end
+
+ describe :compare? do
+ subject { commit_with_project.compare? }
+
+ context 'if commit.before_sha are not nil' do
+ it { should be_true }
+ end
+ end
+
+ describe :short_sha do
+ subject { commit.short_before_sha }
+
+ it { should have(8).items }
+ it { commit.before_sha.should start_with(subject) }
+ end
+
+ describe :short_sha do
+ subject { commit.short_sha }
+
+ it { should have(8).items }
+ it { commit.sha.should start_with(subject) }
+ end
+
+ describe :create_next_builds do
+ before do
+ commit.stub(:config_processor).and_return(config_processor)
+ end
+
+ it "creates builds for next type" do
+ commit.create_builds.should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+
+ commit.create_next_builds(nil).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 4
+
+ commit.create_next_builds(nil).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 5
+
+ commit.create_next_builds(nil).should be_false
+ end
+ end
+
+ describe :create_builds do
+ before do
+ commit.stub(:config_processor).and_return(config_processor)
+ end
+
+ it 'creates builds' do
+ commit.create_builds.should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+ 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
+ commit.create_builds(trigger_request).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+ end
+
+ it 'rebuilds commit' do
+ commit.create_builds.should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+
+ commit.create_builds(trigger_request).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 4
+ end
+
+ it 'creates next builds' do
+ commit.create_builds(trigger_request).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+
+ commit.create_next_builds(trigger_request).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 4
+ end
+
+ context 'for [ci skip]' do
+ before do
+ commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]'
+ commit.save
+ end
+
+ it 'rebuilds commit' do
+ commit.status.should == 'skipped'
+ commit.create_builds(trigger_request).should be_true
+ commit.builds.reload
+ commit.builds.size.should == 2
+ commit.status.should == 'pending'
+ end
+ end
+ end
+ end
+
+ describe "#finished_at" do
+ let(:project) { FactoryGirl.create :ci_project }
+ let(:commit) { FactoryGirl.create :ci_commit, project: project }
+
+ it "returns finished_at of latest build" do
+ build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60
+ build1 = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120
+
+ commit.finished_at.to_i.should == build.finished_at.to_i
+ end
+
+ it "returns nil if there is no finished build" do
+ build = FactoryGirl.create :ci_not_started_build, commit: commit
+
+ commit.finished_at.should be_nil
+ end
+ end
+
+ describe "coverage" do
+ let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" }
+ let(:commit) { FactoryGirl.create :ci_commit, project: 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
+ commit.coverage.should == "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
+ commit.coverage.should == "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
+ commit.coverage.should == "35.00"
+ end
+
+ it "calculates average when there is one build without coverage" do
+ FactoryGirl.create :ci_build, commit: commit
+ commit.coverage.should be_nil
+ end
+ end
+end
diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/mail_service_spec.rb
new file mode 100644
index 00000000000..4830d98bdf8
--- /dev/null
+++ b/spec/models/ci/mail_service_spec.rb
@@ -0,0 +1,184 @@
+# == 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 { should 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 }
+
+ describe 'failed build' do
+ let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
+ let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
+
+ before do
+ mail.stub(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ Notify.should_receive(:build_fail_email).with(build.id, email)
+ Notify.should_not_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(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+
+ before do
+ mail.stub(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ Notify.should_receive(:build_success_email).with(build.id, email)
+ Notify.should_not_receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successfull build and project has email_recipients' do
+ let(:project) {
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: false,
+ email_recipients: "jeroen@example.com")
+ }
+ let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+
+ before do
+ mail.stub(
+ project: project
+ )
+ end
+
+ it do
+ should_email("git@example.com")
+ should_email("jeroen@example.com")
+ mail.execute(build)
+ end
+
+ def should_email(email)
+ Notify.should_receive(:build_success_email).with(build.id, email)
+ Notify.should_not_receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successful build and notify only broken builds' do
+ let(:project) {
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: true,
+ email_recipients: "jeroen@example.com")
+ }
+ let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+
+ before do
+ mail.stub(
+ 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)
+ Notify.should_not_receive(:build_success_email).with(build.id, email)
+ Notify.should_not_receive(:build_fail_email).with(build.id, email)
+ end
+ end
+
+ describe 'successful build and can test service' do
+ let(:project) {
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: false,
+ email_recipients: "jeroen@example.com")
+ }
+ let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+
+ before do
+ mail.stub(
+ project: project
+ )
+ build
+ end
+
+ it do
+ mail.can_test?.should == true
+ end
+ end
+
+ describe 'retried build should not receive email' do
+ let(:project) {
+ FactoryGirl.create(:ci_project,
+ email_add_pusher: true,
+ email_only_broken_builds: true,
+ email_recipients: "jeroen@example.com")
+ }
+ let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
+
+ before do
+ mail.stub(
+ project: project
+ )
+ end
+
+ it do
+ 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)
+ Notify.should_not_receive(:build_success_email).with(build.id, email)
+ Notify.should_not_receive(:build_fail_email).with(build.id, email)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/network_spec.rb b/spec/models/ci/network_spec.rb
new file mode 100644
index 00000000000..b80adba5b08
--- /dev/null
+++ b/spec/models/ci/network_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe Network do
+ let(:network) { Network.new }
+
+ describe :enable_ci do
+ subject { network.enable_ci '', '', '' }
+
+ context 'on success' do
+ before do
+ response = double
+ response.stub(:code) { 200 }
+ network.class.stub(:put) { response }
+ end
+
+ it { should be_true }
+ end
+
+ context 'on failure' do
+ before do
+ response = double
+ response.stub(:code) { 404 }
+ network.class.stub(:put) { response }
+ end
+
+ it { should be_nil }
+ end
+ end
+
+ describe :disable_ci do
+ let(:response) { double }
+ subject { network.disable_ci '', '' }
+
+ context 'on success' do
+ let(:parsed_response) { 'parsed' }
+ before do
+ response.stub(:code) { 200 }
+ response.stub(:parsed_response) { parsed_response }
+ network.class.stub(:delete) { response }
+ end
+
+ it { should equal(parsed_response) }
+ end
+
+ context 'on failure' do
+ before do
+ response.stub(:code) { 404 }
+ network.class.stub(:delete) { response }
+ end
+
+ it { should be_nil }
+ 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..3571cb94793
--- /dev/null
+++ b/spec/models/ci/project_services/hip_chat_message_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+describe Ci::HipChatMessage do
+ subject { HipChatMessage.new(build) }
+
+ let(:project) { FactoryGirl.create(:project) }
+
+ context "One build" do
+ let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) }
+
+ let(:build) do
+ commit.create_builds
+ commit.builds.first
+ end
+
+ context 'when build succeeds' do
+ it 'returns a successful message' do
+ build.update(status: "success")
+
+ expect( subject.status_color ).to eq 'green'
+ expect( subject.notify? ).to be_false
+ expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
+ expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
+ end
+ end
+
+ context 'when build fails' do
+ it 'returns a failure message' do
+ build.update(status: "failed")
+
+ expect( subject.status_color ).to eq 'red'
+ expect( subject.notify? ).to be_true
+ expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
+ expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
+ end
+ end
+ end
+
+ context "Several builds" do
+ let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) }
+
+ let(:build) do
+ commit.builds.first
+ end
+
+ context 'when all matrix builds succeed' do
+ it 'returns a successful message' do
+ commit.create_builds
+ commit.builds.update_all(status: "success")
+ commit.reload
+
+ expect( subject.status_color ).to eq 'green'
+ expect( subject.notify? ).to be_false
+ 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
+ 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_true
+ expect( subject.to_s ).to match(/Commit #\d+/)
+ expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
+ end
+ 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..71dba8fc358
--- /dev/null
+++ b/spec/models/ci/project_services/hip_chat_service_spec.rb
@@ -0,0 +1,75 @@
+# == 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 { should validate_presence_of :hipchat_room }
+ it { should validate_presence_of :hipchat_token }
+
+ end
+ end
+
+ describe "Execute" do
+
+ let(:service) { HipChatService.new }
+ let(:project) { FactoryGirl.create :project }
+ let(:commit) { FactoryGirl.create :commit, project: project }
+ let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' }
+ let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' }
+
+ before do
+ service.stub(
+ project: project,
+ project_id: 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)
+ HipChatNotifierWorker.drain
+
+ expect( WebMock ).to have_requested(:post, api_url).once
+ end
+
+ it "calls the worker with expected arguments" do
+ expect( 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/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb
new file mode 100644
index 00000000000..4a7284fe460
--- /dev/null
+++ b/spec/models/ci/project_services/slack_message_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Ci::SlackMessage do
+ subject { SlackMessage.new(commit) }
+
+ let(:project) { FactoryGirl.create :project }
+
+ context "One build" do
+ let(:commit) { FactoryGirl.create(:commit_with_one_job, project: project) }
+
+ let(:build) do
+ commit.create_builds
+ commit.builds.first
+ end
+
+ context 'when build succeeded' do
+ let(:color) { 'good' }
+
+ it 'returns a message with succeeded build' do
+ build.update(status: "success")
+
+ subject.color.should == color
+ subject.fallback.should include('Build')
+ subject.fallback.should include("\##{build.id}")
+ subject.fallback.should include('succeeded')
+ subject.attachments.first[:fields].should be_empty
+ end
+ end
+
+ context 'when build failed' do
+ let(:color) { 'danger' }
+
+ it 'returns a message with failed build' do
+ build.update(status: "failed")
+
+ subject.color.should == color
+ subject.fallback.should include('Build')
+ subject.fallback.should include("\##{build.id}")
+ subject.fallback.should include('failed')
+ subject.attachments.first[:fields].should be_empty
+ end
+ end
+ end
+
+ context "Several builds" do
+ let(:commit) { FactoryGirl.create(:commit_with_two_jobs, project: project) }
+
+ context 'when all matrix builds succeeded' do
+ let(:color) { 'good' }
+
+ it 'returns a message with success' do
+ commit.create_builds
+ commit.builds.update_all(status: "success")
+ commit.reload
+
+ subject.color.should == color
+ subject.fallback.should include('Commit')
+ subject.fallback.should include("\##{commit.id}")
+ subject.fallback.should include('succeeded')
+ subject.attachments.first[:fields].should 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
+ first_build = commit.builds.first
+ second_build = commit.builds.last
+ first_build.update(status: "success")
+ second_build.update(status: "failed")
+
+ subject.color.should == color
+ subject.fallback.should include('Commit')
+ subject.fallback.should include("\##{commit.id}")
+ subject.fallback.should include('failed')
+ subject.attachments.first[:fields].size.should == 1
+ subject.attachments.first[:fields].first[:title].should == second_build.name
+ subject.attachments.first[:fields].first[:value].should include("\##{second_build.id}")
+ end
+ 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..952349a9def
--- /dev/null
+++ b/spec/models/ci/project_services/slack_service_spec.rb
@@ -0,0 +1,58 @@
+# == 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 { should belong_to :project }
+ end
+
+ describe "Validations" do
+ context "active" do
+ before do
+ subject.active = true
+ end
+
+ it { should validate_presence_of :webhook }
+ end
+ end
+
+ describe "Execute" do
+ let(:slack) { SlackService.new }
+ let(:project) { FactoryGirl.create :project }
+ let(:commit) { FactoryGirl.create :commit, project: project }
+ let(:build) { FactoryGirl.create :build, commit: commit, status: 'failed' }
+ let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
+ let(:notify_only_broken_builds) { false }
+
+ before do
+ slack.stub(
+ project: project,
+ project_id: 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)
+ SlackNotifierWorker.drain
+
+ WebMock.should 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..aa76b99154b
--- /dev/null
+++ b/spec/models/ci/project_spec.rb
@@ -0,0 +1,185 @@
+# == 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 Project do
+ subject { FactoryGirl.build :project }
+
+ it { should have_many(:commits) }
+
+ it { should validate_presence_of :name }
+ it { should validate_presence_of :timeout }
+ it { should validate_presence_of :default_ref }
+
+ describe 'before_validation' do
+ it 'should set an random token if none provided' do
+ project = FactoryGirl.create :project_without_token
+ project.token.should_not == ""
+ end
+
+ it 'should not set an random toke if one provided' do
+ project = FactoryGirl.create :project
+ project.token.should == "iPWx6WM4lhHNedGfBpPJNP"
+ end
+ end
+
+ describe "ordered_by_last_commit_date" do
+ it "returns ordered projects" do
+ newest_project = FactoryGirl.create :project
+ oldest_project = FactoryGirl.create :project
+ project_without_commits = FactoryGirl.create :project
+
+ FactoryGirl.create :commit, committed_at: 1.hour.ago, project: newest_project
+ FactoryGirl.create :commit, committed_at: 2.hour.ago, project: oldest_project
+
+ Project.ordered_by_last_commit_date.should == [newest_project, oldest_project, project_without_commits]
+ end
+ end
+
+ context :valid_project do
+ let(:project) { FactoryGirl.create :project }
+
+ context :project_with_commit_and_builds do
+ before do
+ commit = FactoryGirl.create(:commit, project: project)
+ FactoryGirl.create(:build, commit: commit)
+ end
+
+ it { project.status.should == 'pending' }
+ it { project.last_commit.should be_kind_of(Commit) }
+ it { project.human_status.should == 'pending' }
+ end
+ end
+
+ describe '#email_notification?' do
+ it do
+ project = FactoryGirl.create :project, email_add_pusher: true
+ project.email_notification?.should == true
+ end
+
+ it do
+ project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: 'test tesft'
+ project.email_notification?.should == true
+ end
+
+ it do
+ project = FactoryGirl.create :project, email_add_pusher: false, email_recipients: ''
+ project.email_notification?.should == false
+ end
+ end
+
+ describe '#broken_or_success?' do
+ it {
+ project = FactoryGirl.create :project, email_add_pusher: true
+ project.stub(:broken?).and_return(true)
+ project.stub(:success?).and_return(true)
+ project.broken_or_success?.should == true
+ }
+
+ it {
+ project = FactoryGirl.create :project, email_add_pusher: true
+ project.stub(:broken?).and_return(true)
+ project.stub(:success?).and_return(false)
+ project.broken_or_success?.should == true
+ }
+
+ it {
+ project = FactoryGirl.create :project, email_add_pusher: true
+ project.stub(:broken?).and_return(false)
+ project.stub(:success?).and_return(true)
+ project.broken_or_success?.should == true
+ }
+
+ it {
+ project = FactoryGirl.create :project, email_add_pusher: true
+ project.stub(:broken?).and_return(false)
+ project.stub(:success?).and_return(false)
+ project.broken_or_success?.should == false
+ }
+ end
+
+ describe 'Project.parse' do
+ let(:project_dump) { YAML.load File.read(Rails.root.join('spec/support/gitlab_stubs/raw_project.yml')) }
+ let(:parsed_project) { Project.parse(project_dump) }
+
+
+ it { parsed_project.should be_valid }
+ it { parsed_project.should be_kind_of(Project) }
+ it { parsed_project.name.should eq("GitLab / api.gitlab.org") }
+ it { parsed_project.gitlab_id.should eq(189) }
+ it { parsed_project.gitlab_url.should eq("http://demo.gitlab.com/gitlab/api-gitlab-org") }
+
+ it "parses plain hash" do
+ Project.parse(project_dump).name.should eq("GitLab / api.gitlab.org")
+ end
+ end
+
+ describe :repo_url_with_auth do
+ let(:project) { FactoryGirl.create :project }
+ subject { project.repo_url_with_auth }
+
+ it { should be_a(String) }
+ it { should end_with(".git") }
+ it { should start_with(project.gitlab_url[0..6]) }
+ it { should include(project.token) }
+ it { should include('gitlab-ci-token') }
+ it { should include(project.gitlab_url[7..-1]) }
+ end
+
+ describe :search do
+ let!(:project) { FactoryGirl.create(:project, name: "foo") }
+
+ it { Project.search('fo').should include(project) }
+ it { Project.search('bar').should be_empty }
+ end
+
+ describe :any_runners do
+ it "there are no runners available" do
+ project = FactoryGirl.create(:project)
+ project.any_runners?.should be_false
+ end
+
+ it "there is a specific runner" do
+ project = FactoryGirl.create(:project)
+ project.runners << FactoryGirl.create(:specific_runner)
+ project.any_runners?.should be_true
+ end
+
+ it "there is a shared runner" do
+ project = FactoryGirl.create(:project, shared_runners_enabled: true)
+ FactoryGirl.create(:shared_runner)
+ project.any_runners?.should be_true
+ end
+
+ it "there is a shared runner, but they are prohibited to use" do
+ project = FactoryGirl.create(:project)
+ FactoryGirl.create(:shared_runner)
+ project.any_runners?.should be_false
+ 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..8677d86aa02
--- /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(: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(: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(:runner, description: '')
+ expect(runner.display_name).to eq runner.token
+ end
+ end
+
+ describe :assign_to do
+ let!(:project) { FactoryGirl.create :project }
+ let!(:shared_runner) { FactoryGirl.create(:shared_runner) }
+
+ before { shared_runner.assign_to(project) }
+
+ it { shared_runner.should be_specific }
+ it { shared_runner.projects.should == [project] }
+ it { shared_runner.only_for?(project).should be_true }
+ end
+
+ describe "belongs_to_one_project?" do
+ it "returns false if there are two projects runner assigned to" do
+ runner = FactoryGirl.create(:specific_runner)
+ project = FactoryGirl.create(:project)
+ project1 = FactoryGirl.create(:project)
+ project.runners << runner
+ project1.runners << runner
+
+ runner.belongs_to_one_project?.should be_false
+ end
+
+ it "returns true" do
+ runner = FactoryGirl.create(:specific_runner)
+ project = FactoryGirl.create(:project)
+ project.runners << runner
+
+ runner.belongs_to_one_project?.should be_true
+ 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..5a90229ec43
--- /dev/null
+++ b/spec/models/ci/service_spec.rb
@@ -0,0 +1,49 @@
+# == 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 { should belong_to :project }
+ end
+
+ describe "Mass assignment" do
+ end
+
+ describe "Test Button" do
+ before do
+ @service = Service.new
+ end
+
+ describe "Testable" do
+ let (:project) { FactoryGirl.create :project }
+ let (:commit) { FactoryGirl.create :commit, project: project }
+ let (:build) { FactoryGirl.create :build, commit: commit }
+
+ before do
+ @service.stub(
+ project: project
+ )
+ build
+ @testable = @service.can_test?
+ end
+
+ describe :can_test do
+ it { @testable.should == 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..7c928f9d9dc
--- /dev/null
+++ b/spec/models/ci/trigger_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Ci::Trigger do
+ let(:project) { FactoryGirl.create :project }
+
+ describe 'before_validation' do
+ it 'should set an random token if none provided' do
+ trigger = FactoryGirl.create :trigger_without_token, project: project
+ trigger.token.should_not be_nil
+ end
+
+ it 'should not set an random token if one provided' do
+ trigger = FactoryGirl.create :trigger, project: project
+ trigger.token.should == 'token'
+ end
+ end
+end
diff --git a/spec/models/ci/user_spec.rb b/spec/models/ci/user_spec.rb
new file mode 100644
index 00000000000..d1b87988b74
--- /dev/null
+++ b/spec/models/ci/user_spec.rb
@@ -0,0 +1,100 @@
+require 'spec_helper'
+
+describe Ci::User do
+
+ describe "has_developer_access?" do
+ before do
+ @user = User.new({})
+ end
+
+ let(:project_with_owner_access) do
+ {
+ "name" => "gitlab-shell",
+ "permissions" => {
+ "project_access" => {
+ "access_level"=> 10,
+ "notification_level" => 3
+ },
+ "group_access" => {
+ "access_level" => 50,
+ "notification_level" => 3
+ }
+ }
+ }
+ end
+
+ let(:project_with_reporter_access) do
+ {
+ "name" => "gitlab-shell",
+ "permissions" => {
+ "project_access" => {
+ "access_level" => 20,
+ "notification_level" => 3
+ },
+ "group_access" => {
+ "access_level" => 10,
+ "notification_level" => 3
+ }
+ }
+ }
+ end
+
+ it "returns false for reporter" do
+ @user.stub(:project_info).and_return(project_with_reporter_access)
+
+ @user.has_developer_access?(1).should be_false
+ end
+
+ it "returns true for owner" do
+ @user.stub(:project_info).and_return(project_with_owner_access)
+
+ @user.has_developer_access?(1).should be_true
+ end
+ end
+
+ describe "authorized_projects" do
+ let (:user) { User.new({}) }
+
+ before do
+ FactoryGirl.create :project, gitlab_id: 1
+ FactoryGirl.create :project, gitlab_id: 2
+ gitlab_project = OpenStruct.new({id: 1})
+ gitlab_project1 = OpenStruct.new({id: 2})
+ User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1])
+ end
+
+ it "returns projects" do
+ User.any_instance.stub(:can_manage_project?).and_return(true)
+
+ user.authorized_projects.count.should == 2
+ end
+
+ it "empty list if user miss manage permission" do
+ User.any_instance.stub(:can_manage_project?).and_return(false)
+
+ user.authorized_projects.count.should == 0
+ end
+ end
+
+ describe "authorized_runners" do
+ it "returns authorized runners" do
+ project = FactoryGirl.create :project, gitlab_id: 1
+ project1 = FactoryGirl.create :project, gitlab_id: 2
+ gitlab_project = OpenStruct.new({id: 1})
+ gitlab_project1 = OpenStruct.new({id: 2})
+ User.any_instance.stub(:gitlab_projects).and_return([gitlab_project, gitlab_project1])
+ User.any_instance.stub(:can_manage_project?).and_return(true)
+ user = User.new({})
+
+ runner = FactoryGirl.create :specific_runner
+ runner1 = FactoryGirl.create :specific_runner
+ runner2 = FactoryGirl.create :specific_runner
+
+ project.runners << runner
+ project1.runners << runner1
+
+ user.authorized_runners.should include(runner, runner1)
+ user.authorized_runners.should_not include(runner2)
+ 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..447512bf6df
--- /dev/null
+++ b/spec/models/ci/variable_spec.rb
@@ -0,0 +1,44 @@
+# == 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 { Variable.new }
+
+ let(:secret_value) { 'secret' }
+
+ before :each do
+ subject.value = secret_value
+ end
+
+ describe :value do
+ it 'stores the encrypted value' do
+ subject.encrypted_value.should_not be_nil
+ end
+
+ it 'stores an iv for value' do
+ subject.encrypted_value_iv.should_not be_nil
+ end
+
+ it 'stores a salt for value' do
+ subject.encrypted_value_salt.should_not 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
+ 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..4211576ce5e
--- /dev/null
+++ b/spec/models/ci/web_hook_spec.rb
@@ -0,0 +1,64 @@
+# == 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 { should belong_to :project }
+ end
+
+ describe "Validations" do
+ it { should validate_presence_of(:url) }
+
+ context "url format" do
+ it { should allow_value("http://example.com").for(:url) }
+ it { should allow_value("https://excample.com").for(:url) }
+ it { should allow_value("http://test.com/api").for(:url) }
+ it { should allow_value("http://test.com/api?key=abc").for(:url) }
+ it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) }
+
+ it { should_not allow_value("example.com").for(:url) }
+ it { should_not allow_value("ftp://example.com").for(:url) }
+ it { should_not allow_value("herp-and-derp").for(:url) }
+ end
+ end
+
+ describe "execute" do
+ before(:each) do
+ @web_hook = FactoryGirl.create(: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)
+ WebMock.should have_requested(:post, @web_hook.url).once
+ end
+
+ it "POSTs the data as JSON" do
+ json = @data.to_json
+
+ @web_hook.execute(@data)
+ WebMock.should have_requested(:post, @web_hook.url).with(body: json).once
+ end
+
+ it "catches exceptions" do
+ WebHook.should_receive(:post).and_raise("Some HTTP Post error")
+
+ lambda {
+ @web_hook.execute(@data)
+ }.should raise_error
+ end
+ end
+end