build_eval 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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)