build_eval 0.0.1 → 0.0.2

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/build_eval/http.rb +16 -0
  3. data/lib/build_eval/monitor/base.rb +13 -0
  4. data/lib/build_eval/monitor/composite.rb +17 -0
  5. data/lib/build_eval/monitor/server.rb +18 -0
  6. data/lib/build_eval/result/build_result.rb +41 -0
  7. data/lib/build_eval/result/composite_result.rb +25 -0
  8. data/lib/build_eval/result/server_result.rb +26 -0
  9. data/lib/build_eval/result/status.rb +54 -0
  10. data/lib/build_eval/{ci_server → server}/decorator.rb +3 -3
  11. data/lib/build_eval/server/team_city.rb +28 -0
  12. data/lib/build_eval/server/travis.rb +26 -0
  13. data/lib/build_eval/version.rb +1 -1
  14. data/lib/build_eval.rb +13 -8
  15. data/spec/lib/build_eval/http_spec.rb +82 -0
  16. data/spec/lib/build_eval/monitor/base_spec.rb +29 -0
  17. data/spec/lib/build_eval/monitor/composite_spec.rb +38 -0
  18. data/spec/lib/build_eval/monitor/server_spec.rb +43 -0
  19. data/spec/lib/build_eval/{build_result_spec.rb → result/build_result_spec.rb} +9 -9
  20. data/spec/lib/build_eval/result/composite_result_spec.rb +84 -0
  21. data/spec/lib/build_eval/result/server_result_spec.rb +92 -0
  22. data/spec/lib/build_eval/{status_spec.rb → result/status_spec.rb} +36 -15
  23. data/spec/lib/build_eval/server/decorator_spec.rb +97 -0
  24. data/spec/lib/build_eval/server/team_city_spec.rb +119 -0
  25. data/spec/lib/build_eval/server/travis_spec.rb +92 -0
  26. data/spec/lib/build_eval_spec.rb +8 -8
  27. metadata +35 -20
  28. data/lib/build_eval/build_result.rb +0 -39
  29. data/lib/build_eval/build_results.rb +0 -23
  30. data/lib/build_eval/ci_server/team_city.rb +0 -33
  31. data/lib/build_eval/monitor.rb +0 -15
  32. data/lib/build_eval/status.rb +0 -51
  33. data/spec/lib/build_eval/build_results_spec.rb +0 -82
  34. data/spec/lib/build_eval/ci_server/decorator_spec.rb +0 -97
  35. data/spec/lib/build_eval/ci_server/team_city_spec.rb +0 -111
  36. data/spec/lib/build_eval/monitor_spec.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1daad2c7e52f801829e07f22003bf4536cf91a51
4
- data.tar.gz: 3ab822f2da5e6cad9f5a22d5f0a9b5368d162b98
3
+ metadata.gz: fa95400e5a67ae6b5f26bd9ba13beffcb74a13c2
4
+ data.tar.gz: 0d474510b5011073f58d9d8b4fd77e6fab554ee2
5
5
  SHA512:
