The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
# vim:ts=8:sw=2:et:sta:sts=2
use strict;
use warnings;
use Test::More;
use English qw(-no_match_vars);
use Test::Trap;

eval {
  require DBD::SQLite;
  plan tests => 81;
} or do {
  plan skip_all => 'DBD::SQLite not installed';
};

use lib qw(t/lib);
use t::util;

our $CTRL = 'ClearPress::controller';
use_ok($CTRL);

my $util = t::util->new();

my $T = [
	 ['GET', '/',                          '', {}, 'read',  'example', 'list', 0], #default_view
	 ['GET', '/thing/method',              '', {}, 'read',  'thing', 'read', 'method'],
	 ['GET', '/thing2/method',             '', {}, 'read',  'thing2', 'list_method', 0],
	 ['GET', '/thing/method/50',           '', {}, 'read',  'thing', 'read_method', 50],
	 ['GET', '/thing3/avg/by/pos',         'id_run=1234', {}, 'read', 'thing3', 'list_avg_by_pos', 0],
	 ['GET', '/thing4/avg/by/pos.xml',     'id_run=1234', {}, 'read', 'thing4', 'list_avg_by_pos_xml', 0],
	 ['GET', '/thing5/avg/by/pos.xml',     'id_run=1234', {}, 'read', 'thing5', 'read_avg_by_xml', 'pos'],
	 ['GET', '/thing',                     '', {}, 'read',   'thing', 'list',     0],
	 ['GET', '/thing/1',                   '', {}, 'read',   'thing', 'read',     1],
	 ['GET', '/thing.xml',                 '', {}, 'read',   'thing', 'list_xml', 0],
	 ['GET', '/thing/1.xml',               '', {}, 'read',   'thing', 'read_xml', 1],
	 ['GET', '/thing;list_xml',            '', {}, 'read',   'thing', 'list_xml', 0],
	 ['GET', '/thing;do_stuff',            '', {}, 'read',   'thing', 'list_do_stuff', 0],
	 ['GET', '/thing/1;read_xml',          '', {}, 'read',   'thing', 'read_xml', 1],
	 ['GET', '/thing;add',                 '', {}, 'read',   'thing', 'add',      0],
	 ['GET', '/thing;add_xml',             '', {}, 'read',   'thing', 'add_xml',  0],
	 ['GET', '/thing.xml;add',             '', {}, 'read',   'thing', 'add_xml',  0],

	 ['GET', '/thing/1;edit',              '', {}, 'read',   'thing', 'edit',     1],
	 ['GET', '/thing/edit/1',              '', {}, 'read',   'thing', 'edit',     1],

	 ['GET', '/thing/edit/1.ajax',         '', {}, 'read',   'thing', 'edit_ajax', 1],
	 ['GET', '/thing/edit_ajax/1',         '', {}, 'read',   'thing', 'edit_ajax', 1],

	 ['GET', '/thing/edit_batch/1',        '', {}, 'read',   'thing', 'edit_batch', 1],
	 ['GET', '/thing/edit_batch/1.ajax',   '', {}, 'read',   'thing', 'edit_batch_ajax', 1],
	 ['GET', '/thing/edit_batch_ajax/1',   '', {}, 'read',   'thing', 'edit_batch_ajax', 1],
	 ['GET', '/thing/1.ajax;edit_batch',   '', {}, 'read',   'thing', 'edit_batch_ajax', 1],
	 ['GET', '/thing/1;edit_batch_ajax',   '', {}, 'read',   'thing', 'edit_batch_ajax', 1],

	 ['POST', '/thing/batch/1',             '', {}, 'update', 'thing', 'update_batch', 1],
	 ['POST', '/thing/batch/1.ajax',        '', {}, 'update', 'thing', 'update_batch_ajax', 1],
	 ['POST', '/thing/update_batch/1',      '', {}, 'update', 'thing', 'update_batch', 1],
	 ['POST', '/thing/batch/1.ajax',        '', {}, 'update', 'thing', 'update_batch_ajax', 1],
	 ['POST', '/thing/1.ajax;update_batch', '', {}, 'update', 'thing', 'update_batch_ajax', 1],
	 ['POST', '/thing/1;update_batch_ajax', '', {}, 'update', 'thing', 'update_batch_ajax', 1],

	 ['GET', '/thing/released/cluster.xml', '', {}, 'read', 'thing', 'read_released_xml', 'cluster'],

	 ['GET', '/user/me@example.com;edit',  '', {}, 'read',   'user',   'edit', 'me@example.com'],
	 ['GET', '/thing/heatmap.png',         '', {}, 'read',   'thing',  'read_png', 'heatmap'],
	 ['GET', '/thing5/heatmap.png',        '', {}, 'read',   'thing5', 'list_heatmap_png',   0],
	 ['GET', '/thing9/heatmap',            '', {}, 'read',   'thing9', 'list_heatmap',       0],
	 ['GET', '/thing/heatmap/45.png',      '', {}, 'read',   'thing',  'read_heatmap_png',   45],
	 ['POST', '/thing/heatmap/45.png',     '', {}, 'update', 'thing',  'update_heatmap_png', 45],

	 ['POST', '/thing',                    '', {}, 'create', 'thing', 'create', 0],
	 ['POST', '/thing.xml',                '', {}, 'create', 'thing', 'create_xml', 0],
	 ['POST', '/thing;create_xml',         '', {}, 'create', 'thing', 'create_xml', 0],
	 ['POST', '/thing/10',                 '', {}, 'update', 'thing', 'update', 10],
	 ['POST', '/thing/10.xml',             '', {}, 'update', 'thing', 'update_xml', 10],
	 ['POST', '/thing/10;update_xml',      '', {}, 'update', 'thing', 'update_xml', 10],
	 ['POST', '/thing/update/10.xml',      '', {}, 'update', 'thing', 'update_xml', 10],
	 ['POST', '/thing10/heatmap.png',      '', {}, 'create', 'thing10', 'create_heatmap_png', 0],

	 ['POST', '/thing6/batch.xml',         '', {}, 'create', 'thing6', 'create_batch_xml', 0],
	 ['POST', '/thing6/batch.xml',         '', {
						    HTTP_ACCEPT => 'text/xml',
						   }, 'create', 'thing6', 'create_batch_xml', 0],
	 ['POST', '/thing7/batch',             '', {
						    HTTP_X_REQUESTED_WITH => 'XMLHttpRequest',
						   },  'create', 'thing7', 'create_batch_ajax', 0], ###### fail
	 ['POST', '/thing7;create_batch',      '', {
						    HTTP_X_REQUESTED_WITH => 'XMLHttpRequest',
						   },  'create', 'thing7', 'create_batch_ajax', 0],
	 ['POST', '/thing7;create_batch_ajax', '', {}, 'create', 'thing7', 'create_batch_ajax', 0],
	 ['POST', '/thing7.ajax;create_batch', '', {}, 'create', 'thing7', 'create_batch_ajax', 0],
	 ['POST', '/thing8/batch.xml',         '', {
						    HTTP_X_REQUESTED_WITH => 'XMLHttpRequest',
						   },  'create', 'thing8', 'create_batch_xml', 0],
	 ['DELETE', '/thing/10',               '', {}, 'delete', 'thing', 'delete', 10],
	 ['POST',   '/thing/10;delete',        '', {}, 'delete', 'thing', 'delete', 10],

         ['GET', '/thing11/overridden',        '', {
                                                    HTTP_X_REQUESTED_WITH => 'XmlHttpRequest',
                                                   }, 'read', 'thing11', 'list_overridden_ajax', 0],
         ['GET', '/thing12.txt',               '', {
                                                    HTTP_X_REQUESTED_WITH => 'XmlHttpRequest',
                                                   }, 'read', 'thing12', 'list_txt', 0],
         ['GET', '/thing/12.txt',              '', {
                                                    HTTP_X_REQUESTED_WITH => 'XmlHttpRequest',
                                                   }, 'read', 'thing', 'read_txt', 12],
	 ['GET', '/testmap/test.xml',          '', {}, 'read', 'testmap', 'list_test_xml', 0],

         ['GET', '/thing/valid_flowcell/12.js', '', {
                                                     HTTP_X_REQUESTED_WITH => 'XmlHttpRequest',
                                                    }, 'read', 'thing', 'read_valid_flowcell_json', 12],
         ['GET', '/thing/valid_flowcell_json/12', '', {
                                                     HTTP_X_REQUESTED_WITH => 'XmlHttpRequest',
                                                    }, 'read', 'thing', 'read_valid_flowcell_json', 12],
	 ['OPTIONS', '/thing',                    '', {}, 'options',  'thing', 'options', 0],
	];

