Make sure the taint test does some DB-level ops
[dbsrgits/DBIx-Class.git] / t / 94versioning.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use Test::Warn;
6 use Test::Exception;
7
8 use Path::Class;
9 use File::Copy;
10 use Time::HiRes qw/time sleep/;
11
12 use lib qw(t/lib);
13 use DBICTest;
14 use DBIx::Class::_Util 'sigwarn_silencer';
15
16 my ($dsn, $user, $pass);
17
18 BEGIN {
19   ($dsn, $user, $pass) = @ENV{map { "DBICTEST_MYSQL_${_}" } qw/DSN USER PASS/};
20
21   plan skip_all => 'Set $ENV{DBICTEST_MYSQL_DSN}, _USER and _PASS to run this test'
22     unless ($dsn);
23
24   require DBIx::Class;
25   plan skip_all =>
26       'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('deploy')
27     unless DBIx::Class::Optional::Dependencies->req_ok_for ('deploy');
28
29   plan skip_all =>
30       'Test needs ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_rdbms_mysql')
31     unless DBIx::Class::Optional::Dependencies->req_ok_for ('test_rdbms_mysql');
32 }
33
34 # this is just to grab a lock
35 {
36   my $s = DBICTest::Schema->connect($dsn, $user, $pass);
37 }
38
39 # in case it came from the env
40 $ENV{DBIC_NO_VERSION_CHECK} = 0;
41
42 use_ok('DBICVersion_v1');
43
44 my $version_table_name = 'dbix_class_schema_versions';
45 my $old_table_name = 'SchemaVersions';
46
47 my $ddl_dir = dir(qw/t var/, "versioning_ddl-$$");
48 $ddl_dir->mkpath unless -d $ddl_dir;
49
50 my $fn = {
51     v1 => $ddl_dir->file ('DBICVersion-Schema-1.0-MySQL.sql'),
52     v2 => $ddl_dir->file ('DBICVersion-Schema-2.0-MySQL.sql'),
53     v3 => $ddl_dir->file ('DBICVersion-Schema-3.0-MySQL.sql'),
54     trans_v12 => $ddl_dir->file ('DBICVersion-Schema-1.0-2.0-MySQL.sql'),
55     trans_v23 => $ddl_dir->file ('DBICVersion-Schema-2.0-3.0-MySQL.sql'),
56 };
57
58 my $schema_v1 = DBICVersion::Schema->connect($dsn, $user, $pass, { ignore_version => 1 });
59 eval { $schema_v1->storage->dbh->do('drop table ' . $version_table_name) };
60 eval { $schema_v1->storage->dbh->do('drop table ' . $old_table_name) };
61
62 is($schema_v1->ddl_filename('MySQL', '1.0', $ddl_dir), $fn->{v1}, 'Filename creation working');
63 unlink( $fn->{v1} ) if ( -e $fn->{v1} );
64 $schema_v1->create_ddl_dir('MySQL', undef, $ddl_dir);
65
66 ok(-f $fn->{v1}, 'Created DDL file');
67 $schema_v1->deploy({ add_drop_table => 1 });
68
69 my $tvrs = $schema_v1->{vschema}->resultset('Table');
70 is($schema_v1->_source_exists($tvrs), 1, 'Created schema from DDL file');
71
72 # loading a new module defining a new version of the same table
73 DBICVersion::Schema->_unregister_source ('Table');
74 use_ok('DBICVersion_v2');
75
76 my $schema_v2 = DBICVersion::Schema->connect($dsn, $user, $pass, { ignore_version => 1 });
77 {
78   unlink($fn->{v2});
79   unlink($fn->{trans_v12});
80
81   is($schema_v2->get_db_version(), '1.0', 'get_db_version ok');
82   is($schema_v2->schema_version, '2.0', 'schema version ok');
83   $schema_v2->create_ddl_dir('MySQL', '2.0', $ddl_dir, '1.0');
84   ok(-f $fn->{trans_v12}, 'Created DDL file');
85
86   warnings_like (
87     sub { $schema_v2->upgrade() },
88     qr/DB version .+? is lower than the schema version/,
89     'Warn before upgrade',
90   );
91
92   is($schema_v2->get_db_version(), '2.0', 'db version number upgraded');
93
94   lives_ok ( sub {
95     $schema_v2->storage->dbh->do('select NewVersionName from TestVersion');
96   }, 'new column created' );
97
98   warnings_exist (
99     sub { $schema_v2->create_ddl_dir('MySQL', '2.0', $ddl_dir, '1.0') },
100     [
101       qr/Overwriting existing DDL file - \Q$fn->{v2}\E/,
102       qr/Overwriting existing diff file - \Q$fn->{trans_v12}\E/,
103     ],
104     'An overwrite warning generated for both the DDL and the diff',
105   );
106 }
107
108 {
109   my $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass);
110   lives_ok (sub {
111     $schema_version->storage->dbh->do('select * from ' . $version_table_name);
112   }, 'version table exists');
113
114   lives_ok (sub {
115     $schema_version->storage->dbh->do("DROP TABLE IF EXISTS $old_table_name");
116     $schema_version->storage->dbh->do("RENAME TABLE $version_table_name TO $old_table_name");
117   }, 'versions table renamed to old style table');
118
119   $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass);
120   is($schema_version->get_db_version, '2.0', 'transition from old table name to new okay');
121
122   dies_ok (sub {
123     $schema_version->storage->dbh->do('select * from ' . $old_table_name);
124   }, 'old version table gone');
125
126 }
127
128 # repeat the v1->v2 process for v2->v3 before testing v1->v3
129 DBICVersion::Schema->_unregister_source ('Table');
130 use_ok('DBICVersion_v3');
131
132 my $schema_v3 = DBICVersion::Schema->connect($dsn, $user, $pass, { ignore_version => 1 });
133 {
134   unlink($fn->{v3});
135   unlink($fn->{trans_v23});
136
137   is($schema_v3->get_db_version(), '2.0', 'get_db_version 2.0 ok');
138   is($schema_v3->schema_version, '3.0', 'schema version 3.0 ok');
139   $schema_v3->create_ddl_dir('MySQL', '3.0', $ddl_dir, '2.0');
140   ok(-f $fn->{trans_v23}, 'Created DDL 2.0 -> 3.0 file');
141
142   warnings_exist (
143     sub { $schema_v3->upgrade() },
144     qr/DB version .+? is lower than the schema version/,
145     'Warn before upgrade',
146   );
147
148   is($schema_v3->get_db_version(), '3.0', 'db version number upgraded');
149
150   lives_ok ( sub {
151     $schema_v3->storage->dbh->do('select ExtraColumn from TestVersion');
152   }, 'new column created');
153 }
154
155 # now put the v1 schema back again
156 {
157   # drop all the tables...
158   eval { $schema_v1->storage->dbh->do('drop table ' . $version_table_name) };
159   eval { $schema_v1->storage->dbh->do('drop table ' . $old_table_name) };
160   eval { $schema_v1->storage->dbh->do('drop table TestVersion') };
161
162   {
163     local $DBICVersion::Schema::VERSION = '1.0';
164     $schema_v1->deploy;
165   }
166   is($schema_v1->get_db_version(), '1.0', 'get_db_version 1.0 ok');
167 }
168
169 # attempt v1 -> v3 upgrade
170 {
171   local $SIG{__WARN__} = sigwarn_silencer( qr/Attempting upgrade\.$/ );
172   $schema_v3->upgrade();
173   is($schema_v3->get_db_version(), '3.0', 'db version number upgraded');
174 }
175
176 # Now, try a v1 -> v3 upgrade with a file that has comments strategically placed in it.
177 # First put the v1 schema back again...
178 {
179   # drop all the tables...
180   eval { $schema_v1->storage->dbh->do('drop table ' . $version_table_name) };
181   eval { $schema_v1->storage->dbh->do('drop table ' . $old_table_name) };
182   eval { $schema_v1->storage->dbh->do('drop table TestVersion') };
183
184   {
185     local $DBICVersion::Schema::VERSION = '1.0';
186     $schema_v1->deploy;
187   }
188   is($schema_v1->get_db_version(), '1.0', 'get_db_version 1.0 ok');
189 }
190
191 # add a "harmless" comment before one of the statements.
192 {
193   my ($perl) = $^X =~ /(.+)/;
194   local $ENV{PATH};
195   system( qq($perl -pi.bak -e "s/ALTER/-- this is a comment\nALTER/" $fn->{trans_v23}) );
196 }
197
198 # Then attempt v1 -> v3 upgrade
199 {
200   local $SIG{__WARN__} = sigwarn_silencer( qr/Attempting upgrade\.$/ );
201   $schema_v3->upgrade();
202   is($schema_v3->get_db_version(), '3.0', 'db version number upgraded to 3.0');
203
204   # make sure that the column added after the comment is actually added.
205   lives_ok ( sub {
206     $schema_v3->storage->dbh->do('select ExtraColumn from TestVersion');
207   }, 'new column created');
208 }
209
210
211 # check behaviour of DBIC_NO_VERSION_CHECK env var and ignore_version connect attr
212 {
213   my $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass);
214   eval {
215     $schema_version->storage->dbh->do("DELETE from $version_table_name");
216   };
217
218
219   warnings_like ( sub {
220     $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass);
221   }, qr/Your DB is currently unversioned/, 'warning detected without env var or attr' );
222
223   warnings_like ( sub {
224     $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass, { ignore_version => 1 });
225   },  [], 'warning not detected with attr set');
226
227
228   local $ENV{DBIC_NO_VERSION_CHECK} = 1;
229   warnings_like ( sub {
230     $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass);
231   }, [], 'warning not detected with env var set');
232
233   warnings_like ( sub {
234     $schema_version = DBICVersion::Schema->connect($dsn, $user, $pass, { ignore_version => 0 });
235   }, qr/Your DB is currently unversioned/, 'warning detected without env var or attr');
236 }
237
238 # attempt a deploy/upgrade cycle within one second
239 {
240   eval { $schema_v2->storage->dbh->do('drop table ' . $version_table_name) };
241   eval { $schema_v2->storage->dbh->do('drop table ' . $old_table_name) };
242   eval { $schema_v2->storage->dbh->do('drop table TestVersion') };
243
244   # this attempts to sleep until the turn of the second
245   my $t = time();
246   sleep (int ($t) + 1 - $t);
247   note ('Fast deploy/upgrade start: ', time() );
248
249   {
250     local $DBICVersion::Schema::VERSION = '2.0';
251     $schema_v2->deploy;
252   }
253
254   local $SIG{__WARN__} = sigwarn_silencer( qr/Attempting upgrade\.$/ );
255
256   $schema_v2->upgrade();
257
258   is($schema_v2->get_db_version(), '3.0', 'Fast deploy/upgrade');
259 };
260
261 # Check that it Schema::Versioned deals with new/all forms of connect arguments.
262 {
263   my $get_db_version_run = 0;
264
265   no warnings qw/once redefine/;
266   local *DBIx::Class::Schema::Versioned::get_db_version = sub {
267     $get_db_version_run = 1;
268     return $_[0]->schema_version;
269   };
270
271   # Make sure the env var isn't whats triggering it
272   local $ENV{DBIC_NO_VERSION_CHECK} = 0;
273
274   DBICVersion::Schema->connect({
275     dsn => $dsn,
276     user => $user,
277     pass => $pass,
278     ignore_version => 1
279   });
280
281   ok($get_db_version_run == 0, "attributes pulled from hashref connect_info");
282   $get_db_version_run = 0;
283
284   DBICVersion::Schema->connect( $dsn, $user, $pass, { ignore_version => 1 } );
285   ok($get_db_version_run == 0, "attributes pulled from list connect_info");
286 }
287
288 END {
289   unless ($ENV{DBICTEST_KEEP_VERSIONING_DDL}) {
290     $ddl_dir->rmtree;
291   }
292 }
293
294 done_testing;