6
- metadata.gz: a35303425c8ac6b5cec64f15ae1daec79c08cafa96d2193a8e75ea7f701fc05144dc898a81f232df11d8888ca440f8f55257da4d2c1d880263925cda502ff1c8
7
- data.tar.gz: 12d7ceb5d11e619699da7cc19965e8accecd442bddae5e4b81692b8434bcbebcfe6e038972cd16075bf686d434b41862e2fd7f145bbedc65f03475b975902b05
6
+ metadata.gz: 860cd65effbb7b85d0e763f4bce1fb8867701f4c0b32d25d722b8a119499b0cf652605181c44a79e73e4aae57af6427e7354d82ed11a0902863a3d7839211c2c
7
+ data.tar.gz: 6e5f3bc2867e832c8a80ddfd924ff4000ec056f95ef148594ef7407fbb010102d7f5e407504dad5a5a6d514ef9e209ed0ad90d83d511bb87eba19ae476848744
@@ -0,0 +1,16 @@
1
+ module BuildEval
2
+
3
+ class Http
4
+
5
+ def self.get(uri_string, basic_auth=nil)
6
+ uri = URI.parse(uri_string)
7
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
8
+ request = Net::HTTP::Get.new(uri.request_uri)
9
+ request.basic_auth(basic_auth[:username], basic_auth[:password]) if basic_auth
10
+ http.request(request)
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,13 @@
1
+ module BuildEval
2
+ module Monitor
3
+
4
+ class Base
5
+
6
+ def +(monitor)
7
+ BuildEval::Monitor::Composite.new(self, monitor)
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ module BuildEval
2
+ module Monitor
3
+
4
+ class Composite < BuildEval::Monitor::Base
5
+
6
+ def initialize(*monitors)
7
+ @monitors = monitors
8
+ end
9
+
10
+ def evaluate
11
+ BuildEval::Result::CompositeResult.new(@monitors.map(&:evaluate))
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module BuildEval
2
+ module Monitor
3
+
4
+ class Server < BuildEval::Monitor::Base
5
+
6
+ def initialize(args)
7
+ @server = args[:server]
8
+ @build_names = args[:build_names]
9
+ end
10
+
11
+ def evaluate
12
+ build_results = @build_names.map { |build_name| @server.build_result(build_name) }
13
+ BuildEval::Result::ServerResult.new(@server, build_results)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module BuildEval
2
+ module Result
3
+
4
+ class BuildResult
5
+
6
+ class << self
7
+
8
+ def create(args)
9
+ self.new(build_name: args[:build_name], status: BuildEval::Result::Status.find(args[:status_name]))
10
+ end
11
+
12
+ def unknown(build_name)
13
+ self.new(build_name: build_name, status: BuildEval::Result::Status::UNKNOWN)
14
+ end
15
+
16
+ end
17
+
18
+ attr_reader :build_name
19
+ attr_reader :status
20
+
21
+ private
22
+
23
+ def initialize(args)
24
+ @build_name = args[:build_name]
25
+ @status = args[:status]
26
+ end
27
+
28
+ public
29
+
30
+ def unsuccessful?
31
+ @status.unsuccessful?
32
+ end
33
+
34
+ def to_s
35
+ "#{@build_name}: #{@status}"
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,25 @@
1
+ module BuildEval
2
+ module Result
3
+
4
+ class CompositeResult
5
+
6
+ def initialize(results)
7
+ @results = results
8
+ end
9
+
10
+ def status
11
+ BuildEval::Result::Status.effective_status(@results.map(&:status))
12
+ end
13
+
14
+ def unsuccessful
15
+ @results.map(&:unsuccessful).flatten
16
+ end
17
+
18
+ def to_s
19
+ @results.map(&:to_s).join("\n")
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module BuildEval
2
+ module Result
3
+
4
+ class ServerResult
5
+
6
+ def initialize(server, build_results)
7
+ @server = server
8
+ @build_results = build_results
9
+ end
10
+
11
+ def status
12
+ BuildEval::Result::Status.effective_status(@build_results.map(&:status))
13
+ end
14
+
15
+ def unsuccessful
16
+ @build_results.find_all(&:unsuccessful?)
17
+ end
18
+
19
+ def to_s
20
+ "#{@server}: #{@build_results.map(&:to_s).join(", ")}"
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ module BuildEval
2
+ module Result
3
+
4
+ class Status
5
+
6
+ private
7
+
8
+ def initialize(args)
9
+ @severity = args[:severity]
10
+ @symbol = args[:symbol]
11
+ @description = args[:description]
12
+ end
13
+
14
+ public
15
+
16
+ SUCCESS = self.new(severity: 0, symbol: :success!, description: "succeeded")
17
+ UNKNOWN = self.new(severity: 1, symbol: :warning!, description: "unknown")
18
+ FAILURE = self.new(severity: 2, symbol: :failed!, description: "failed")
19
+ ERROR = self.new(severity: 3, symbol: :failed!, description: "errored")
20
+
21
+ class << self
22
+
23
+ def find(name)
24
+ begin
25
+ self.const_get(name.upcase)
26
+ rescue NameError
27
+ raise "Build status '#{name}' is invalid"
28
+ end
29
+ end
30
+
31
+ def effective_status(statuses)
32
+ statuses.sort_by { |status| status.severity }.last
33
+ end
34
+
35
+ end
36
+
37
+ attr_reader :severity
38
+
39
+ def unsuccessful?
40
+ self != SUCCESS
41
+ end
42
+
43
+ def to_sym
44
+ @symbol
45
+ end
46
+
47
+ def to_s
48
+ @description
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -1,5 +1,5 @@
1
1
  module BuildEval
