require 'rubygems'
require 'bundler/setup'
Bundler.require :default, :development
require 'grouped_scope'
require 'minitest/autorun'
require 'factories'
require 'logger'


ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__),'debug.log'))
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
module ActiveRecord
  class SQLCounter
    cattr_accessor :ignored_sql
    self.ignored_sql = [/^PRAGMA table_info\(.*\)/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
    ignored_sql.concat [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
    def initialize
      $queries_executed = []
    end
    def call(name, start, finish, message_id, values)
      sql = values[:sql]
      unless 'CACHE' == values[:name]
        $queries_executed << sql unless self.class.ignored_sql.any? { |r| sql =~ r }
      end
    end
  end
  ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
end


module GroupedScope
  class TestCase < MiniTest::Spec
    
    include Mocha::API
    
    before { setup_environment }
    after  { mocha_teardown }
    
    def setup_environment(options={})
      options.reverse_merge! :group_column => :group_id
      setup_database(options)
      Department.create! :name => 'IT'
      Department.create! :name => 'Human Resources'
      Department.create! :name => 'Finance'
    end
    
    protected
    
    def assert_same_elements(a1, a2, msg = nil)
      [:select, :inject, :size].each do |m|
        [a1, a2].each { |a| a.must_respond_to m, "Are you sure that #{a.inspect} is an array?  It doesn't respond to #{m}." }
      end
      assert a1h = a1.inject({}) { |h,e| h[e] ||= a1.select { |i| i == e }.size; h }
      assert a2h = a2.inject({}) { |h,e| h[e] ||= a2.select { |i| i == e }.size; h }
      a1h.must_equal a2h, msg
    end
    
    def assert_sql(*patterns_to_match)
      $queries_executed = []
      yield
      $queries_executed
    ensure
      failed_patterns = []
      patterns_to_match.each do |pattern|
        failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
      end
      assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
    end

    def assert_queries(num = 1)
      $queries_executed = []
      yield
    ensure
      assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
    end

    def assert_no_queries
      assert_queries(0) { yield }
    end
    
    def setup_database(options)
      ActiveRecord::Base.class_eval do
        silence do
          connection.create_table :employees, :force => true do |t|
            t.column :name,         :string
            t.column :email,        :string
            t.column options[:group_column],  :integer
          end
          connection.create_table :reports, :force => true do |t|
            t.column :title,        :string
            t.column :body,         :string
            t.column :employee_id,  :integer
          end
          connection.create_table :departments, :force => true do |t|
            t.column :name,         :string
          end
          connection.create_table :department_memberships, :force => true do |t|
            t.column :employee_id,    :integer
            t.column :department_id,  :integer
            t.column :meta_info,      :string
          end
          connection.create_table :legacy_employees, :force => true, :id => false do |t|
            t.column :name,         :string
            t.column :email,        :string
            t.column options[:group_column],  :integer
          end
          connection.create_table :legacy_reports, :force => true do |t|
            t.column :title,        :string
            t.column :body,         :string
            t.column :email,        :string
          end
          connection.create_table :foo_bars, :force => true do |t|
            t.column :foo,          :string
            t.column :bar,          :string
          end
        end
      end
    end
    
  end
end

class Department < ActiveRecord::Base
  scope :it, where(:name => 'IT')
  scope :hr, where(:name => 'Human Resources')
  scope :finance, where(:name => 'Finance')
  has_many :department_memberships
  has_many :employees, :through => :department_memberships
end

class DepartmentMembership < ActiveRecord::Base
  belongs_to :employee
  belongs_to :department
end

class Report < ActiveRecord::Base
  scope :with_urgent_title, where(:title => 'URGENT')
  scope :with_urgent_body, where("body LIKE '%URGENT%'")
  belongs_to :employee
  def urgent_title? ; self[:title] == 'URGENT' ; end
  def urgent_body? ; self[:body] =~ /URGENT/ ; end
end

class LegacyReport < ActiveRecord::Base
  belongs_to :employee, :class_name => 'LegacyEmployee', :foreign_key => 'email'
end

class Employee < ActiveRecord::Base
  scope :email_for_actionmoniker, where("email LIKE '%@actionmoniker.com'")
  has_many :reports do ; def urgent ; find(:all,:conditions => {:title => 'URGENT'}) ; end ; end
  has_many :taxonomies, :as => :classable
  has_many :department_memberships
  has_many :departments, :through => :department_memberships
  grouped_scope :reports, :departments
end

class LegacyEmployee < ActiveRecord::Base
  self.primary_key = 'email'
  has_many :reports, :class_name => 'LegacyReport', :foreign_key => 'email'
  grouped_scope :reports
end

class FooBar < ActiveRecord::Base
  has_many :reports
  grouped_scope :reports
end



