package Alzabo::Test::Utils;
use strict;
use Alzabo::Config;
use Cwd ();
use File::Path ();
use File::Spec;
use Module::Build;
# This should always happen whenever the module is loaded
__PACKAGE__->create_test_schema_dir;
# Used in a number of test files
sub main::eval_ok (&$)
{
my ( $code, $name ) = @_;
eval { $code->() };
if ( my $e = $@ )
{
Test::More::ok( 0, $name );
Test::More::diag(" got error: $e\n" );
}
else
{
Test::More::ok( 1, $name );
}
}
sub create_test_schema_dir
{
my $class = shift;
my $schema_dir = $class->_schema_dir;
unless ( -d $schema_dir )
{
mkdir $schema_dir, 0755
or die "Can't make dir $schema_dir for testing: $!\n";
}
}
sub _schema_dir
{
my $class = shift;
return File::Spec->catdir( $class->_root_dir, 'schemas' );
}
sub _root_dir
{
my $cwd = Cwd::cwd();
my $root_dir = File::Spec->catdir( $cwd, 't' );
Alzabo::Config::root_dir($root_dir);
return $root_dir;
}
sub rdbms_names
{
my $class = shift;
my %c = $class->test_config;
return sort keys %c;
}
sub rdbms_count
{
my $class = shift;
return scalar $class->rdbms_names;
}
sub test_config
{
my $build = Module::Build->current;
my $tests = $build->notes('test_config');
return map { $_->{rdbms} => $_ } @$tests;
}
sub mysql_test_config
{
my $class = shift;
my %t = $class->test_config;
return $t{mysql};
}
sub pg_test_config
{
my $class = shift;
my %t = $class->test_config;
return $t{pg};
}
sub test_config_for
{
my $class = shift;
my $rdbms = shift;
my $meth = "${rdbms}_test_config";
return $class->$meth();
}
sub connect_params_for
{
my $class = shift;
my $config = $class->test_config_for( shift );
return ( map { defined $config->{$_}
? ( $_ => $config->{$_} )
: () }
qw( user password host port )
);
}
sub cleanup
{
my $class = shift;
$class->remove_schema_dir;
$class->remove_all_schemas;
}
sub remove_schema_dir
{
my $class = shift;
my $dir = $class->_schema_dir;
Test::More::diag( "Removing test schema directory: $dir" );
File::Path::rmtree( $dir, $Test::Harness::verbose, 0 );
}
sub remove_all_schemas
{
my $class = shift;
$class->remove_schema($_) foreach 'mysql', 'pg';
}
sub remove_schema
{
my $class = shift;
my $rdbms = shift;
my $meth = "remove_${rdbms}_schema";
$class->$meth();
}
sub remove_mysql_schema
{
my $class = shift;
my $config = $class->mysql_test_config();
return unless keys %$config;
Test::More::diag( "Removing MySQL database $config->{schema_name}" );
my $s = $class->_load_or_create( name => $config->{schema_name},
rdbms => 'MySQL' );
delete @{ $config }{ 'schema_name', 'rdbms' };
eval { $s->drop( %$config ) };
eval { $s->drop( %$config, schema_name => $s->name . '_2' ) };
$s->delete if $s->is_saved;
}
sub remove_pg_schema
{
my $class = shift;
my $config = $class->pg_test_config();
return unless keys %$config;
Test::More::diag( "Removing PostgreSQL database $config->{schema_name}" );
my $s = $class->_load_or_create( name => $config->{schema_name},
rdbms => 'PostgreSQL' );
delete @{ $config }{ 'schema_name', 'rdbms' };
eval { $s->drop(%$config) };
eval { $s->drop( %$config, schema_name => $s->name . '_2' ) };
$s->delete if $s->is_saved;
}
sub _load_or_create
{
my $class = shift;
my %p = @_;
require Alzabo::Create::Schema;
my $s;
$s = eval { Alzabo::Create::Schema->load_from_file( name => $p{name} ) };
return $s if $s;
return Alzabo::Create::Schema->new(%p);
}
sub any_connected_runtime_schema
{
my $class = shift;
my $rdbms = ( $class->rdbms_names )[0];
return unless $rdbms;
my $s = $class->make_schema($rdbms);
my $r = Alzabo::Runtime::Schema->load_from_file( name => $s->name );
$r->connect( $class->connect_params_for($rdbms) );
return $r;
}
sub any_schema_name
{
my $class = shift;
my $rdbms = ( $class->rdbms_names )[0];
my $s = $class->make_schema($rdbms);
return $s->name;
}
sub make_schema
{
my $class = shift;
my $rdbms = shift;
my $skip_create = shift;
my $meth = "make_${rdbms}_schema";
return $class->$meth($skip_create);
}
sub make_mysql_schema
{
my $class = shift;
my $skip_create = shift;
my $config = $class->mysql_test_config;
my $s = Alzabo::Create::Schema->new( name => $config->{schema_name},
rdbms => 'MySQL',
);
$s->make_table( name => 'employee',
attributes => [ 'TYPE=MYISAM' ],
);
my $emp_t = $s->table('employee');
$emp_t->make_column( name => 'employee_id',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$emp_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$emp_t->make_column( name => 'smell',
type => 'varchar',
length => 200,
nullable => 0,
default => 'grotesque',
);
$emp_t->make_column( name => 'cash',
type => 'float',
length => 6,
precision => 2,
nullable => 1,
);
$emp_t->make_column( name => 'tstamp',
type => 'integer',
nullable => 1,
);
# only here to test that making an enum works - not used in tests
$emp_t->make_column( name => 'test_enum',
type => "enum('foo','bar')",
nullable => 1 );
$emp_t->make_index( columns => [ { column => $emp_t->column('name'),
prefix => 10 },
{ column => $emp_t->column('smell') },
] );
# Having a fulltext index tests handling of mysql fulltext index
# sub_part bug when reverse engineering
$emp_t->make_index( columns => [ { column => $emp_t->column('name') } ],
fulltext => 1 );
$s->make_table( name => 'department',
attributes => [ 'TYPE=MYISAM' ],
);
my $dep_t = $s->table('department');
$dep_t->make_column( name => 'department_id',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$dep_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$dep_t->make_column( name => 'manager_id',
type => 'int',
length => 200,
nullable => 1,
);
$s->add_relationship( table_from => $dep_t,
table_to => $emp_t,
columns_from => $dep_t->column('manager_id'),
columns_to => $emp_t->column('employee_id'),
cardinality => [1, 1],
from_is_dependent => 0,
to_is_dependent => 0,
);
$s->add_relationship( table_from => $emp_t,
table_to => $dep_t,
cardinality => ['n', 1],
from_is_dependent => 1,
to_is_dependent => 0,
);
$s->make_table( name => 'project',
attributes => [ 'TYPE=MYISAM' ],
);
my $proj_t = $s->table('project');
$proj_t->make_column( name => 'project_id',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$proj_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$proj_t->make_index( columns => [ { column => $proj_t->column('name'),
prefix => 20 } ] );
$proj_t->make_column( name => 'blobby',
type => 'text',
nullable => 1,
);
$s->add_relationship( table_from => $proj_t,
table_to => $dep_t,
cardinality => ['n', 1],
from_is_dependent => 1,
to_is_dependent => 0,
);
$emp_t->column('department_id')->set_name('dep_id');
$s->add_relationship( table_from => $emp_t,
table_to => $proj_t,
cardinality => ['n', 'n'],
from_is_dependent => 0,
to_is_dependent => 0,
);
$s->table('employee_project')->set_attributes( 'TYPE=MYISAM' );
my $char_pk_t = $s->make_table( name => 'char_pk',
attributes => [ 'TYPE=MYISAM' ],
);
$char_pk_t->make_column( name => 'char_col',
type => 'varchar',
length => 40,
primary_key => 1 );
my $outer_1_t = $s->make_table( name => 'outer_1',
attributes => [ 'TYPE=MYISAM' ],
);
$outer_1_t->make_column( name => 'outer_1_pk',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$outer_1_t->make_column( name => 'outer_1_name',
type => 'varchar',
length => 40,
);
$outer_1_t->make_column( name => 'outer_2_pk',
type => 'int',
nullable => 1,
);
my $outer_2_t = $s->make_table( name => 'outer_2',
attributes => [ 'TYPE=MYISAM' ],
);
$outer_2_t->make_column( name => 'outer_2_pk',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$outer_2_t->make_column( name => 'outer_2_name',
type => 'varchar',
length => 20,
);
$s->add_relationship( table_from => $outer_1_t,
table_to => $outer_2_t,
columns_from => $outer_1_t->column('outer_2_pk'),
columns_to => $outer_2_t->column('outer_2_pk'),
cardinality => [1, 1],
from_is_dependent => 0,
to_is_dependent => 0,
);
my $u = $s->make_table( name => 'user',
attributes => [ 'TYPE=MYISAM' ],
);
$u->make_column( name => 'user_id', type => 'integer', primary_key => 1 );
unless ($skip_create)
{
delete @{ $config }{'rdbms', 'schema_name'};
$s->create(%$config);
$s->driver->disconnect;
}
$s->save_to_file;
return $s;
}
# make sure to use native types or Postgres converts them and then the
# reverse engineering tests fail.
sub make_pg_schema
{
my $class = shift;
my $skip_create = shift;
my $config = $class->pg_test_config;
my $s = Alzabo::Create::Schema->new( name => $config->{schema_name},
rdbms => 'PostgreSQL',
);
$s->make_table( name => 'employee' );
my $emp_t = $s->table('employee');
$emp_t->make_column( name => 'employee_id',
type => 'serial',
sequenced => 1,
primary_key => 1,
);
$emp_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$emp_t->make_column( name => 'smell',
type => 'varchar',
length => 200,
nullable => 1,
default => 'grotesque',
);
$emp_t->make_column( name => 'cash',
type => 'numeric',
length => 6,
precision => 2,
nullable => 1,
);
$emp_t->make_column( name => 'tstamp',
type => 'integer',
nullable => 1,
);
$emp_t->make_index( columns => [ { column => $emp_t->column('name') } ] );
$emp_t->make_index( columns => [ { column => $emp_t->column('smell') } ],
function => 'lower(smell)',
);
$s->make_table( name => 'department');
my $dep_t = $s->table('department');
$dep_t->make_column( name => 'department_id',
type => 'int4',
sequenced => 1,
primary_key => 1,
);
$dep_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$dep_t->make_column( name => 'manager_id',
type => 'int4',
nullable => 1,
);
$s->add_relationship( table_from => $dep_t,
table_to => $emp_t,
columns_from => $dep_t->column('manager_id'),
columns_to => $emp_t->column('employee_id'),
cardinality => [ 1, 1 ],
from_is_dependent => 0,
to_is_dependent => 0,
);
$s->add_relationship( table_from => $emp_t,
table_to => $dep_t,
cardinality => ['n', 1],
from_is_dependent => 1,
to_is_dependent => 0,
);
$s->make_table( name => 'project' );
my $proj_t = $s->table('project');
$proj_t->make_column( name => 'project_id',
type => 'int4',
sequenced => 1,
primary_key => 1,
);
$proj_t->make_column( name => 'name',
type => 'varchar',
length => 200,
);
$proj_t->make_column( name => 'blobby',
type => 'text',
nullable => 1,
);
$s->add_relationship( table_from => $emp_t,
table_to => $proj_t,
cardinality => ['n', 'n'],
from_is_dependent => 0,
to_is_dependent => 0,
);
$proj_t->make_index( columns => [ { column => $proj_t->column('name') } ] );
$emp_t->column('department_id')->set_name('dep_id');
$s->add_relationship( table_from => $proj_t,
table_to => $dep_t,
cardinality => ['n', 1],
from_is_dependent => 1,
to_is_dependent => 0,
);
my $char_pk_t = $s->make_table( name => 'char_pk' );
$char_pk_t->make_column( name => 'char_col',
type => 'varchar',
length => 20,
primary_key => 1 );
$char_pk_t->make_column( name => 'fixed_char',
type => 'char',
nullable => 1,
length => 5 );
my $outer_1_t = $s->make_table( name => 'outer_1' );
$outer_1_t->make_column( name => 'outer_1_pk',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$outer_1_t->make_column( name => 'outer_1_name',
type => 'varchar',
length => 40,
);
$outer_1_t->make_column( name => 'outer_2_pk',
type => 'int',
nullable => 1,
);
my $outer_2_t = $s->make_table( name => 'outer_2' );
$outer_2_t->make_column( name => 'outer_2_pk',
type => 'int',
sequenced => 1,
primary_key => 1,
);
$outer_2_t->make_column( name => 'outer_2_name',
type => 'varchar',
length => 40,
);
$s->add_relationship( table_from => $outer_1_t,
table_to => $outer_2_t,
columns_from => $outer_1_t->column('outer_2_pk'),
columns_to => $outer_2_t->column('outer_2_pk'),
cardinality => [1, 1],
from_is_dependent => 0,
to_is_dependent => 0,
);
my $mixed = $s->make_table( name => 'MixEDCasE' );
$mixed->make_column( name => 'mixed_CASE_Pk',
type => 'integer',
primary_key => 1 );
my $name = $config->{schema_name};
unless ($skip_create)
{
delete @{ $config }{'rdbms', 'schema_name'};
$s->create(%$config);
$s->driver->disconnect;
}
$s->save_to_file;
return $s;
}
1;
__END__
=head1 DESCRIPTION
Alzabo::Test::Utils - Utility module for Alzabo test suite
=head1 SYNOPSIS
use Alzabo::Test::Utils;
Alzabo::Test::Utils->
=cut
syntax highlighted by Code2HTML, v. 0.9.1