package tests::functional::LrCronTest;

use strict;

use base qw/Lire::Test::FunctionalTestCase 
            tests::functional::TestSchemaFixture
            tests::ChartTypesFixture 
            tests::OutputFormatsFixture /;

use Lire::Config;
use Lire::DlfStore;
use Lire::Utils qw/create_file/;
use File::Copy qw/copy/;
use POSIX qw/strftime/;
use File::Basename qw/basename/;
{
    # See LrMailTest.pm for rationale
    use utf8;
    use MIME::Parser;
}
use Time::Local;

#our @TESTS = qw//;

sub new {
    my $self = shift->SUPER::new(@_);

    $self->tests::functional::TestSchemaFixture::init(@_);

    $self->{'log_file'} = $self->tests_datadir() . "/test.dlf";


    return $self;
}

sub set_up {
    my $self = $_[0];
    $self->SUPER::set_up();

    $self->set_up_test_schemas();
    $self->set_up_test_specs();
    $self->set_up_TestDlfConverter();
    $self->set_up_test_analysers();
    $self->set_up_test_another_analyser();

    Lire::Config->init();
    $self->{'store_path'} = $self->homedir() . "/store";

    return;
}

sub tear_down {
    my $self = $_[0];

    $self->SUPER::tear_down();
    $self->tests::functional::TestSchemaFixture::tear_down();

    return;
}

sub test_usage_period {
    my $self = $_[0];

    my $result = $self->lire_run( "lr_cron" );
    $self->annotate( $result->stderr() );
    $self->assert_not_equals( 0, $result->status() );
    $self->assert( !$result->stdout(), "stdout should be empty" );
    $self->assert_str_equals( "Missing 'period' argument.\nUsage: lr_cron <period> <store>\n",
                              $result->stderr() );

}

sub test_usage_store {
    my $self = $_[0];

    my $result = $self->lire_run( "lr_cron daily" );
    $self->annotate( $result->stderr() );
    $self->assert_not_equals( 0, $result->status() );
    $self->assert( !$result->stdout(), "stdout should be empty" );
    $self->assert_str_equals( "Missing 'store' argument.\nUsage: lr_cron <period> <store>\n",
                              $result->stderr() );
}

sub test_usage_bad_period {
    my $self = $_[0];

    my $result = $self->lire_run( "lr_cron bad" );
    $self->annotate( $result->stderr() );
    $self->assert_not_equals( 0, $result->status() );
    $self->assert( !$result->stdout(), "stdout should be empty" );
    $self->assert_str_equals( "Invalid period 'bad'. Should be one of hourly, daily, weekly, monthly or yearly.\nUsage: lr_cron <period> <store>\n",
                              $result->stderr() );
}

sub set_up_import_jobs {
    my $self = $_[0];

    my $store = Lire::DlfStore->open( $self->{'store_path'}, 1 );
    my $cfg = $store->config();
    my $jobs = $cfg->get( 'import_jobs' );
    my $hourly_job = $jobs->spec()->get( 'import_job' )->instance();
    $hourly_job->get( 'name' )->set( 'hourly' );
    $hourly_job->get( 'period' )->set( 'hourly' );
    $hourly_job->get( 'service' )->set_plugin( 'test_newapi' );
    $hourly_job->get( 'log_file' )->set( $self->{'log_file'} );
    $hourly_job->get( 'log_encoding' )->set( '' );
    $hourly_job->get( 'filter' )->set( '' );

    my $daily_job = $jobs->spec()->get( 'import_job' )->instance();
    $daily_job->get( 'name' )->set( 'daily' );
    $daily_job->get( 'period' )->set( 'daily' );
    $daily_job->get( 'service' )->set_plugin( 'test_newapi' );
    $daily_job->get( 'log_file' )->set( $self->{'log_file'} );
    $daily_job->get( 'log_encoding' )->set( '' );
    $daily_job->get( 'filter' )->set( 'grep " flacoste "' );

    $jobs->append( $hourly_job );
    $jobs->append( $daily_job );

    $store->get_stream_config( 'test-derived' )->get( 'test-extended' )->set_plugin( 'none' );

    $store->close();
}