2
- module CIServer
2
+ module Server
3
3
 
4
4
  class Decorator
5
5
 
@@ -11,12 +11,12 @@ module BuildEval
11
11
  begin
12
12
  @delegate.build_result(name)
13
13
  rescue Exception
14
- BuildEval::BuildResult.unknown(name)
14
+ BuildEval::Result::BuildResult.unknown(name)
15
15
  end
16
16
  end
17
17
 
18
18
  def monitor(*build_names)
19
- BuildEval::Monitor.new(server: @delegate, build_names: build_names.flatten)
19
+ BuildEval::Monitor::Server.new(server: @delegate, build_names: build_names.flatten)
20
20
  end
21
21
 
22
22
  end
@@ -0,0 +1,28 @@
1
+ module BuildEval
2
+ module Server
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 = BuildEval::Http.get(
14
+ "#{@base_uri}/httpAuth/app/rest/buildTypes/id:#{name}/builds", username: @username, password: @password
15
+ )
16
+ build_element = Nokogiri::XML(response.body).xpath("//build").first
17
+ raise "Unexpected build response: #{response.message}" unless build_element
18
+ BuildEval::Result::BuildResult.create(build_name: name, status_name: build_element.attribute("status").value)
19
+ end
20
+
21
+ def to_s
22
+ "TeamCity server #{@base_uri}"
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ module BuildEval
2
+ module Server
3
+
4
+ class Travis
5
+
6
+ def initialize(args)
7
+ @username = args[:username]
8
+ end
9
+
10
+ def build_result(name)
11
+ response = BuildEval::Http.get("https://api.travis-ci.org/repositories/#{@username}/#{name}/cc.xml")
12
+ build_element = Nokogiri::XML(response.body).xpath("//Project").first
13
+ raise "Unexpected build response: #{response.message}" unless build_element
14
+ BuildEval::Result::BuildResult.create(
15
+ build_name: name, status_name: build_element.attribute("lastBuildStatus").value
16
+ )
17
+ end
18
+
19
+ def to_s
20
+ "Travis CI #{@username}"
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module BuildEval
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
data/lib/build_eval.rb CHANGED
@@ -1,11 +1,16 @@
1
1
  require 'nokogiri'
2
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'
3
+ require_relative 'build_eval/http'
4
+ require_relative 'build_eval/result/status'
5
+ require_relative 'build_eval/result/build_result'
6
+ require_relative 'build_eval/result/server_result'
7
+ require_relative 'build_eval/result/composite_result'
8
+ require_relative 'build_eval/server/decorator'
9
+ require_relative 'build_eval/server/team_city'
10
+ require_relative 'build_eval/server/travis'
11
+ require_relative 'build_eval/monitor/base'
12
+ require_relative 'build_eval/monitor/server'
13
+ require_relative 'build_eval/monitor/composite'
9
14
 
10
15
  module BuildEval
11
16
 
@@ -14,14 +19,14 @@ module BuildEval
14
19
  def server(args)
15
20
  type_args = args.clone
16
21
  server_type = type_args.delete(:type)
17
- BuildEval::CIServer::Decorator.new(server_class_for(server_type).new(type_args))
22
+ BuildEval::Server::Decorator.new(server_class_for(server_type).new(type_args))
18
23
  end
19
24
 
20
25
  private
21
26
 
22
27
  def server_class_for(type)
23
28
  begin
24
- BuildEval::CIServer.const_get(type.to_s)
29
+ BuildEval::Server.const_get(type.to_s)
25
30
  rescue NameError
26
31
  raise "Server type '#{type}' is invalid"
27
32
  end
