require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))),'spec_helper')

describe Saulabs::Reportable::Grouping do

  describe '#new' do

    it 'should raise an error if an unsupported grouping is specified' do
      lambda { Saulabs::Reportable::Grouping.new(:unsupported) }.should raise_error(ArgumentError)
    end

  end

  describe '#to_sql' do

    describe 'for MySQL' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('MySQL')
      end

      it 'should use DATE_FORMAT with format string "%Y/%m/%d/%H" for grouping :hour' do
        Saulabs::Reportable::Grouping.new(:hour).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m/%d/%H')"
      end

      it 'should use DATE_FORMAT with format string "%Y/%m/%d" for grouping :day' do
        Saulabs::Reportable::Grouping.new(:day).send(:to_sql, 'created_at').should == "DATE(created_at)"
      end

      it 'should use YEARWEEK with mode 3 for grouping :week' do
        Saulabs::Reportable::Grouping.new(:week).send(:to_sql, 'created_at').should == "YEARWEEK(created_at, 3)"
      end

      it 'should use DATE_FORMAT with format string "%Y/%m" for grouping :month' do
        Saulabs::Reportable::Grouping.new(:month).send(:to_sql, 'created_at').should == "DATE_FORMAT(created_at, '%Y/%m')"
      end

    end

    describe 'for PostgreSQL' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('PostgreSQL')
      end

      for grouping in [:hour, :day, :week, :month] do

        it "should use date_trunc with truncation identifier \"#{grouping.to_s}\" for grouping :#{grouping.to_s}" do
          Saulabs::Reportable::Grouping.new(grouping).send(:to_sql, 'created_at').should == "date_trunc('#{grouping.to_s}', created_at)"
        end

      end

    end

    describe 'for SQLite3' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('SQLite')
      end

      it 'should use strftime with format string "%Y/%m/%d/%H" for grouping :hour' do
        Saulabs::Reportable::Grouping.new(:hour).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d/%H', created_at)"
      end

      it 'should use strftime with format string "%Y/%m/%d" for grouping :day' do
        Saulabs::Reportable::Grouping.new(:day).send(:to_sql, 'created_at').should == "strftime('%Y/%m/%d', created_at)"
      end

      it 'should use date with mode "weekday 0" for grouping :week' do
        Saulabs::Reportable::Grouping.new(:week).send(:to_sql, 'created_at').should == "date(created_at, 'weekday 0')"
      end

      it 'should use strftime with format string "%Y/%m" for grouping :month' do
        Saulabs::Reportable::Grouping.new(:month).send(:to_sql, 'created_at').should == "strftime('%Y/%m', created_at)"
      end

    end

    describe 'for MS SQL Server' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('sqlserver')
      end

      it 'should output a format of "YYYY-MM-DD HH:00:00.0" for grouping :hour' do # string "%Y-%m-%d %h:00:00.0" 
        Saulabs::Reportable::Grouping.new(:hour).send(:to_sql, 'created_at').should == "DATEADD(hh,DATEDIFF(hh,DATEADD(dd,DATEDIFF(dd,'1 Jan 1900',created_at), '1 Jan 1900'),created_at), DATEADD(dd,DATEDIFF(dd,'1 Jan 1900',created_at), '1 Jan 1900'))"
      end

      it 'should output a format of "YYYY-MM-DD" for grouping :day' do
        Saulabs::Reportable::Grouping.new(:day).send(:to_sql, 'created_at').should == "DATEADD(dd,DATEDIFF(dd,'1 Jan 1900',created_at), '1 Jan 1900')"
      end

      it 'should output a format of "YYYY-WW" for grouping :week' do
        Saulabs::Reportable::Grouping.new(:week).send(:to_sql, 'created_at').should == "LEFT(CONVERT(varchar,created_at,120), 4) + '-' + CAST(DATEPART(isowk,created_at) AS VARCHAR)"
      end

      it 'should output a format of "YYYY-MM-01" for grouping :month' do
        Saulabs::Reportable::Grouping.new(:month).send(:to_sql, 'created_at').should == "DATEADD(mm,DATEDIFF(mm,'1 Jan 1900',created_at), '1 Jan 1900')"
      end

    end

  end

  describe '#date_parts_from_db_string' do

    describe 'for SQLite3' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('SQLite')
      end

      for grouping in [[:hour, '2008/12/31/12'], [:day, '2008/12/31'], [:month, '2008/12']] do

        it "should split the string with '/' for grouping :#{grouping[0].to_s}" do
          Saulabs::Reportable::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
        end

      end

      it 'should split the string with "-" and return the calendar year and week for grouping :week' do
        db_string = '2008-2-1'
        expected = [2008, 5]

        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
      end

    end

    describe 'for PostgreSQL' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('PostgreSQL')
      end

      it 'should split the date part of the string with "-" and read out the hour for grouping :hour' do
        Saulabs::Reportable::Grouping.new(:hour).date_parts_from_db_string('2008-12-03 06:00:00').should == [2008, 12, 03, 6]
      end

      it 'should split the date part of the string with "-" for grouping :day' do
        Saulabs::Reportable::Grouping.new(:day).date_parts_from_db_string('2008-12-03 00:00:00').should == [2008, 12, 03]
      end

      it 'should split the date part of the string with "-" and calculate the calendar week for grouping :week' do
        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 49]
      end

      it 'should split the date part of the string with "-" and return year and month for grouping :month' do
        Saulabs::Reportable::Grouping.new(:month).date_parts_from_db_string('2008-12-01 00:00:00').should == [2008, 12]
      end

    end

    describe 'for MySQL' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('MySQL')
      end

      for grouping in [[:hour, '2008/12/31/12'], [:day, '2008/12/31'], [:month, '2008/12']] do

        it "should split the string with '/' for grouping :#{grouping[0].to_s}" do
          Saulabs::Reportable::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split('/').map(&:to_i)
        end

      end

      it 'should use the first 4 numbers for the year and the last 2 numbers for the week for grouping :week' do
        db_string = '200852'
        expected = [2008, 52]

        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
      end

    end

    describe 'for MS SQL Server' do

      before do
        ActiveRecord::Base.connection.stub!(:adapter_name).and_return('sqlserver')
      end

      for grouping in [[:hour, '2008-12-31 12'], [:day, '2008-12-31'], [:month, '2008-12']] do

        it "should split the string with '-' and ' ' for grouping :#{grouping[0].to_s}" do
          Saulabs::Reportable::Grouping.new(grouping[0]).date_parts_from_db_string(grouping[1]).should == grouping[1].split(/[- ]/).map(&:to_i)
        end

      end

      it 'should use the first 4 numbers for the year and the last 2 numbers for the week for grouping :week' do
        db_string = '2008-52'
        expected = [2008, 52]

        Saulabs::Reportable::Grouping.new(:week).date_parts_from_db_string(db_string).should == expected
      end

    end

  end

end