sub test_import_daily_job {
    my $self = $_[0];

    $self->set_up_import_jobs();

    my $jan26_2003 = timelocal( 0, 0, 1, 26, 0, 2003 );

    my $result = $self->lire_run( "lr_cron daily $self->{'store_path'} $jan26_2003" );
    $self->annotate( $result->stderr() );
    $self->assert_num_equals( 0, $result->status() );
    $self->assert( !$result->stdout(), "stdout should be empty" );
    $self->assert( !$result->stderr(), "stderr should be empty" );

    my $store = Lire::DlfStore->open( $self->{'store_path'} );
    my $stream = $store->open_dlf_stream( 'lire_import_stats', 'r' );
    my $stats = $stream->read_dlf();

    $self->assert_str_equals( 'daily', $stats->{'job_name'} );
    $self->assert_num_equals( 0, $stats->{'line_count'} );
    $self->assert_num_equals( 2, $stats->{'dlf_count'} );
    $self->assert_num_equals( 0, $stats->{'ignored_count'} );
    $self->assert_num_equals( 0, $stats->{'saved_count'} );
    $self->assert_num_equals( 0, $stats->{'error_count'} );

    $stats = $stream->read_dlf();
    $self->assert_str_equals( 'derived', $stats->{'job_name'} );
    $self->assert_num_equals( 2, $stats->{'dlf_count'} );
    $self->assert_num_equals( 0, $stats->{'error_count'} );

    $stats = $stream->read_dlf();
    $self->assert_str_equals( 'extended', $stats->{'job_name'} );
    $self->assert_num_equals( 2, $stats->{'dlf_count'} );
    $self->assert_num_equals( 0, $stats->{'error_count'} );

    $self->assert_null( $stream->read_dlf() );
    $stream->close();

    $stream = $store->open_dlf_stream( 'test', 'r' );
    $self->assert_num_equals( 2, $stream->nrecords() );
    $self->assert_num_equals( 1043514300, $stream->start_time() );
    $self->assert_num_equals( 1043515075, $stream->end_time() );
    $stream->close();

    $stream = $store->open_dlf_stream( 'test-extended', 'r' );
    $self->assert_num_equals( 2, $stream->nrecords() );
    $stream->close();

    $stream = $store->open_dlf_stream( 'test-derived', 'r' );
    $self->assert_num_equals( 2, $stream->nrecords() );
    $stream->close();

    $store->close();
}

sub set_up_store_config {
    my $self = $_[0];

    my $cfg_file = $self->create_test_cfg_file( 'lr_week_numbering' );
    $cfg_file->get( 'lr_week_numbering' )->set( 'ISO' );
    $cfg_file->save();

    my $store = Lire::DlfStore->open( $self->rundir() . "/store", 1 );
    $self->{'store_path'} = $store->path();
    $store->close();
    copy( $self->tests_datadir() . "/jobs-config.xml",
          "$self->{'store_path'}/config.xml" );
}

# We will run a daily job from 21 to 27
# that is the jobs for the week Jan 20th to 26th 2003
#
# The configuration should be identifcal to the one
# tested in Lire::Test::test_configure_jobs(). These
# two tests making a central Lire use-case
sub test_lr_cron_one_week {
    my $self = $_[0];

    $self->set_up_chart_types();
    $self->set_up_output_formats();
    my $jan21_2003 = timelocal( 0, 0, 1, 21, 0, 2003 );
    $self->set_up_store_config();
    for ( my $i=0; $i < 7; $i++ ) {
        my $time = $jan21_2003 + $i*86400;
        if ( $i == 5 ) {
            # 25th
            copy( $self->tests_datadir() . "/test.dlf",
                  $self->rundir() . "/test.dlf" );
        } else {
            create_file( $self->rundir() . "/test.dlf", '' );
        }
        $self->annotate( strftime( "==> lr_cron daily %Y-%m-%d %H:%M\n\n",
                                   localtime $time ) );
        my $result = $self->lire_run( "lr_cron daily $self->{'store_path'} $time" );
        $self->annotate( $result->stderr() );
        $self->assert_num_equals( 0, $result->status() );
        $self->assert( ! $result->stdout(), "stdout should be empty" );
        $self->assert_does_not_match( qr/ (crit|err|warning) /,
                                      $result->stderr(),
                                      "There were warnings or error messages." );
        if ( $i == 5 ) {
            # 25th we have DLF data
            $self->check_daily_report( $time, $result->sent_mail() );
        } else {
            $self->check_nodata_report( $time, $result->sent_mail() );
        }
    }

    my $time = $jan21_2003 + 6 * 86400;
    $self->annotate( strftime( "==> lr_cron weekly %Y-%m-%d %H:%M\n\n",
                               localtime( $time ) ) );
    my $result = $self->lire_run( "lr_cron weekly $self->{'store_path'} $time" );
    $self->annotate( $result->stderr() );
    $self->assert_num_equals( 0, $result->status() );
    $self->assert( ! $result->stdout(), "stdout should be empty" );
    $self->assert_does_not_match( qr/ (crit|err|warning) /,
                                  $result->stderr(),
                                  "There were warnings or error messages." );
    $self->check_weekly_report( $time, $result->sent_mail() );


    # We run lr_cron daily another time to check for streams cleaning
    # since on the 2003/01/27 01:00 - 1d (cron offset) - 1d (keep_days) =
    # delete records < 2003/01/25 01:00
    $time = $jan21_2003 + 7 * 86400;
    $self->annotate( strftime( "==> lr_cron daily %Y-%m-%d %H:%M\n\n",
                               localtime( $time ) ) );
    $result = $self->lire_run( "lr_cron daily $self->{'store_path'} $time" );
    $self->annotate( $result->stderr() );
    $self->assert_num_equals( 0, $result->status() );
    $self->assert( ! $result->stdout(), "stdout should be empty" );
    $self->assert_does_not_match( qr/ (crit|err|warning) /,
                                  $result->stderr(),
                                  "There were warnings or error messages." );
    $self->check_streams_cleaning();
}