@@ -0,0 +1,82 @@
1
+ describe BuildEval::Http do
2
+
3
+ shared_examples_for "a http method returning a response" do
4
+
5
+ let(:response_status) { [ "200", "OK" ] }
6
+ let(:response_body) { "Some Response Body" }
7
+
8
+ it "returns a response containing the response body" do
9
+ expect(subject.body).to eql(response_body)
10
+ end
11
+
12
+ it "returns a response containing the response status" do
13
+ expect(subject.code).to eql("200")
14
+ end
15
+
16
+ it "returns a response containing the response message" do
17
+ expect(subject.message).to eql("OK")
18
+ end
19
+
20
+ end
21
+
22
+ describe "::get" do
23
+
24
+ let(:scheme) { "http" }
25
+ let(:host) { "a.host" }
26
+ let(:path) { "some/path" }
27
+ let(:uri_string) { "#{scheme}://#{host}/#{path}" }
28
+
29
+ subject { described_class.get(uri_string) }
30
+
31
+ context "when the uri is valid" do
32
+
33
+ let(:expected_request_uri) { uri_string }
34
+
35
+ before(:example) do
36
+ FakeWeb.register_uri(:get, expected_request_uri, status: response_status, body: response_body)
37
+ end
38
+
39
+ context "and the uri contains a http scheme" do
40
+
41
+ let(:scheme) { "http" }
42
+
43
+ it_behaves_like "a http method returning a response"
44
+
45
+ end
46
+
47
+ context "and the uri contains a https scheme" do
48
+
49
+ let(:scheme) { "https" }
50
+
51
+ it_behaves_like "a http method returning a response"
52
+
53
+ end
54
+
55
+ context "and basic authentication is provided" do
56
+
57
+ let(:username) { "some_username" }
58
+ let(:password) { "some_password" }
59
+
60
+ let(:expected_request_uri) { "#{scheme}://#{username}:#{password}@#{host}/#{path}" }
61
+
62
+ subject { described_class.get(uri_string, basic_authentication: { username: username, password: password }) }
63
+
64
+ it_behaves_like "a http method returning a response"
65
+
66
+ end
67
+
68
+ end
69
+
70
+ context "when the uri is invalid" do
71
+
72
+ before(:example) { FakeWeb.clean_registry }
73
+
74
+ it "raises an error" do
75
+ expect { subject }.to raise_error(SocketError)
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,29 @@
1
+ describe BuildEval::Monitor::Base do
2
+
3
+ class BuildEval::Monitor::TestableBase < BuildEval::Monitor::Base
4
+ end
5
+
6
+ let(:base_monitor) { BuildEval::Monitor::TestableBase.new }
7
+
8
+ describe "#+" do
9
+
10
+ let(:provided_monitor) { instance_double(BuildEval::Monitor::Base) }
11
+
12
+ subject { base_monitor + provided_monitor }
13
+
14
+ it "creates a composite monitor combining the monitor with the provided monitor" do
15
+ expect(BuildEval::Monitor::Composite).to receive(:new).with(base_monitor, provided_monitor)
16
+
17
+ subject
18
+ end
19
+
20
+ it "returns the composite monitor" do
21
+ composite_monitor = instance_double(BuildEval::Monitor::Composite)
22
+ allow(BuildEval::Monitor::Composite).to receive(:new).and_return(composite_monitor)
23
+
24
+ expect(subject).to eql(composite_monitor)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,38 @@
1
+ describe BuildEval::Monitor::Composite do
2
+
3
+ let(:monitors) { (1..2).map { double(BuildEval::Monitor::Base) } }
4
+
5
+ let(:composite) { described_class.new(*monitors) }
6
+
7
+ describe "#evaluate" do
8
+
9
+ let(:results) { monitors.map { instance_double("BuildEval::Result") } }
10
+
11
+ subject { composite.evaluate }
12
+
13
+ before(:example) do
14
+ monitors.zip(results) { |monitor, result| allow(monitor).to receive(:evaluate).and_return(result) }
15
+ end
16
+
17
+ it "delegates to the monitors" do
18
+ monitors.each { |monitor| expect(monitor).to receive(:evaluate) }
19
+
20
+ subject
21
+ end
22
+
23
+ it "creates a composite result containing the result of each monitor" do
24
+ expect(BuildEval::Result::CompositeResult).to receive(:new).with(results)
25
+
26
+ subject
27
+ end
28
+
29
+ it "returns the composite result" do
30
+ composite_result = instance_double(BuildEval::Result::CompositeResult)
31
+ allow(BuildEval::Result::CompositeResult).to receive(:new).and_return(composite_result)
32
+
33
+ expect(subject).to eql(composite_result)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,43 @@
1
+ describe BuildEval::Monitor::Server do
2
+
3
+ let(:server) { double("BuildEval::Server") }
4
+ let(:build_names) { (1..3).map { |i| "build##{i}" } }
5
+
6
+ let(:server_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::Result::BuildResult) } }
11
+
12
+ subject { server_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 server result for the server" do
23
+ expect(BuildEval::Result::ServerResult).to receive(:new).with(server, anything)
24
+
25
+ subject
26
+ end
27
+
28
+ it "composes a server result containing the results" do
29
+ expect(BuildEval::Result::ServerResult).to receive(:new).with(anything, results)
30
+
31
+ subject
32
+ end
33
+
34
+ it "returns the server result" do
35
+ server_result = instance_double(BuildEval::Result::ServerResult)
36
+ expect(BuildEval::Result::ServerResult).to receive(:new).and_return(server_result)
37
+
38
+ expect(subject).to eql(server_result)
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -1,4 +1,4 @@
1
- describe BuildEval::BuildResult do
1
+ describe BuildEval::Result::BuildResult do
2
2
 
3
3
  describe "::create" do
4
4
 
@@ -13,14 +13,14 @@ describe BuildEval::BuildResult do
13
13
  end
14
14
 
15
15
  it "determines the status with the provided status name" do
16
- expect(BuildEval::Status).to receive(:find).with(status_name)
16
+ expect(BuildEval::Result::Status).to receive(:find).with(status_name)
17
17
 
18
18
  subject
19
19
  end
20
20
 
21
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)
22
+ status = BuildEval::Result::Status::UNKNOWN
23
+ allow(BuildEval::Result::Status).to receive(:find).and_return(status)
24
24
 
