public
Description: Inter-application integration testing
Homepage:
Clone URL: git://github.com/gma/integral.git
integral / lib / integral / database.rb
100644 135 lines (112 sloc) 3.826 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require "activerecord"
require "logger"
 
module Integral
  class Error < RuntimeError; end
  
  module Database
    def self.disable_logging
      log_file = File.join(INTEGRAL_ROOT, "log", "#{INTEGRAL_ENV}.log")
      ActiveRecord::Base.logger = Logger.new(log_file)
    end
    
    def self.connect
      disable_logging
      args = Integral::Configuration.database_configuration[INTEGRAL_ENV]
      ActiveRecord::Base.establish_connection(args)
    end
  end
end
 
# Models are defined here – doesn't seem worth the effort to create a
# separate models directory. This isn't Rails, after all!
 
class Application < ActiveRecord::Base
  has_many :application_versions, :dependent => :destroy
 
  validates_uniqueness_of :name
  validates_presence_of :name, :path
 
  def self.find_active
    find(:all, :conditions => ["active = ?", true])
  end
  
  def active?
    self.active
  end
 
  def activate!
    update_attribute(:active, true)
  end
 
  def deactivate!
    update_attribute(:active, false)
  end
  
  def current_version(type)
    host = Integral::Configuration.server(type)
    command = Integral::Configuration.version_command.
        gsub("$hostname", host).
        gsub("$path", self.path)
    print "Checking version of #{self.name} on #{host} ... " if ENV["VERBOSE"]
    $stdout.flush
    fh = IO.popen(command)
    version = fh.gets.rstrip
    puts "ok" if ENV["VERBOSE"]
    version
  end
end
 
class ApplicationVersion < ActiveRecord::Base
  belongs_to :application
  has_many :application_version_test_runs
  has_many :test_runs, :through => :application_version_test_runs
  
  def self.find_current
    find(:all, :group => :application_id, :order => "updated_at")
  end
  
  def self.check_current_versions(type)
    versions = []
    Application.find(:all, :conditions => ["active = ?", true]).each do |app|
      versions << find_or_create_by_application_id_and_version(
          app.id, app.current_version(type))
    end
    versions
  end
end
 
class ApplicationVersionTestRun < ActiveRecord::Base
  belongs_to :application_version
  belongs_to :test_run
end
 
class ApplicationNotSpecified < Integral::Error; end
class TestRunNotFound < Integral::Error; end
 
class TestRun < ActiveRecord::Base
  has_many :application_version_test_runs
  has_many :application_versions, :through => :application_version_test_runs
  
  def self.test_command
    "ruby #{File.expand_path(File.join(INTEGRAL_ROOT, "integrate.rb"))}"
  end
  
  def self.start
    run = new
    run.application_versions << ApplicationVersion.check_current_versions(:test)
    IO.popen(test_command) { |fh| fh.each { |line| puts line } }
    run.passed = ($?.exitstatus == 0)
    run.save!
  end
  
  def self.passed?(versions)
    last_run = find_test_runs(versions).sort do |x, y|
      x.created_at <=> y.created_at
    end.last
    raise TestRunNotFound if last_run.nil?
    apps_in_run = last_run.application_versions.map { |av| av.application.name }
    raise ApplicationNotSpecified if apps_in_run.sort != versions.keys.sort
    last_run.passed
  end
  
  private
    def self.find_test_runs(versions)
      test_runs = []
      versions.each do |name, version|
        app_version = ApplicationVersion.find(
            :first,
            :joins => :application,
            :include => :test_runs,
            :conditions => ["name = ? and version = ?", name, version])
        raise TestRunNotFound if app_version.nil?
        test_runs << app_version.test_runs
      end
      test_runs.inject { |x, y| x & y } # only the runs that tested every app
    end
    
    def self.convert_to_application_ids(versions)
      apps = Application.find(:all,
                              :conditions => ["name in (?)", versions.keys])
      apps.map { |app| versions[app.id] = versions.delete(app.name) }
      versions
    end
end