{
  no warnings;
  *{t::view::thing2::list_method}           = sub { return 1; };
  *{t::view::thing3::list_avg_by_pos}       = sub { return 1; };
  *{t::view::thing4::list_avg_by_pos_xml}   = sub { return 1; };
  *{t::view::thing5::list_heatmap_png}      = sub { return 1; };
  *{t::view::thing6::create_batch_xml}      = sub { return 1; };
  *{t::view::thing7::create_batch_ajax}     = sub { return 1; };
  *{t::view::thing8::create_batch_xml}      = sub { return 1; };
  *{t::view::thing9::list_heatmap}          = sub { return 1; };
  *{t::view::thing10::create_heatmap_png}   = sub { return 1; };
  *{t::view::thing11::list_overridden_ajax} = sub { return 1; };
  *{t::view::thing12::list_txt_ajax}        = sub { return 1; };
  *{t::view::foo::test::list_test_xml}      = sub { return 1; }; # packagemapped
}

{
  my $ctrl = $CTRL->new({util => $util});
  is($ctrl->packagespace('view', 'testmap', $util),
     't::view::foo::test',
     'packagemapped space');
}

sub request_test {
  my $t = shift;
  local %ENV = (
		DOCUMENT_ROOT  => 't/htdocs',
		REQUEST_METHOD => $t->[0],
		PATH_INFO      => $t->[1],
		QUERY_STRING   => $t->[2],
		%{$t->[3]},
	       );
  my $ctrl    = $CTRL->new({util => $util});
  my $headers = HTTP::Headers->new;
  my $ref     = [];
  eval {
    $ref = [$ctrl->process_request($headers)];

  } or do {
    diag($EVAL_ERROR);
  };

  is((join q[,], @{$ref}),
     (join q[,], grep { defined } @{$t}[4..7]),
     "$t->[0] $t->[1]?$t->[2] => @{[join q[, ], grep {defined} @{$t}[4..7]]}");
  delete $util->{cgi};
}