25
25
  expect(subject.status).to eql(status)
26
26
  end
@@ -38,19 +38,19 @@ describe BuildEval::BuildResult do
38
38
  end
39
39
 
40
40
  it "returns a result with an unknown status" do
41
- expect(subject.status).to eql(BuildEval::Status::UNKNOWN)
41
+ expect(subject.status).to eql(BuildEval::Result::Status::UNKNOWN)
42
42
  end
43
43
 
44
44
  end
45
45
 
46
46
  describe "#unsuccessful?" do
47
47
 
48
- let(:status) { instance_double(BuildEval::Status) }
48
+ let(:status) { instance_double(BuildEval::Result::Status) }
49
49
  let(:build_result) { described_class.create(build_name: "some build", status_name: "some status") }
50
50
 
51
51
  subject { build_result.unsuccessful? }
52
52
 
53
- before(:example) { allow(BuildEval::Status).to receive(:find).and_return(status) }
53
+ before(:example) { allow(BuildEval::Result::Status).to receive(:find).and_return(status) }
54
54
 
55
55
  it "delegates to the underlying status" do
56
56
  allow(status).to receive(:unsuccessful?).and_return(true)
@@ -64,13 +64,13 @@ describe BuildEval::BuildResult do
64
64
 
65
65
  let(:build_name) { "Some build name" }
66
66
  let(:status_string_representation) { "SUCCESS" }
67
- let(:status) { instance_double(BuildEval::Status, to_s: status_string_representation) }
67
+ let(:status) { instance_double(BuildEval::Result::Status, to_s: status_string_representation) }
68
68
 
69
69
  let(:build_result) { described_class.create(build_name: build_name, status_name: "some status") }
70
70
 
71
71
  subject { build_result.to_s }
72
72
 
73
- before(:example) { allow(BuildEval::Status).to receive(:find).and_return(status) }
73
+ before(:example) { allow(BuildEval::Result::Status).to receive(:find).and_return(status) }
74
74
 
75
75
  it "contains the name of the build" do
76
76
  expect(subject).to include(build_name)