build_eval 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1daad2c7e52f801829e07f22003bf4536cf91a51
4
+ data.tar.gz: 3ab822f2da5e6cad9f5a22d5f0a9b5368d162b98
5
+ SHA512:
6
+ metadata.gz: a35303425c8ac6b5cec64f15ae1daec79c08cafa96d2193a8e75ea7f701fc05144dc898a81f232df11d8888ca440f8f55257da4d2c1d880263925cda502ff1c8
7
+ data.tar.gz: 12d7ceb5d11e619699da7cc19965e8accecd442bddae5e4b81692b8434bcbebcfe6e038972cd16075bf686d434b41862e2fd7f145bbedc65f03475b975902b05
@@ -0,0 +1,39 @@
1
+ module BuildEval
2
+
3
+ class BuildResult
4
+
5
+ class << self
6
+
7
+ def create(args)
8
+ self.new(build_name: args[:build_name], status: BuildEval::Status.find(args[:status_name]))
9
+ end
10
+
11
+ def unknown(build_name)
12
+ self.new(build_name: build_name, status: BuildEval::Status::UNKNOWN)
13
+ end
14
+
15
+ end
16
+
17
+ attr_reader :build_name
18
+ attr_reader :status
19
+
20
+ private
21
+
22
+ def initialize(args)
23
+ @build_name = args[:build_name]
24
+ @status = args[:status]
25
+ end
26
+
27
+ public
28
+
29
+ def unsuccessful?
30
+ @status.unsuccessful?
31
+ end
32
+
33
+ def to_s
34
+ "#{@build_name}: #{@status}"
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,23 @@
1
+ module BuildEval
2
+
3
+ class BuildResults
4
+
5
+ def initialize(build_results)
6
+ @build_results = build_results
7
+ end
8
+
9
+ def status
10
+ BuildEval::Status.effective_status(@build_results.map(&:status))
11
+ end
12
+
13
+ def unsuccessful
14
+ @build_results.find_all(&:unsuccessful?)
15
+ end
16
+
17
+ def to_s
18
+ @build_results.map(&:to_s).join(", ")
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,25 @@
1
+ module BuildEval
2
+ module CIServer
3
+
4
+ class Decorator
5
+
6
+ def initialize(delegate)
7
+ @delegate = delegate
8
+ end
9
+
10
+ def build_result(name)
11
+ begin
12
+ @delegate.build_result(name)
13
+ rescue Exception
14
+ BuildEval::BuildResult.unknown(name)
15
+ end
16
+ end
17
+
18
+ def monitor(*build_names)
19
+ BuildEval::Monitor.new(server: @delegate, build_names: build_names.flatten)
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ module BuildEval
2
+ module CIServer
3
+
4
+ class TeamCity
5
+
6
+ def initialize(args)
7
+ @base_uri = args[:uri]
8
+ @username = args[:username]
9
+ @password = args[:password]
10
+ end
11
+
12
+ def build_result(name)
13
+ response = issue_request(name)
14
+ build_element = Nokogiri::XML(response.body).xpath("//build").first
15
+ raise "Unexpected build response: #{response.message}" unless build_element
16
+ BuildEval::BuildResult.create(build_name: name, status_name: build_element.attribute("status").value)
17
+ end
18
+
19
+ private
20
+
21
+ def issue_request(name)
22
+ uri = URI.parse("#{@base_uri}/httpAuth/app/rest/buildTypes/id:#{name}/builds")
23
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
24
+ request = Net::HTTP::Get.new(uri.request_uri)
25
+ request.basic_auth(@username, @password)
26
+ http.request(request)
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module BuildEval
2
+
3
+ class Monitor
4
+
5
+ def initialize(args)
6
+ @server = args[:server]
7
+ @build_names = args[:build_names]
8
+ end
9
+
10
+ def evaluate
11
+ BuildEval::BuildResults.new(@build_names.map { |build_name| @server.build_result(build_name) })
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module BuildEval
2
+
3
+ class Status
4
+
5
+ private
6
+
7
+ def initialize(args)
8
+ @severity = args[:severity]
9
+ @symbol = args[:symbol]
10
+ @description = args[:description]
11
+ end
12
+
13
+ public
14
+
15
+ SUCCESS = self.new(severity: 0, symbol: :success!, description: "succeeded")
16
+ FAILURE = self.new(severity: 2, symbol: :failed!, description: "failed")
17
+ UNKNOWN = self.new(severity: 1, symbol: :warning!, description: "unknown")
18
+
19
+ class << self
20
+
21
+ def find(name)
22
+ begin
23
+ self.const_get(name)
24
+ rescue NameError
25
+ raise "Build status '#{name}' is invalid"
26
+ end
27
+ end
28
+
29
+ def effective_status(statuses)
30
+ statuses.sort_by { |status| status.severity }.last
31
+ end
32
+
33
+ end
34
+
35
+ attr_reader :severity
36
+
37
+ def unsuccessful?
38
+ self != SUCCESS
39
+ end
40
+
41
+ def to_sym
42
+ @symbol
43
+ end
44
+
45
+ def to_s
46
+ @description
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,3 @@
1
+ module BuildEval
2
+ VERSION = "0.0.1".freeze
3
+ end
data/lib/build_eval.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'nokogiri'
2
+
3
+ require_relative 'build_eval/status'
4
+ require_relative 'build_eval/build_result'
5
+ require_relative 'build_eval/build_results'
6
+ require_relative 'build_eval/ci_server/decorator'
7
+ require_relative 'build_eval/ci_server/team_city'
8
+ require_relative 'build_eval/monitor'
9
+
10
+ module BuildEval
11
+
12
+ class << self
13
+
14
+ def server(args)
15
+ type_args = args.clone
16
+ server_type = type_args.delete(:type)
17
+ BuildEval::CIServer::Decorator.new(server_class_for(server_type).new(type_args))
18
+ end
19
+
20
+ private
21
+
22
+ def server_class_for(type)
23
+ begin
24
+ BuildEval::CIServer.const_get(type.to_s)
25
+ rescue NameError
26
+ raise "Server type '#{type}' is invalid"
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,85 @@
1
+ describe BuildEval::BuildResult do
2
+
3
+ describe "::create" do
4
+
5
+ let(:build_name) { "Some build name" }
6
+ let(:status_name) { "SUCCESS" }
7
+
8
+ subject { described_class.create(build_name: build_name, status_name: status_name) }
9
+
10
+ it "returns a result with the provided build name" do
11
+ expect(subject.build_name).to eql(build_name)
12
+
13
+ end
14
+
15
+ it "determines the status with the provided status name" do
16
+ expect(BuildEval::Status).to receive(:find).with(status_name)
17
+
18
+ subject
19
+ end
20
+
21
+ it "returns a result with the determined status" do
22
+ status = BuildEval::Status::UNKNOWN
23
+ allow(BuildEval::Status).to receive(:find).and_return(status)
24
+
25
+ expect(subject.status).to eql(status)
26
+ end
27
+
28
+ end
29
+
30
+ describe "::unknown" do
31
+
32
+ let(:build_name) { "Some build name" }
33
+
34
+ subject { described_class.unknown(build_name) }
35
+
36
+ it "returns a result with the provided build name" do
37
+ expect(subject.build_name).to eql(build_name)
38
+ end
39
+
40
+ it "returns a result with an unknown status" do
41
+ expect(subject.status).to eql(BuildEval::Status::UNKNOWN)
42
+ end
43
+
44
+ end
45
+
46
+ describe "#unsuccessful?" do
47
+
48
+ let(:status) { instance_double(BuildEval::Status) }
49
+ let(:build_result) { described_class.create(build_name: "some build", status_name: "some status") }
50
+
51
+ subject { build_result.unsuccessful? }
52
+
53
+ before(:example) { allow(BuildEval::Status).to receive(:find).and_return(status) }
54
+
55
+ it "delegates to the underlying status" do
56
+ allow(status).to receive(:unsuccessful?).and_return(true)
57
+
58
+ expect(subject).to be(true)
59
+ end
60
+
61
+ end
62
+
63
+ describe "#to_s" do
64
+
65
+ let(:build_name) { "Some build name" }
66
+ let(:status_string_representation) { "SUCCESS" }
67
+ let(:status) { instance_double(BuildEval::Status, to_s: status_string_representation) }
68
+
69
+ let(:build_result) { described_class.create(build_name: build_name, status_name: "some status") }
70
+
71
+ subject { build_result.to_s }
72
+
73
+ before(:example) { allow(BuildEval::Status).to receive(:find).and_return(status) }
74
+
75
+ it "contains the name of the build" do
76
+ expect(subject).to include(build_name)
77
+ end
78
+
79
+ it "contains the string representation of the status" do
80
+ expect(subject).to include(status_string_representation)
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,82 @@
1
+ describe BuildEval::BuildResults do
2
+
3
+ let(:results) { (1..3).map { instance_double(BuildEval::BuildResult) } }
4
+
5
+ let(:build_results) { described_class.new(results) }
6
+
7
+ describe "#status" do
8
+
9
+ let(:statuses) { results.map { instance_double(BuildEval::Status) } }
10
+
11
+ subject { build_results.status }
12
+
13
+ before(:example) do
14
+ results.zip(statuses).each { |result, status| allow(result).to receive(:status).and_return(status) }
15
+ end
16
+
17
+ it "determines the effective status of the build results" do
18
+ expect(BuildEval::Status).to receive(:effective_status).with(statuses)
19
+
20
+ subject
21
+ end
22
+
23
+ it "returns the effective status" do
24
+ status = instance_double(BuildEval::Status)
25
+ allow(BuildEval::Status).to receive(:effective_status).and_return(status)
26
+
27
+ expect(subject).to be(status)
28
+ end
29
+
30
+ end
31
+
32
+ describe "#unsuccessful" do
33
+
34
+ subject { build_results.unsuccessful }
35
+
36
+ before(:example) do
37
+ results.each do |build_result|
38
+ allow(build_result).to receive(:unsuccessful?).and_return(unsuccessful_results.include?(build_result))
39
+ end
40
+ end
41
+
42
+ context "when some build results are unsuccessful" do
43
+
44
+ let(:unsuccessful_results) { [ results[0], results[2] ] }
45
+
46
+ it "returns the unsuccessful build results" do
47
+ expect(subject).to eql(unsuccessful_results)
48
+ end
49
+
50
+ end
51
+
52
+ context "when no build results are unsuccessful" do
53
+
54
+ let(:unsuccessful_results) { [] }
55
+
56
+ it "returns an empty array" do
57
+ expect(subject).to eql([])
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ describe "#to_s" do
65
+
66
+ let(:result_string_representations) { results.each_with_index.map { |_, i| "Result description ##{i}" } }
67
+
68
+ subject { build_results.to_s }
69
+
70
+ before(:example) do
71
+ results.zip(result_string_representations).each do |build_result, string|
72
+ allow(build_result).to receive(:to_s).and_return(string)
73
+ end
74
+ end
75
+
76
+ it "contains the string representation of each result" do
77
+ result_string_representations.each { |string| expect(subject).to include(string) }
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,97 @@
1
+ describe BuildEval::CIServer::Decorator do
2
+
3
+ let(:decorated_server) { double("BuildEval::CIServer::Server") }
4
+
5
+ let(:decorator) { described_class.new(decorated_server) }
6
+
7
+ describe "#build_result" do
8
+
9
+ let(:build_name) { "some build name" }
10
+
11
+ subject { decorator.build_result(build_name) }
12
+
13
+ it "delegates to the decorated server" do
14
+ expect(decorated_server).to receive(:build_result).with(build_name)
15
+
16
+ subject
17
+ end
18
+
19
+ context "when the decorated server returns a result" do
20
+
21
+ let(:build_result) { instance_double(BuildEval::BuildResult) }
22
+
23
+ before(:example) { allow(decorated_server).to receive(:build_result).and_return(build_result) }
24
+
25
+ it "returns the result" do
26
+ expect(subject).to eql(build_result)
27
+ end
28
+
29
+ end
30
+
31
+ context "when the decorated server raises an error" do
32
+
33
+ before(:example) { allow(decorated_server).to receive(:build_result).and_raise("Forced error") }
34
+
35
+ it "creates an unknown result" do
36
+ expect(BuildEval::BuildResult).to receive(:unknown).with(build_name)
37
+
38
+ subject
39
+ end
40
+
41
+ it "returns the unknown result" do
42
+ unknown_build_result = instance_double(BuildEval::BuildResult)
43
+ allow(BuildEval::BuildResult).to receive(:unknown).and_return(unknown_build_result)
44
+
45
+ expect(subject).to eql(unknown_build_result)
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ describe "#monitor" do
53
+
54
+ let(:build_names) { (1..3).map { |i| "build##{i}" } }
55
+
56
+ subject { decorator.monitor(*build_names) }
57
+
58
+ it "creates a monitor for the decorated server" do
59
+ expect(BuildEval::Monitor).to receive(:new).with(hash_including(server: decorated_server))
60
+
61
+ subject
62
+ end
63
+
64
+ it "returns the monitor" do
65
+ monitor = instance_double(BuildEval::Monitor)
66
+ allow(BuildEval::Monitor).to receive(:new).and_return(monitor)
67
+
68
+ expect(subject).to eql(monitor)
69
+ end
70
+
71
+ context "when an array of build names is provided" do
72
+
73
+ subject { decorator.monitor(build_names) }
74
+
75
+ it "creates a monitor for the provided build names" do
76
+ expect(BuildEval::Monitor).to receive(:new).with(hash_including(build_names: build_names))
77
+
78
+ subject
79
+ end
80
+
81
+ end
82
+
83
+ context "when variable argument list of build names is provided" do
84
+
85
+ subject { decorator.monitor(*build_names) }
86
+
87
+ it "creates a monitor for the provided build names" do
88
+ expect(BuildEval::Monitor).to receive(:new).with(hash_including(build_names: build_names))
89
+
90
+ subject
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,111 @@
1
+ describe BuildEval::CIServer::TeamCity do
2
+
3
+ let(:scheme) { "http" }
4
+ let(:host) { "some.teamcity.server" }
5
+ let(:uri) { "#{scheme}://#{host}" }
6
+ let(:username) { "some_username" }
7
+ let(:password) { "some_password" }
8
+
9
+ let(:team_city) { described_class.new(uri: uri, username: username, password: password) }
10
+
11
+ describe "#build_result" do
12
+
13
+ let(:build_name) { "some_build_name" }
14
+ let(:last_status) { "FAILED" }
15
+
16
+ let(:expected_uri) do
17
+ "#{scheme}://#{username}:#{password}@#{host}/httpAuth/app/rest/buildTypes/id:#{build_name}/builds"
18
+ end
19
+
20
+ subject { team_city.build_result(build_name) }
21
+
22
+ before(:example) { FakeWeb.register_uri(:get, expected_uri, body: response_body, status: response_status) }
23
+
24
+ context "when the server responds with build results" do
25
+
26
+ let(:response_body) do
27
+ <<-RESPONSE
28
+ <builds count="3" href="/httpAuth/app/rest/buildTypes/#{build_name}/builds/" nextHref="/httpAuth/app/rest/buildTypes/#{build_name}/builds/?count=3&start=3">
29
+ <build id="87735" buildTypeId="#{build_name}" number="2062" status="#{last_status}" state="finished" href="/httpAuth/app/rest/builds/id:87735" webUrl="#{uri}/viewLog.html?buildId=87735&buildTypeId=#{build_name}"/>
30
+ <build id="87723" buildTypeId="#{build_name}" number="2061" status="SUCCESS" state="finished" href="/httpAuth/app/rest/builds/id:87723" webUrl="#{uri}/viewLog.html?buildId=87723&buildTypeId=#{build_name}"/>
31
+ <build id="87658" buildTypeId="#{build_name}" number="2060" status="SUCCESS" state="finished" href="/httpAuth/app/rest/builds/id:87658" webUrl="#{uri}/viewLog.html?buildId=87658&buildTypeId=#{build_name}"/>
32
+ </builds>
33
+ RESPONSE
34
+ end
35
+ let(:response_status) { [ "200", "OK" ] }
36
+
37
+ it "creates a build result containing the build name" do
38
+ expect(BuildEval::BuildResult).to receive(:create).with(hash_including(build_name: build_name))
39
+
40
+ subject
41
+ end
42
+
43
+ it "creates a build result containing the latest build status" do
44
+ expect(BuildEval::BuildResult).to receive(:create).with(hash_including(status_name: last_status))
45
+
46
+ subject
47
+ end
48
+
49
+ it "returns the created result" do
50
+ build_result = instance_double(BuildEval::BuildResult)
51
+ allow(BuildEval::BuildResult).to receive(:create).and_return(build_result)
52
+
53
+ expect(subject).to eql(build_result)
54
+ end
55
+
56
+ context "and the uri has a https scheme" do
57
+
58
+ let(:scheme) { "https" }
59
+
60
+ it "creates a build result from the response" do
61
+ expect(BuildEval::BuildResult).to receive(:create).with(hash_including(status_name: last_status))
62
+
63
+ subject
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ context "when the server authentication request fails" do
71
+
72
+ let(:response_body) { "Incorrect username or password" }
73
+ let(:response_status) { [ "401", "Unauthorized" ] }
74
+
75
+ it "raises an error" do
76
+ expect { subject }.to raise_error(/Unauthorized/)
77
+ end
78
+
79
+ end
80
+
81
+ context "when the build is not found" do
82
+
83
+ let(:response_body) do
84
+ <<-BODY
85
+ Error has occurred during request processing (Not Found).
86
+ Error: jetbrains.buildServer.server.rest.errors.NotFoundException: No build type nor template is found by id '#{build_name}'.
87
+ BODY
88
+ end
89
+ let(:response_status) { [ "404", "Not Found" ] }
90
+
91
+ it "raises an error" do
92
+ expect { subject }.to raise_error(/Not Found/)
93
+ end
94
+
95
+ end
96
+
97
+ context "when the server cannot be reached" do
98
+
99
+ let(:expected_uri) { "http://some.invalid.uri" }
100
+ let(:response_body) { nil }
101
+ let(:response_status) { nil }
102
+
103
+ it "raises an error" do
104
+ expect { subject }.to raise_error(/Not Found/)
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,37 @@
1
+ describe BuildEval::Monitor do
2
+
3
+ let(:server) { double("BuildEval::CIServer") }
4
+ let(:build_names) { (1..3).map { |i| "build##{i}" } }
5
+
6
+ let(:monitor) { described_class.new(server: server, build_names: build_names) }
7
+
8
+ describe "#evaluate" do
9
+
10
+ let(:results) { build_names.map { instance_double(BuildEval::BuildResult) } }
11
+
12
+ subject { monitor.evaluate }
13
+
14
+ before(:example) { allow(server).to receive(:build_result).and_return(*results) }
15
+
16
+ it "determines build results for builds of interest" do
17
+ build_names.each { |build_name| expect(server).to receive(:build_result).with(build_name) }
18
+
19
+ subject
20
+ end
21
+
22
+ it "composes a build results object containing the results" do
23
+ expect(BuildEval::BuildResults).to receive(:new).with(results)
24
+
25
+ subject
26
+ end
27
+
28
+ it "returns the build results object" do
29
+ build_results = instance_double(BuildEval::BuildResults)
30
+ expect(BuildEval::BuildResults).to receive(:new).and_return(build_results)
31
+
32
+ expect(subject).to eql(build_results)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,138 @@
1
+ describe BuildEval::Status do
2
+
3
+ describe "::find" do
4
+
5
+ subject { described_class.find(name) }
6
+
7
+ context "when the name exactly matches a status constant name" do
8
+
9
+ let(:name) { "UNKNOWN" }
10
+
11
+ it "returns the constant" do
12
+ expect(subject).to be(BuildEval::Status::UNKNOWN)
13
+ end
14
+
15
+ end
16
+
17
+ context "when the name is completely different from a status constant name" do
18
+
19
+ let(:name) { "does_not_match" }
20
+
21
+ it "raises an error indicating the name is invalid" do
22
+ expect { subject }.to raise_error("Build status '#{name}' is invalid")
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ describe "::effective_status" do
30
+
31
+ subject { described_class.effective_status(statuses) }
32
+
33
+ context "when a single status is provided" do
34
+
35
+ let(:statuses) { [ BuildEval::Status::UNKNOWN ] }
36
+
37
+ it "returns the status" do
38
+ expect(subject).to eql(BuildEval::Status::UNKNOWN)
39
+ end
40
+
41
+ end
42
+
43
+ context "when the statuses are ordered in descending severity" do
44
+
45
+ let(:statuses) { [ BuildEval::Status::FAILURE, BuildEval::Status::UNKNOWN, BuildEval::Status::SUCCESS ] }
46
+
47
+ it "returns the most severe status" do
48
+ expect(subject).to eql(BuildEval::Status::FAILURE)
49
+ end
50
+
51
+ end
52
+
53
+ context "when the statuses are ordered in ascending severity" do
54
+
55
+ let(:statuses) { [ BuildEval::Status::SUCCESS, BuildEval::Status::UNKNOWN, BuildEval::Status::FAILURE ] }
56
+
57
+ it "returns the most severe status" do
58
+ expect(subject).to eql(BuildEval::Status::FAILURE)
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ describe "#unsuccessful?" do
66
+
67
+ subject { status.unsuccessful? }
68
+
69
+ context "when the status is SUCCESS" do
70
+
71
+ let(:status) { BuildEval::Status::SUCCESS }
72
+
73
+ it "returns false" do
74
+ expect(subject).to be(false)
75
+ end
76
+
77
+ end
78
+
79
+ {
80
+ "FAILURE" => BuildEval::Status::FAILURE,
81
+ "UNKNOWN" => BuildEval::Status::UNKNOWN
82
+ }.each do |name, status|
83
+
84
+ context "when the status is #{name}" do
85
+
86
+ let(:status) { status }
87
+
88
+ it "returns true" do
89
+ expect(subject).to be(true)
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ describe "#to_sym" do
99
+
100
+ subject { status.to_sym }
101
+
102
+ { SUCCESS: :success!, FAILURE: :failed!, UNKNOWN: :warning! }.each do |name, expected_symbol|
103
+
104
+ context "when the status is #{name}" do
105
+
106
+ let(:status) { BuildEval::Status.const_get(name) }
107
+
108
+ it "returns success!" do
109
+ expect(subject).to eql(expected_symbol)
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+
118
+ describe "#to_s" do
119
+
120
+ subject { status.to_s }
121
+
122
+ { SUCCESS: "succeeded", FAILURE: "failed", UNKNOWN: "unknown" }.each do |name, expected_string|
123
+
124
+ context "when the status is #{name}" do
125
+
126
+ let(:status) { BuildEval::Status.const_get(name) }
127
+
128
+ it "returns success!" do
129
+ expect(subject).to eql(expected_string)
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,58 @@
1
+ describe BuildEval do
2
+
3
+ describe "::server" do
4
+
5
+ class BuildEval::CIServer::TestableServer
6
+
7
+ def initialize(_args)
8
+ # Intentionally blank
9
+ end
10
+
11
+ end
12
+
13
+ let(:server_type) { :TestableServer }
14
+ let(:server_arguments) { { key1: "value1", key2: "value2", key3: "value3" } }
15
+ let(:args) { { type: server_type }.merge(server_arguments) }
16
+
17
+ subject { described_class.server(args) }
18
+
19
+ it "constructs an instance of the server with the provided type" do
20
+ expect(BuildEval::CIServer::TestableServer).to receive(:new)
21
+
22
+ subject
23
+ end
24
+
25
+ it "constructs the instance with additional arguments" do
26
+ expect(BuildEval::CIServer::TestableServer).to receive(:new).with(server_arguments)
27
+
28
+ subject
29
+ end
30
+
31
+ it "decorates the server with standard server behaviour" do
32
+ server = instance_double(BuildEval::CIServer::TestableServer)
33
+ allow(BuildEval::CIServer::TestableServer).to receive(:new).and_return(server)
34
+ expect(BuildEval::CIServer::Decorator).to receive(:new).with(server)
35
+
36
+ subject
37
+ end
38
+
39
+ it "returns the decorated server" do
40
+ server_decorator = instance_double(BuildEval::CIServer::Decorator)
41
+ allow(BuildEval::CIServer::Decorator).to receive(:new).and_return(server_decorator)
42
+
43
+ expect(subject).to eql(server_decorator)
44
+ end
45
+
46
+ context "when a server of the provided type is not found" do
47
+
48
+ let(:server_type) { :invalid_type }
49
+
50
+ it "raises an error indicating the server type is invalid" do
51
+ expect { subject }.to raise_error("Server type '#{server_type}' is invalid")
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler.require(:development)
3
+
4
+ SimpleCov.start do
5
+ coverage_dir "tmp/coverage"
6
+
7
+ add_filter "/spec/"
8
+
9
+ minimum_coverage 100
10
+ refuse_coverage_drop
11
+ end if ENV["coverage"]
12
+
13
+ require_relative '../lib/build_eval'
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: build_eval
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Ueckerman
8
+ - Ryan Davis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-08-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.6'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.4'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.4'
42
+ - !ruby/object:Gem::Dependency
43
+ name: travis-lint
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '2.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: metric_fu
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '4.12'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '4.12'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.3'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.3'
84
+ - !ruby/object:Gem::Dependency
85
+ name: fakeweb
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.3'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.3'
98
+ - !ruby/object:Gem::Dependency
99
+ name: simplecov
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '0.10'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.10'
112
+ description: Evaluates the effective status of continuous integration builds. Useful
113
+ for subsequent display on information radiators.
114
+ email: matthew.ueckerman@myob.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - "./lib/build_eval.rb"
120
+ - "./lib/build_eval/build_result.rb"
121
+ - "./lib/build_eval/build_results.rb"
122
+ - "./lib/build_eval/ci_server/decorator.rb"
123
+ - "./lib/build_eval/ci_server/team_city.rb"
124
+ - "./lib/build_eval/monitor.rb"
125
+ - "./lib/build_eval/status.rb"
126
+ - "./lib/build_eval/version.rb"
127
+ - "./spec/lib/build_eval/build_result_spec.rb"
128
+ - "./spec/lib/build_eval/build_results_spec.rb"
129
+ - "./spec/lib/build_eval/ci_server/decorator_spec.rb"
130
+ - "./spec/lib/build_eval/ci_server/team_city_spec.rb"
131
+ - "./spec/lib/build_eval/monitor_spec.rb"
132
+ - "./spec/lib/build_eval/status_spec.rb"
133
+ - "./spec/lib/build_eval_spec.rb"
134
+ - "./spec/spec_helper.rb"
135
+ homepage: http://github.com/MYOB-Technology/build_eval
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 1.9.3
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project: build_eval
155
+ rubygems_version: 2.4.6
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Evaluates the effective status of continuous integration builds
159
+ test_files:
160
+ - "./spec/lib/build_eval/build_result_spec.rb"
161
+ - "./spec/lib/build_eval/build_results_spec.rb"
162
+ - "./spec/lib/build_eval/ci_server/decorator_spec.rb"
163
+ - "./spec/lib/build_eval/ci_server/team_city_spec.rb"
164
+ - "./spec/lib/build_eval/monitor_spec.rb"
165
+ - "./spec/lib/build_eval/status_spec.rb"
166
+ - "./spec/lib/build_eval_spec.rb"
167
+ - "./spec/spec_helper.rb"