package tests::DlfSchemaTest; use strict; use base qw/Test::Unit::TestSuite/; sub name { return "DlfSchema Tests"; } sub include_tests { return qw/tests::DlfSchemaTestBase tests::DlfSchemaTestI18N /; } package tests::DlfSchemaTestBase; use base qw/ Lire::Test::TestCase /; use Lire::DlfSchema; use Lire::DerivedSchema; use Lire::ExtendedSchema; use Lire::Field; use Lire::DlfStore; use Lire::Utils qw/tempdir/; my $test_schema = < EOF sub new { my $self = shift()->SUPER::new( @_ ); $self->{'tmpdir'} = tempdir( __PACKAGE__ . "XXXXXX", 'TMPDIR' => 1, CLEANUP => 1 ); $self->{'schemapath'} = $self->{'tmpdir'} . "/test.xml"; mkdir $self->{'tmpdir'} . "/schemas", 0755 or $self->error( "failed to make directory: $self->{'tmpdir'}/schemas" ); $self->create_schema( $test_schema, "test", $self->{'schemapath'} ); $self->create_schema( $test_schema, "test-exttest", "$self->{'tmpdir'}/schemas/test-exttest.xml" ); return $self; } sub set_up { my $self = $_[0]; $self->SUPER::set_up(); $self->{'cfg'}{'lr_schemas_path'} = [ $self->{'tmpdir'}, $self->{'tmpdir'} . "/schemas", ]; $self->{'cfg'}{'_lr_config_spec'} = $self->lire_default_config_spec(); # We don't open the store in new() because we need to # change the configuration before $self->{'store'} = Lire::DlfStore->open( "$self->{'tmpdir'}/store", 1 ) unless defined $self->{'store'}; $self->{'_old_cache'} = { %Lire::DlfSchema::SCHEMA_CACHE }; %Lire::DlfSchema::SCHEMA_CACHE = (); return; } sub tear_down { my $self = $_[0]; $self->SUPER::tear_down(); $self->{'store'}->_dbh()->rollback(); %Lire::DlfSchema::SCHEMA_CACHE = %{$self->{'_old_cache'}}; return; } sub create_schema { my ( $self, $template, $name, $path ) = @_; $template =~ s/superservice="test"/superservice="$name"/; open my $fh, "> $path" or die "can't create $name schema: $path: $!"; print $fh $template; close $fh; } sub test_new { my $self = $_[0]; my $schema = new Lire::DlfSchema( 'superservice' => 'test', 'timestamp' => 'a_field' ); $self->assert_isa( 'Lire::DlfSchema', $schema ); $self->assert_str_equals( 'test', $schema->{'id'} ); $self->assert_str_equals( 'test', $schema->{'superservice'} ); $self->assert_str_equals( 'a_field', $schema->{'timestamp_field'} ); $self->assert_num_equals( 2, scalar @{$schema->{'fields_by_pos'}} ); $self->assert( $schema->has_field( 'dlf_id' ), 'missing dlf_id' ); $self->assert( $schema->has_field( 'dlf_source' ), 'missing dlf_source' ); my $dlf_id = $schema->field( 'dlf_id' ); $self->assert_str_equals( 'id', $dlf_id->type() ); $self->assert_not_null( $dlf_id->description(), 'missing description' ); my $dlf_source = $schema->field( 'dlf_source' ); $self->assert_str_equals( 'string', $dlf_source->type() ); $self->assert_not_null( $dlf_source->description(), 'missing description' ); } sub test_add_field { my $self = $_[0]; my $schema = new Lire::DlfSchema( 'superservice' => 'test', 'timestamp' => 'a_field' ); my $field = new Lire::Field( 'name' => 'field', 'type' => 'string' ); $schema->add_field( $field ); $self->assert_str_equals( $field, $schema->{'fields_by_pos'}[-1] ); $self->assert_str_equals( $field, $schema->{'fields_by_name'}{'field'} ); $self->assert_num_equals( $#{$schema->{'fields_by_pos'}}, $field->pos() ); } sub test_schema_load { my $self = $_[0]; my $or_schema = Lire::DlfSchema::load_schema( "test" ); $self->assert_isa( 'Lire::DlfSchema', $or_schema ); $self->assert_str_equals( $or_schema, $Lire::DlfSchema::SCHEMA_CACHE{'test'} ); # Should return cached copy my $schema = Lire::DlfSchema::load_schema( "test" ); $self->assert_str_equals( $or_schema, $schema ); my $test2 = $Lire::DlfSchema::SCHEMA_CACHE{'test2'} = new Lire::DlfSchema( 'superservice' => 'test2', 'timestamp' => 'time' ); $self->assert_str_equals( $test2, Lire::DlfSchema::load_schema( 'test2' ) ); return; } sub test_has_schema { my $self = $_[0]; $self->assert( Lire::DlfSchema->has_schema( "test"), "has_schema( 'test' ) failed" ); $self->assert( Lire::DlfSchema->has_schema( "test-exttest"), "has_schema( 'test-exttest' ) failed" ); $self->assert( ! Lire::DlfSchema->has_schema( "test2"), "has_schema( 'test2' ) failed" ); $Lire::DlfSchema::SCHEMA_CACHE{'test-another'} = {}; $self->assert( Lire::DlfSchema->has_schema( 'test-another' ), 'has_schema should look into cache' ); $self->assert_dies( qr/missing 'schema_name' parameter/, sub { Lire::DlfSchema->has_schema() } ); $self->assert_dies( qr/invalid schema name/, sub { Lire::DlfSchema->has_schema( "../test" ) } ); } sub test_has_superservice { my $self = $_[0]; $self->assert( Lire::DlfSchema->has_superservice( "test" ), "has_schema( 'test' ) failed" ); $self->assert( ! Lire::DlfSchema->has_superservice( "test2" ), "has_schema( 'test2' ) should have failed" ); $Lire::DlfSchema::SCHEMA_CACHE{'test2'} = {}; $self->assert( Lire::DlfSchema->has_superservice( 'test2' ), 'has_schema should look into cache' ); $self->assert_died( sub { Lire::DlfSchema->has_superservice() }, qr/missing 'superservice' parameter/ ); $self->assert_dies( qr/invalid superservice schema name/, sub { Lire::DlfSchema->has_superservice( "test-exttest" ) } ); $self->assert_dies( qr/invalid superservice schema name/, sub { Lire::DlfSchema->has_superservice( "../test" ) } ); } sub test_superservices { my $self = $_[0]; my @super = Lire::DlfSchema->superservices(); $self->assert_deep_equals( [ "test" ], \@super ); } sub test_schemas { my $self = $_[0]; $Lire::DlfSchema::SCHEMA_CACHE{'test2'} = {}; my @schemas = Lire::DlfSchema->schemas(); $self->assert_deep_equals( [ "test2", "test", "test-exttest" ], \@schemas ); } sub test_can_join_schema { my $self = $_[0]; $self->set_up_fake_test_schema(); $Lire::DlfSchema::SCHEMA_CACHE{'test'} = $self->{'test'}; my $test2 = new Lire::DlfSchema( 'superservice' => 'test2', 'timestamp' => 'time' ); $test2->add_field( new Lire::Field( 'name' => 'time', 'type' => 'timestamp' ) ); $Lire::DlfSchema::SCHEMA_CACHE{'test2'} = $test2; my $ext1 = new Lire::ExtendedSchema( 'id' => 'test-ext1', 'base-schema' => 'test', 'module' => 'MyModule' ); my $ext2 = new Lire::ExtendedSchema( 'id' => 'test2-ext1', 'base-schema' => 'test2', 'module' => 'MyModule' ); my $derived = new Lire::DerivedSchema( 'id' => 'test-derived', 'base-schema' => 'test', 'timestamp' => 'time', 'module' => 'MyModule' ); $self->assert( $self->{'test'}->can_join_schema( $ext1 ) ); $self->assert( ! $self->{'test'}->can_join_schema( $self->{'test'} ) ); $self->assert( ! $self->{'test'}->can_join_schema( $ext2 ) ); $self->assert( ! $self->{'test'}->can_join_schema( $test2 ) ); $self->assert( ! $self->{'test'}->can_join_schema( $derived ) ); } sub test__sql_fields_def { my $self = $_[0]; my $test_sql_fields_def = <assert_str_equals( $test_sql_fields_def, $schema->_sql_fields_def() ); } sub set_up_fake_test_schema { my $self = $_[0]; $self->{'test'} = new Lire::DlfSchema( 'superservice' => 'test', 'timestamp' => 'time_start' ); $self->{'test'}->add_field( new Lire::Field( 'name' => 'time_start', 'type' => 'timestamp' ) ); $self->{'test'}->add_field( new Lire::Field( 'name' => 'field-1', 'type' => 'string' ) ); $self->{'test'}->add_field( new Lire::Field( 'name' => 'field_2', 'type' => 'string' ) ); $self->{'test'}->add_field( new Lire::Field( 'name' => 'int_3', 'type' => 'int' ) ); return; } sub set_up_old_test_stream { my $self = $_[0]; $self->{'store'}->_dbh()->do( 'CREATE TABLE dlf_test ( time_start TIMESTAMP, field_removed VARCHAR(100), int_3 NUMBER(10,0) )' ); my $sth = $self->{'store'}{'_dbh'}->prepare( 'INSERT INTO dlf_test VALUES ( ?, ?, ? )' ); $self->{'dlf'} = [ { 'time_start' => time, 'int_3' => 10 }, { 'time_start' => time + 10, 'int_3' => undef }, { 'time_start' => time + 100, 'int_3' => -10 }, { 'time_start' => time + 50, 'int_3' => 3 }, ]; foreach my $dlf ( @{$self->{'dlf'}} ) { $sth->execute( $dlf->{'time_start'}, 'Anything', $dlf->{'int_3'} ); } return; } sub test_sql_table { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->assert_str_equals( 'dlf_test', $self->{'test'}->sql_table() ); $self->assert_str_equals( 'temp_dlf_test', $self->{'test'}->sql_table( 'temp_' ) ); $self->assert_str_equals( 'temp_dlf_test_idx', $self->{'test'}->sql_table( 'temp_', '_idx' ) ); $self->assert_str_equals( 'dlf_test_idx', $self->{'test'}->sql_table( undef, '_idx')); $self->assert_str_equals( '"temp-dlf_test"', $self->{'test'}->sql_table( "temp-" ) ); $self->assert_str_equals( '"dlf_test:idx"', $self->{'test'}->sql_table( undef, ":idx" ) ); $self->{'test'}{'id'} = 'test-extended'; $self->assert_equals( '"dlf_test-extended"', $self->{'test'}->sql_table() ); } sub test_create_sql_schema { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->{'test'}->create_sql_schema( $self->{'store'} ); my $sql_def = $self->{'test'}->_sql_fields_def(); chomp $sql_def; # Trailing newline removed by SQLite my $dbh = $self->{'store'}->_dbh(); my $table = $dbh->selectrow_hashref( "SELECT * FROM sqlite_master WHERE name = 'dlf_test'" ); $self->assert_not_null( $table, "table dlf_test wasn't created" ); $self->assert_matches( qr/\Q$sql_def\E/, $table->{'sql'} ); my $index = $dbh->selectrow_hashref( "SELECT * FROM sqlite_master WHERE name = 'dlf_test_time_start_idx'" ); $self->assert_not_null( $index, "index dlf_test_time_start_idx wasn't created" ); $self->assert_equals( "index", $index->{'type'} ); $self->assert_equals( "CREATE INDEX dlf_test_time_start_idx ON dlf_test ( time_start )", $index->{'sql'} ); return; } sub test__migration_insert_query { my $self = $_[0]; $self->set_up_fake_test_schema(); my $e_sql = "INSERT INTO dlf_test (time_start, \"field-1\", field_2, int_3) SELECT time_start, \"field-1\", field_2, int_3 FROM temp_dlf_test"; my $cfields = [ "time_start", "field-1", "field_2", "int_3"]; $self->assert_equals( $e_sql, $self->{'test'}->_migration_insert_query( $cfields) ); } sub test_needs_sql_schema_migration { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->assert( ! $self->{'test'}->needs_sql_schema_migration($self->{'store'}), "no migration needed" ); $self->set_up_old_test_stream(); $self->assert( $self->{'test'}->needs_sql_schema_migration($self->{'store'}), "migration required" ); } sub test_migrate_sql_schema { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->set_up_old_test_stream(); $self->{'test'}->migrate_sql_schema( $self->{'store'} ); my $records = $self->{'store'}->_dbh()->selectall_hashref( 'SELECT * FROM dlf_test', 'dlf_id' ); my $e_records = {}; my $id = 1; foreach my $dlf ( @{$self->{'dlf'}} ) { $e_records->{$id} = { 'dlf_id' => $id, 'dlf_source' => undef, 'time_start' => $dlf->{'time_start'}, 'field-1' => undef, 'field_2' => undef, 'int_3' => $dlf->{'int_3'} }; $id++; } $self->assert_deep_equals( $e_records, $records ); } sub test_dlf_query { my $self = $_[0]; $self->set_up_fake_test_schema(); my $query = $self->{'test'}->dlf_query( "-time_start field-1" ); $self->assert_isa( 'Lire::DlfQuery', $query ); $self->assert_str_equals( 'test', $query->stream_name() ); $self->assert_deep_equals( [], $query->aggr_fields() ); $self->assert_deep_equals( [], $query->group_fields() ); $self->assert_deep_equals( [ 'dlf_id', 'dlf_source', 'time_start', 'field-1', 'field_2', 'int_3' ], $query->fields() ); $self->assert_str_equals( "time_start DESC, \"field-1\"", $query->order_by_clause() ); } sub test_sql_insert_query { my $self = $_[0]; $self->set_up_fake_test_schema(); my $e_sql = "INSERT INTO dlf_test (dlf_id, dlf_source, time_start, \"field-1\", field_2, int_3) VALUES (?,?,?,?,?,?)"; $self->assert_str_equals( $e_sql, $self->{'test'}->sql_insert_query() ); } sub test_sql_clean_query { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->assert_str_equals( 'DELETE FROM dlf_test', $self->{'test'}->sql_clean_query() ); $self->{'test'}{'timestamp_field'} = 'time-start'; $self->assert_str_equals( 'DELETE FROM dlf_test WHERE "time-start" < ?', $self->{'test'}->sql_clean_query( 1 ) ); } sub test_sql_clean_period_query { my $self = $_[0]; $self->set_up_fake_test_schema(); $self->{'test'}{'timestamp_field'} = 'time-start'; $self->assert_str_equals( 'DELETE FROM dlf_test WHERE "time-start" >= ? AND "time-start" < ?', $self->{'test'}->sql_clean_period_query() ); } package tests::DlfSchemaTestI18N; use base qw/Lire::Test::TestCase tests::TestStoreFixture /; sub new { my $self = shift()->SUPER::new( @_ ); $self->init(); $self->init_i18n(); return $self; } sub set_up { my $self = $_[0]; $self->SUPER::set_up(); $self->set_up_test_schema(); # This has to be done before set_up_locale() otherwise # the textdomain might be wrong (since first load of schema # will set it to a non-existent directory $self->{'schema'} = Lire::DlfSchema::load_schema( "test-extended" ); $self->set_up_locale(); return; } sub tear_down { my $self = $_[0]; $self->SUPER::tear_down(); $self->tear_down_locale(); return; } sub test_title_i18n { my $self = $_[0]; my $schema = $self->{'schema'}; $self->assert_str_equals( 'Simple Extended Schema for Tests', $schema->title() ); $self->set_locale( 'fr_CA.iso8859-1' ); $self->assert_str_equals( "Définition simple d'un schéma étendu pour test", $schema->title() ); } sub test_description_i18n { my $self = $_[0]; my $schema = $self->{'schema'}; local $schema->{'description'} = 'JPEG Files'; $self->assert_str_equals( "JPEG Files", $schema->description() ); $self->set_locale( 'fr_CA.iso8859-1' ); $self->assert_str_equals( "Fichiers JPEG", $schema->description() ); } 1;