for my $t (@{$T}) {
  request_test($t);
}

{
  no warnings qw(redefine once);
  local *t::util::data_path = sub { return 't/data-no-default_view'; };
  delete $util->{config};
  request_test(['GET', '/', '', {}, 'read',  'no_default', 'list', 0]);
}

{
  no warnings qw(redefine once);
  local *t::util::data_path = sub { return 't/data-no-views'; };
  delete $util->{config};
  request_test(['GET', '/', '', {}]);
}

my $B = [# METHOD PATH_INFO    QUERY_STRING CGI COMMENT
	 ['POST', '/thing/10;read_xml', '', {}, 'update vs. read'],
	 ['POST', '/thing;read',        '', {}, 'create vs. read'],
	 ['GET',  '/thing/10;delete',   '', {}, 'read vs. delete'],
	 ['GET',  '/thing;read',        '', {}, 'read without id'],
	 ['POST', '/thing;update',      '', {}, 'update without id'],
	 ['GET',  '/thing;edit',        '', {}, 'edit without id'],
	 ['POST', '/thing;delete',      '', {}, 'delete without id'],
	 ['POST', '/thing/10;create',   '', {}, 'create with id'],
	 ['GET',  '/thing/10;list',     '', {}, 'list with id'],
	 ['GET',  '/thing/10;add',      '', {}, 'add with id'],
	 ];

for my $b (@{$B}) {
  local %ENV = (
		DOCUMENT_ROOT  => 't/htdocs',
		REQUEST_METHOD => $b->[0],
		PATH_INFO      => $b->[1],
		QUERY_STRING   => $b->[2],
		%{$b->[3]},
	       );
  my $ctrl    = $CTRL->new({util => $util});
  my $headers = HTTP::Headers->new;
  my $ref     = [];
  eval {
    $ref = [$ctrl->process_request($headers)];
  };

  if(scalar @{$ref}) {
    diag(join q[,], @{$ref});
  }

  like($EVAL_ERROR, qr/Bad[ ]request/smx, $b->[4]);
  delete $util->{cgi};
}

{
  delete $util->{config};

  local %ENV = (
		DOCUMENT_ROOT  => 't/htdocs',
		REQUEST_METHOD => 'GET',
		QUERY_STRING   => q[],
		PATH_INFO      => '/thing/10',
	       );

  my $ctrl = $CTRL->new({util => $util});
  trap {
    $ctrl->handler($util);
  };

  like($trap->stdout, qr{Status:[ ]500}smix,  'error response status');
  unlike($trap->stdout, qr/charset=UTF-8/smx, 'error response header is NOT UTF-8 by default');
  delete $util->{cgi};
}

{
  delete $util->{config};
$util->config->setval('application', 'views', 'thing20');
  local %ENV = (
		DOCUMENT_ROOT  => 't/htdocs',
		REQUEST_METHOD => 'GET',
		QUERY_STRING   => q[],
		PATH_INFO      => '/thing20',
	       );

  my $ctrl = $CTRL->new({util => $util});
  trap {
    $ctrl->handler($util);
  };

  like($trap->stdout, qr{Status:[ ]200}smix, 'non-error response status');
  like($trap->stdout, qr/charset=UTF-8/smx,  'non-error response header is UTF-8 by default');
  delete $util->{cgi};
}

package t::view::thing;
use base qw(ClearPress::view);

1;

package t::view::thing20;
use base qw(ClearPress::view);

sub decor { return; }
sub streamed_aspects { return ['list']; }
sub list {
  my $self = shift;
  $self->output_buffer($self->headers->as_string, "\n");
  $self->output_buffer(q[list]);
  return q[];
}
1;

package t::model::thing;
use base qw(ClearPress::model);

1;

package t::model::thing20;
use base qw(ClearPress::model);

1;