sub check_streams_cleaning {
    my $self = $_[0];

    my $store = Lire::DlfStore->open( $self->{'store_path'} );

    foreach my $name ( qw/ test test-derived test-extended / ) {
        my $stream = $store->open_dlf_stream( $name, 'r' );
        $self->assert_num_equals( 0, $stream->nrecords(),
                                  "$name wasn't cleaned" );
        $stream->close();
    }
    $store->close();
}

sub check_nodata_report {
    my ( $self, $time, $mails ) = @_;

    # Since reports are generated for the previous period
    my $day = (localtime( $time ))[3] - 1;
    my $xml_report = "$self->{'store_path'}/daily_reports/Test_Report/200301/$day.xml";
    $self->assert( ! -s $xml_report, "File '$xml_report' exists" );
    $self->assert_num_equals( 0, scalar @$mails );
}

sub check_daily_report {
    my ( $self, $time, $mails ) = @_;

    # Since reports are generated for the previous period
    my $day = (localtime( $time ))[3] - 1;
    my $xml_report = "$self->{'store_path'}/daily_reports/Test_Report/200301/$day.xml";
    $self->assert( -s $xml_report, "File '$xml_report' is missing" );

    $self->check_xml_report( $self->tests_datadir() . "/TestReport_daily_jan25_2003.xml",
                             $xml_report );

    $self->assert_num_equals( 1, scalar @$mails );
    $self->assert_deep_equals( [ 'flacoste@logreport.org' ],
                               $mails->[0]{'recipients'} );

    my $parser = new MIME::Parser();
    $parser->output_under( $self->rundir() );
    my $msg = $parser->parse_data( $mails->[0]{'message'} );

    $self->assert_str_equals( "PDF Report\n", $msg->head()->get( 'Subject' ) );
    $self->assert_str_equals( 'application/pdf', $msg->mime_type() );
    $self->assert(  length( $msg->bodyhandle()->as_string() ) > 0,
                    "PDF report is empty" );

    return;
}

sub check_weekly_report {
    my ( $self, $time, $mails ) = @_;

    my $xml_report = "$self->{'store_path'}/weekly_reports/Test_Report/2003/04.xml";
    $self->assert( -s $xml_report, "File '$xml_report' is missing" );

    $self->check_xml_report( $self->tests_datadir() . "/TestReport_weekly_w04_2003.xml",
                             $xml_report );

    $self->assert( -d $self->rundir() . "/report 2003-W04",
                   "HTML report 'report 2003-W04' is missing" );
    $self->assert( -s $self->rundir() . "/report 2003-W04/index.html",
                   "File 'report 2003-W04/index.html' is missing" );

    $self->assert_num_equals( 1, scalar @$mails );
    $self->assert_deep_equals( [ 'wolfgang@logreport.org' ],
                               $mails->[0]{'recipients'} );

    my $parser = new MIME::Parser();
    $parser->output_under( $self->rundir() );
    my $msg = $parser->parse_data( $mails->[0]{'message'} );

    $self->assert_str_equals( "Text Report\n", $msg->head()->get( 'Subject' ));
    $self->assert_str_equals( 'text/plain', $msg->mime_type() );
    $self->assert_matches( qr/movies\/001145\.mpg/,
                           $msg->bodyhandle()->as_string() );
}

1;


syntax highlighted by Code2HTML, v. 0.9.1