build_eval 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"