1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
52 package RT::Test::Shredder;
60 RT::Shredder test suite utilities
64 Since RT:Shredder 0.01_03 we have a test suite. You
65 can run tests and see if everything works as expected
66 before you try shredder on your actual data.
67 Tests also help in the development process.
69 The test suite uses SQLite databases to store data in individual files,
70 so you could sun tests on your production servers without risking
71 damage to your production data.
73 You'll want to run the test suite almost every time you install or update
74 the shredder distribution, especialy if you have local customizations of
75 the DB schema and/or RT code.
77 Tests are one thing you can write even if you don't know much perl,
78 but want to learn more about RT's internals. New tests are very welcome.
82 The shredder distribution has several files to help write new tests.
84 t/shredder/utils.pl - this file, utilities
85 t/00skeleton.t - skeleteton .t file for new tests
87 All tests follow this algorithm:
89 require "t/shredder/utils.pl"; # plug in utilities
90 init_db(); # create new tmp RT DB and init RT API
91 # create RT data you want to be always in the RT DB
93 create_savepoint('mysp'); # create DB savepoint
94 # create data you want delete with shredder
96 # run shredder on the objects you've created
98 # check that shredder deletes things you want
99 # this command will compare savepoint DB with current
100 cmp_deeply( dump_current_and_savepoint('mysp'), "current DB equal to savepoint");
101 # then you can create another object and delete it, then check again
103 Savepoints are named and you can create two or more savepoints.
109 $class->SUPER::import(@_);
110 $class->export_to_level(1);
117 =head3 rewrite_rtconfig
119 Call this sub after C<RT::LoadConfig>. It changes the RT config
120 options necessary to switch to a local SQLite database.
124 sub bootstrap_more_config {
128 print $config <<'END';
129 Set($DatabaseType , 'SQLite');
130 Set($DatabaseHost , 'localhost' );
131 Set($DatabaseRTHost , 'localhost' );
132 Set($DatabasePort , '' );
135 print $config "Set(\$DatabaseName, '". $self->db_name ."');\n";
143 Returns the absolute file path to the current DB.
144 It is C<<RT::Test->temp_directory . 'main.db'>>.
148 sub db_name { return File::Spec->catfile((shift)->temp_directory, "main.db") }
150 =head3 connect_sqlite
152 Returns connected DBI DB handle.
154 Takes path to sqlite db.
161 return DBI->connect("dbi:SQLite:dbname=". shift, "", "");
168 Creates and returns a new RT::Shredder object.
176 require RT::Shredder;
177 my $obj = RT::Shredder->new;
179 my $file = File::Spec->catfile( $self->temp_directory, 'dump.XXXX.sql' );
180 $obj->AddDumpPlugin( Arguments => {
191 =head3 savepoint_name
193 Returns the absolute path to the named savepoint DB file.
194 Takes one argument - savepoint name, by default C<sp>.
201 my $name = shift || 'default';
202 return File::Spec->catfile( $self->temp_directory, "sp.$name.db" );
205 =head3 create_savepoint
207 Creates savepoint DB from the current DB.
208 Takes name of the savepoint as argument.
210 =head3 restore_savepoint
212 Restores current DB to savepoint state.
213 Takes name of the savepoint as argument.
217 sub create_savepoint {
219 return $self->__cp_db( $self->db_name => $self->savepoint_name( shift ) );
221 sub restore_savepoint {
223 return $self->__cp_db( $self->savepoint_name( shift ) => $self->db_name );
228 my( $orig, $dest ) = @_;
229 RT::Test::__disconnect_rt();
230 File::Copy::copy( $orig, $dest ) or die "Couldn't copy '$orig' => '$dest': $!";
231 RT::Test::__reconnect_rt();
240 Returns DB dump as a complex hash structure:
249 Takes named argument C<CleanDates>. If true, clean all date fields from
250 dump. True by default.
258 my %args = ( CleanDates => 1, @_ );
260 my $old_fhkn = $dbh->{'FetchHashKeyName'};
261 $dbh->{'FetchHashKeyName'} = 'NAME_lc';
263 my @tables = $RT::Handle->_TableNames( $dbh );
266 foreach my $t( @tables ) {
267 next if lc($t) eq 'sessions';
268 $res->{$t} = $dbh->selectall_hashref(
269 "SELECT * FROM $t". $self->dump_sqlite_exceptions($t), 'id'
271 $self->clean_dates( $res->{$t} ) if $args{'CleanDates'};
272 die $DBI::err if $DBI::err;
275 $dbh->{'FetchHashKeyName'} = $old_fhkn;
279 =head3 dump_sqlite_exceptions
281 If there are parts of the DB which can change from creating and deleting
282 a queue, skip them when doing the comparison. One example is the global
283 queue cache attribute on RT::System which will be updated on Queue creation
284 and can't be rolled back by the shredder. It may actually make sense for
285 Shredder to be updating this at some point in the future.
289 sub dump_sqlite_exceptions {
293 my $special_wheres = {
294 attributes => " WHERE Name != 'QueueCacheNeedsUpdate'"
297 return $special_wheres->{lc $table}||'';
301 =head3 dump_current_and_savepoint
303 Returns dump of the current DB and of the named savepoint.
304 Takes one argument - savepoint name.
308 sub dump_current_and_savepoint
311 my $orig = $self->savepoint_name( shift );
312 die "Couldn't find savepoint file" unless -f $orig && -r _;
313 my $odbh = $self->connect_sqlite( $orig );
314 return ( $self->dump_sqlite( $RT::Handle->dbh, @_ ), $self->dump_sqlite( $odbh, @_ ) );
317 =head3 dump_savepoint_and_current
319 Returns the same data as C<dump_current_and_savepoint> function,
320 but in reversed order.
324 sub dump_savepoint_and_current { return reverse (shift)->dump_current_and_savepoint(@_) }
330 my $date_re = qr/^\d\d\d\d\-\d\d\-\d\d\s*\d\d\:\d\d(\:\d\d)?$/i;
331 foreach my $id ( keys %{ $h } ) {
332 next unless $h->{ $id };
333 foreach ( keys %{ $h->{ $id } } ) {
334 delete $h->{$id}{$_} if $h->{$id}{$_} &&
335 $h->{$id}{$_} =~ /$date_re/;