The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use t::share; guard my $guard;
use Narada::Config qw( set_config );

require (wd().'/blib/script/narada-setup-cron');


# - main()
#   * too many params
#   * wrong params
#   * no params and config/crontab/backup not readable: throw exception
#   * --clean: make sure del_cron() called
#   * no --clean and no config/crontab/backup: make sure del_cron() called
#   * no --clean and exists config/crontab/backup: make sure set_cron called
#   * many files in config/crontab/: make sure set_cron called for them all
#   * manage var/use/cron

throws_ok { main('param-1', 'param-2') }    qr/Usage:/,
    'main: too many params';

throws_ok { main('not_existing_param') }    qr/Usage:/,
    'main: wrong param';

SKIP: {
    skip 'non-root user required', 1 if $< == 0;
    chmod 0, 'config/crontab/backup'                        or die "chmod: $!";
    throws_ok { main() }                    qr/permission/i,
        'main: not readable';
    chmod 0644, 'config/crontab/backup'                     or die "chmod: $!";
}

{ 
    my (@log, $lines);
    my $is_log = sub { is("@log", "@{$_[0]}", $_[1]); @log=(); };
    my $m = new Test::MockModule('main');
    $m->mock('del_cron', sub { push @log, 'del_cron'; $lines = 0 });
    $m->mock('set_cron', sub { push @log, 'set_cron'; $lines = shift=~tr/\n// });

    main('--clean');
    $is_log->(['del_cron'],
        'main: call del_cron() on --clean');

    rename 'config/crontab/backup', 'config/crontab.orig'   or die "rename: $!";
    main();
    rename 'config/crontab.orig', 'config/crontab/backup'   or die "rename: $!";
    $is_log->(['del_cron'],
        'main: call del_cron() on no config/crontab/backup without --clean');

    main();
    $is_log->(['set_cron'],
        'main: call set_cron() on config/crontab/backup without --clean');

    my $wait = path('config/crontab/backup')->lines;
    is $lines, $wait, 'one config';

    set_config('crontab/service', "# line1\n# line2\n");
    main();
    $is_log->(['set_cron'],
        'main: call set_cron() on config/crontab/*');
    $wait += 1 + path('config/crontab/service')->lines;
    is $lines, $wait, 'many configs';

    main('--clean');
    ok !path('var/use/cron')->exists, 'no var/use/cron';
    output_from { main() };
    ok path('var/use/cron')->is_file, 'created var/use/cron';
    main('--clean');
    ok !path('var/use/cron')->exists, 'removed var/use/cron';
}

# - get_project_dir()
#   * throw on \n inside directory name

Echo('pwd', "#!/bin/sh\ncat cwd");
chmod 0755, 'pwd'                                   or die "chmod: $!";

{
    local $ENV{PATH} = ".:$ENV{PATH}";

    Echo('cwd', "/a/b/c\n");
    is(get_project_dir(), '/a/b/c',
        'get_project_dir: ok');

    Echo('cwd', "/a/b\nb/c\n");
    throws_ok { get_project_dir() }     qr/must not contain \\n/,
        'get_project_dir: throw on \n inside directory name';
}

# - process()
#   * test multi line data, with several commands and comments

my $process = process(
    "# comment1\n"
  . "* * * * * echo ok\n"
  . "# comment2\n"
  . "10 6 * * * date\n"
  . "0 23-7/2,8 * * *   ( date >/tmp/date ) &"
);
my $cwd = quotemeta(get_project_dir());
$cwd =~ s{\\([/,._-])}{$1}xmsg; # unquote safe chars for readability
my $expect =
    "# comment1\n"
  . "* * * * * cd $cwd || exit; echo ok\n"
  . "# comment2\n"
  . "10 6 * * * cd $cwd || exit; date\n"
  . "0 23-7/2,8 * * *   cd $cwd || exit; ( date >/tmp/date ) &"
    ;
is($process, $expect,
    'process: ok');

# - get_markers()
#   . test regexp will match blocks composed from:
#     * start."\n".end."\n"
#     * start."\n".line1."\n".end."\n"
#     * start."\n".line1."\n".line2."\n".end."\n"
#     * start."\n".end
#   . test regexp will not match blocks composed from:
#     * junk.start."\n".end."\n"
#     * start.junk."\n".end."\n"
#     * start."\n".junk.end."\n"
#     * start."\n".end.junk."\n"

$cwd = get_project_dir();
my($re, $start, $end) = get_markers();

my $expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
like($expected, $re, 'get_markers: start.\n.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "1 * * * * echo line1\n"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
like($expected, $re, 'get_markers: start.\n.line1.\n.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "1 * * * * echo line1\n"
  . "\n"
  . "1 * * * * echo line2\n"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
like($expected, $re, 'get_markers: start.\n.line1.\n.line2.\n.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "# LEAVE Narada: $cwd"
    ;
like($expected, $re, 'get_markers: start.\n.end');

$expected =
    "* 1 * * * echo ups;"
  . "# ENTER Narada: $cwd"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
unlike($expected, $re, 'get_markers: junk.start.\n.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "# some unexpected user comment"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
unlike($expected, $re, 'get_markers: start.junk\n.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "* 1 * * * echo junk!"
  . "# LEAVE Narada: $cwd"
  . "\n"
    ;
unlike($expected, $re, 'get_markers: start.\n.junk.end.\n');

$expected =
    "# ENTER Narada: $cwd"
  . "\n"
  . "# LEAVE Narada: $cwd"
  . "* 1 * * * echo junk!"
  . "\n"
    ;
unlike($expected, $re, 'get_markers: start.\n.end.junk.\n');



# get_user_crontab(), set_user_crontab()
#   . Test MANUALLY!
# - force_last_cr()
#   . Will be tested while testing set_cron().
# - set_cron()
#   . project crontab variants:
#     * empty
#     * single line without last \n
#     * single line with last \n
#     * multi line without last \n
#     * multi line with last \n
#   . user crontab variants:
#     * empty
#     * has user data and another narada project block without last \n
#     * has user data and another narada project block with last \n
#   . operations:
#     * add
#     * update

{ 
    my $got;
    my $user_crontab;
    my $m = new Test::MockModule('main');
    $m->mock('get_user_crontab', sub { $user_crontab; });
    $m->mock('set_user_crontab', sub { $got = shift() });

    my $start = "# ENTER Narada: $cwd";
    my $end   = "# LEAVE Narada: $cwd";

    my $single_line = '* * 1 * * echo singleline';
    my $multi_line = 
        '# multiline crontab example'
      . "\n"
      . '* * 1 * * echo firstline;'
      . "\n"
      . '* * 1 * * echo secondline;'
        ;
    my $narada_project = 
        '* 1 * * * * echo user_data;' 
      . "\n"
      . '# ENTER Narada: /some/path/to/narada'
      . "\n"
      . '1 * * * * cd /some/path/to/narada || exit; echo Narada;'
      . "\n"
      . '# LEAVE Narada: /some/path/to/narada'
        ;
    my $my_narada_project = $start
              . "\n"
              . "1 * * * * echo current_narada_proj;"
              . "\n"
              . $end
              ;

    my %crontab = ('empty'                       => q{},
                   'single line without last \n' => $single_line,
                   'single line with last \n'    => $single_line."\n",
                   'multi line without last \n'  => $multi_line,
                   'multi line with last \n'     => $multi_line."\n",
                  );
    my %user_crontab = (
        'empty' => q{},
        'has user data and another narada project block without last \n'
                => $narada_project,
        'has user data and another narada project block with last \n'
                => $narada_project."\n",
                       );
   
    for my $action('add','edit'){
        for my $crontab (sort keys %crontab){
            for (sort keys %user_crontab){
                if ($action eq 'add'){
                    $user_crontab = $user_crontab{$_};
                    $expected = force_last_cr($user_crontab{$_})
                              . $start
                              . "\n"
                              . force_last_cr($crontab{$crontab})
                              . $end
                              . "\n"
                            ;
                }else{
                    $user_crontab = $my_narada_project."\n".$user_crontab{$_};
                    $expected = $start
                              . "\n"
                              . force_last_cr($crontab{$crontab})
                              . $end
                              . "\n"
                              . $user_crontab{$_}
                            ;
                }
                $got=q{};
                set_cron($crontab{$crontab});
                is ($got,
                    $expected,
                    "action: $action crontab: $crontab user crontab: $_"
                   );
            }
        }
    }
# - del_cron()
# #   . user crontab variants:
# #     * no block
# #     * empty block
# #     * block with data
    $user_crontab = $multi_line;
    del_cron();
    $expected = $multi_line;
    is($got, $expected, 'del_cron: no block');

    $user_crontab = $start."\n".$end."\n".$multi_line;
    del_cron();
    $expected = $multi_line;
    is($got, $expected, 'del_cron: empty block');

    $user_crontab = $single_line."\n".$start."\n".$multi_line."\n".$end."\n".$narada_project;
    del_cron();
    $expected = $single_line."\n".$narada_project;
    is($got, $expected, 'del_cron: block with data');
}


done_testing();


sub Echo {
    my ($filename, $content) = @_;
    open my $fh, '>', $filename                         or die "open: $!";
    print {$fh} $content;
    close $fh                                           or die "close: $!";
    return;
}