The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Before 'make install' is performed this script should be runnable with
# 'make test'. After 'make install' it should work as
# 'perl t/01-basic-standalone-tests.t'
use strict;
use warnings;

use lib 'lib'; # Where MPA should live


# 01-basic-tests
#
# This test runs without any external dependencies to make sure the
# Authorization modules itself appears to be intact

#########################

use Test::More tests => 16;
my $DEBUG = 0;


########################################################################
#
#  SIMULATION
#
#  Before we run the tests, we need to simulate its environment.
#  That is, we need some objects that behave a bit like Maypole.
#
########################################################################

# Simulate the configuration
{
package Maypole::Config;					# singleton

my $self;
sub new { return $self || ($self = bless {}, __PACKAGE__) }
sub auth { return Maypole::Config::Auth->new }
sub mk_accessors { print STDERR "mk_accessors called\n"; }
}

{
package Maypole::Config::Auth;					# singleton

my $self;
sub new { return $self || ($self =
		bless {
			user_class => 'Model::User'
		}, __PACKAGE__)
	}
}

# Simulate a DBI statement handle
# 
# This class also simulates the database content
# $self->{x} is set to either classes or methods when the statement is
# created. execute checks whether the supplied args are sensible in the
# particular case (get_*_classes or get_*_methods). If permission should
# be granted, it leaves $self->{x} alone, but if not, it deletes it.
# fetchall_arrayref then returns an appropriate data structure depending
# on the value of $self->{x}
{
package Statement;

sub new
{
  my ($self, $x, $u) = @_;
  return bless { x => $x, u => $u }, __PACKAGE__;
}

sub execute
{
  my ($self, $userid, $class) = @_;
  my $user = $self->{u};
  if ($self->{x} eq 'classes') {
    delete $self->{x} unless $userid == $user and not defined $class
  }
  elsif ($self->{x} eq 'methods') {
    print STDERR "execute: userid=$userid, user=$user, class=$class\n"
      if $DEBUG;
    delete $self->{x} unless $userid == $user and $class eq 'Model::Class';
  }
  else {
    delete $self->{x}
  }
}

sub fetchall_arrayref
{
  my $self = shift;
  return [] unless $self->{x};
  return [['Model::Class']] if $self->{x} eq 'classes';
  return [['action']] if $self->{x} eq 'methods';
}

}

# Simulate a user (and CDBI :)
{
package Model::User;						# singleton

my $hash;
sub new { return $hash || ($hash = bless {id => 42}, __PACKAGE__) }
sub id { return shift->{id} }
sub set_sql { }
sub select_val
{
  my ($self, $userid, $class, $method) = @_;
  return $userid == $self->{id}
    and $class eq 'Model::Class' and $method eq 'action';
}

sub sql_check_authorization    { return $hash }
sub sql_get_authorized_classes { return Statement->new('classes', $hash->{id})}
sub sql_get_authorized_methods { return Statement->new('methods', $hash->{id})}
}

# Simulate a request object
{
package Request;						# singleton

use base 'Maypole::Plugin::Authorization';

my $hash;
sub new
{
  return ($hash = bless {
 	model => 'Model::Class',
	user  => Model::User->new,
	}, __PACKAGE__);
}

sub action { return 'action' }
sub config { return new Maypole::Config }
sub model_class { shift->{model} }
sub user { return shift->{user} }

}


########################################################################
#
#  TESTS
#
#  We can test the basic behaviour of the module
#  now we have a suitable environment.
#
########################################################################

# First test we can load the module

require_ok('Maypole::Plugin::Authorization');


# Test we can call each method successfully

# Test authorize method
my $r = new Request;
ok(Maypole::Plugin::Authorization->authorize($r),
  'authorize handles basic case');

# Test get_authorized_classes method
my @c = Request->new->get_authorized_classes;
ok((@c == 1 and $c[0] eq 'Model::Class'),
  'get_authorized_classes handles basic case');

# Test get_authorized_methods method
my @m = Request->new->get_authorized_methods;
ok((@m == 1 and $m[0] eq 'action'),
  'get_authorized_methods handles basic case');


# Test various combinations of parameters

# Test get_authorized_classes method with explicit userid
$r = new Request;
$r->user->{id} = 27;
@c = $r->get_authorized_classes(27);
ok((@c == 1 and $c[0] eq 'Model::Class'),
  'get_authorized_classes handles explicit userid');

@c = $r->get_authorized_classes(42);
ok(@c == 0,
  'get_authorized_classes handles unauthorized user');

# Test get_authorized_methods method with explicit userid
@m = $r->get_authorized_methods(27);
ok((@m == 1 and $m[0] eq 'action'),
  'get_authorized_methods handles explicit userid');

@m = $r->get_authorized_methods(42);
ok(@m == 0,
  'get_authorized_methods handles unauthorized user');

# Test get_authorized_methods method with explicit class
@m = $r->get_authorized_methods(undef, 'Model::Class');
ok((@m == 1 and $m[0] eq 'action'),
  'get_authorized_methods handles explicit class');

@m = $r->get_authorized_methods(undef, 'Model::Car');
ok(@m == 0,
  'get_authorized_methods handles unauthorized class');

# Test get_authorized_methods method with explicit userid and class
@m = $r->get_authorized_methods(27, 'Model::Class');
ok((@m == 1 and $m[0] eq 'action'),
  'get_authorized_methods handles explicit userid and class');

@m = $r->get_authorized_methods(27, 'Model::Car');
ok(@m == 0,
  'get_authorized_methods handles unauthorized class no.2');

@m = $r->get_authorized_methods(16, 'Model::Class');
ok(@m == 0,
  'get_authorized_methods handles unauthorized user no.2');


# Test missing implicit parameters

delete $r->{user};
@c = $r->get_authorized_classes;
ok(@c == 0,
  'get_authorized_classes handles no user');

@m = $r->get_authorized_methods;
ok(@m == 0,
  'get_authorized_methods handles no user');

$r = new Request;
delete $r->{model};
@m = $r->get_authorized_methods;
ok(@m == 0,
  'get_authorized_methods handles no model class');