0.01003 - fixed has_many cond rels
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / dbixcsl_common_tests.pm
1 package dbixcsl_common_tests;
2
3 use strict;
4 use warnings;
5
6 use Test::More;
7 use DBIx::Class::Schema::Loader;
8 use DBI;
9
10 sub new {
11     my $class = shift;
12
13     my $self;
14
15     if( ref($_[0]) eq 'HASH') {
16        my $args = shift;
17        $self = { (%$args) };
18     }
19     else {
20        $self = { @_ };
21     }
22
23     # Only MySQL uses this
24     $self->{innodb} ||= '';
25     
26     $self->{verbose} = $ENV{TEST_VERBOSE} || 0;
27
28     return bless $self => $class;
29 }
30
31 sub skip_tests {
32     my ($self, $why) = @_;
33
34     plan skip_all => $why;
35 }
36
37 sub run_tests {
38     my $self = shift;
39
40     plan tests => 42;
41
42     $self->create();
43
44     my $schema_class = 'DBIXCSL_Test_' . $self->{vendor} . '::Schema';
45
46     my $debug = ($self->{verbose} > 1) ? 1 : 0;
47
48     my %loader_opts = (
49         dsn                     => $self->{dsn},
50         user                    => $self->{user},
51         password                => $self->{password},
52         constraint              => '^(?:\S+\.)?(?i:loader_test)[0-9]+$',
53         relationships           => 1,
54         additional_classes      => 'TestAdditional',
55         additional_base_classes => 'TestAdditionalBase',
56         left_base_classes       => [ qw/TestLeftBase/ ],
57         debug                   => $debug,
58     );
59
60     $loader_opts{db_schema} = $self->{db_schema} if $self->{db_schema};
61     $loader_opts{drop_db_schema} = $self->{drop_db_schema} if $self->{drop_db_schema};
62
63     eval qq{
64         package $schema_class;
65         use base qw/DBIx::Class::Schema::Loader/;
66
67         __PACKAGE__->load_from_connection(\%loader_opts);
68     };
69     ok(!$@, "Loader initialization") or diag $@;
70
71     my $conn = $schema_class->connect($self->{dsn},$self->{user},$self->{password});
72     my $monikers = $schema_class->loader->monikers;
73     my $classes = $schema_class->loader->classes;
74
75     my $moniker1 = $monikers->{loader_test1};
76     my $class1   = $classes->{loader_test1};
77     my $rsobj1   = $conn->resultset($moniker1);
78
79     my $moniker2 = $monikers->{loader_test2};
80     my $class2   = $classes->{loader_test2};
81     my $rsobj2   = $conn->resultset($moniker2);
82
83     isa_ok( $rsobj1, "DBIx::Class::ResultSet" );
84     isa_ok( $rsobj2, "DBIx::Class::ResultSet" );
85
86     can_ok( $class1, 'test_additional_base' );
87     can_ok( $class1, 'test_additional_base_override' );
88     can_ok( $class1, 'test_additional_base_additional' );
89
90     is( $class1->test_additional_base, "test_additional_base",
91         "Additional Base method" );
92
93     is( $class1->test_additional_base_override, "test_left_base_override",
94         "Left Base overrides Additional Base method" );
95
96     is( $class1->test_additional_base_additional, "test_additional",
97         "Additional Base can use Additional package method" );
98
99     my $obj    = $rsobj1->find(1);
100     is( $obj->id,  1 );
101     is( $obj->dat, "foo" );
102     is( $rsobj2->count, 4 );
103
104     my ($obj2) = $rsobj2->find( dat => 'bbb' );
105     is( $obj2->id, 2 );
106
107     SKIP: {
108         skip $self->{skip_rels}, 29 if $self->{skip_rels};
109
110         my $moniker3 = $monikers->{loader_test3};
111         my $class3   = $classes->{loader_test3};
112         my $rsobj3   = $conn->resultset($moniker3);
113
114         my $moniker4 = $monikers->{loader_test4};
115         my $class4   = $classes->{loader_test4};
116         my $rsobj4   = $conn->resultset($moniker4);
117
118         my $moniker5 = $monikers->{loader_test5};
119         my $class5   = $classes->{loader_test5};
120         my $rsobj5   = $conn->resultset($moniker5);
121
122         my $moniker6 = $monikers->{loader_test6};
123         my $class6   = $classes->{loader_test6};
124         my $rsobj6   = $conn->resultset($moniker6);
125
126         my $moniker7 = $monikers->{loader_test7};
127         my $class7   = $classes->{loader_test7};
128         my $rsobj7   = $conn->resultset($moniker7);
129
130         my $moniker8 = $monikers->{loader_test8};
131         my $class8   = $classes->{loader_test8};
132         my $rsobj8   = $conn->resultset($moniker8);
133
134         my $moniker9 = $monikers->{loader_test9};
135         my $class9   = $classes->{loader_test9};
136         my $rsobj9   = $conn->resultset($moniker9);
137
138         isa_ok( $rsobj3, "DBIx::Class::ResultSet" );
139         isa_ok( $rsobj4, "DBIx::Class::ResultSet" );
140         isa_ok( $rsobj5, "DBIx::Class::ResultSet" );
141         isa_ok( $rsobj6, "DBIx::Class::ResultSet" );
142         isa_ok( $rsobj7, "DBIx::Class::ResultSet" );
143         isa_ok( $rsobj8, "DBIx::Class::ResultSet" );
144         isa_ok( $rsobj9, "DBIx::Class::ResultSet" );
145
146         # basic rel test
147         my $obj4 = $rsobj4->find(123);
148         isa_ok( $obj4->fkid, $class3);
149
150         my $obj3 = $rsobj3->find(1);
151         my $rs_rel4 = $obj3->search_related('loader_test4s');
152         isa_ok( $rs_rel4->first, $class4);
153
154         # fk def in comments should not be parsed
155         my $obj5 = $rsobj5->find( id1 => 1, id2 => 1 );
156         is( ref( $obj5->id2 ), '' );
157
158         # mulit-col fk def
159         my $obj6 = $rsobj6->find(1);
160         isa_ok( $obj6->loader_test2, $class2);
161         isa_ok( $obj6->loader_test5, $class5);
162
163         # fk that references a non-pk key (UNIQUE)
164         my $obj8 = $rsobj8->find(1);
165         isa_ok( $obj8->loader_test7, $class7);
166
167         # from Chisel's tests...
168         SKIP: {
169             if($self->{vendor} =~ /sqlite/i) {
170                 skip 'SQLite cannot do the advanced tests', 8;
171             }
172
173             my $moniker10 = $monikers->{loader_test10};
174             my $class10   = $classes->{loader_test10};
175             my $rsobj10   = $conn->resultset($moniker10);
176
177             my $moniker11 = $monikers->{loader_test11};
178             my $class11   = $classes->{loader_test11};
179             my $rsobj11   = $conn->resultset($moniker11);
180
181             isa_ok( $rsobj10, "DBIx::Class::ResultSet" ); 
182             isa_ok( $rsobj11, "DBIx::Class::ResultSet" );
183
184             my $obj10 = $rsobj10->create({ subject => 'xyzzy' });
185
186             $obj10->update();
187             ok( defined $obj10, '$obj10 is defined' );
188
189             my $obj11 = $rsobj11->create({ loader_test10 => $obj10->id() });
190             $obj11->update();
191             ok( defined $obj11, '$obj11 is defined' );
192
193             eval {
194                 my $obj10_2 = $obj11->loader_test10;
195                 $obj10_2->loader_test11( $obj11->id11() );
196                 $obj10_2->update();
197             };
198             is($@, '', 'No errors after eval{}');
199
200             SKIP: {
201                 skip 'Previous eval block failed', 3
202                     unless ($@ eq '');
203         
204                 my $results = $rsobj10->search({ subject => 'xyzzy' });
205                 is( $results->count(), 1,
206                     'One $rsobj10 returned from search' );
207
208                 my $obj10_3 = $results->first();
209                 isa_ok( $obj10_3, $class10 );
210                 is( $obj10_3->loader_test11()->id(), $obj11->id(),
211                     'found same $rsobj11 object we expected' );
212             }
213         }
214
215         SKIP: {
216             skip 'This vendor cannot do inline relationship definitions', 5
217                 if $self->{no_inline_rels};
218
219             my $moniker12 = $monikers->{loader_test12};
220             my $class12   = $classes->{loader_test12};
221             my $rsobj12   = $conn->resultset($moniker12);
222
223             my $moniker13 = $monikers->{loader_test13};
224             my $class13   = $classes->{loader_test13};
225             my $rsobj13   = $conn->resultset($moniker13);
226
227             isa_ok( $rsobj12, "DBIx::Class::ResultSet" ); 
228             isa_ok( $rsobj13, "DBIx::Class::ResultSet" );
229
230             my $obj13 = $rsobj13->find(1);
231             isa_ok( $obj13->id, $class12 );
232             isa_ok( $obj13->loader_test12, $class12);
233             isa_ok( $obj13->dat, $class12);
234         }
235
236         SKIP: {
237             skip 'This vendor cannot do out-of-line implicit rel defs', 3
238                 if $self->{no_implicit_rels};
239             my $moniker14 = $monikers->{loader_test14};
240             my $class14   = $classes->{loader_test14};
241             my $rsobj14   = $conn->resultset($moniker14);
242
243             my $moniker15 = $monikers->{loader_test15};
244             my $class15   = $classes->{loader_test15};
245             my $rsobj15   = $conn->resultset($moniker15);
246
247             isa_ok( $rsobj14, "DBIx::Class::ResultSet" ); 
248             isa_ok( $rsobj15, "DBIx::Class::ResultSet" );
249
250             my $obj15 = $rsobj15->find(1);
251             isa_ok( $obj15->loader_test14, $class14 );
252         }
253     }
254 }
255
256 sub dbconnect {
257     my ($self, $complain) = @_;
258
259     my $dbh = DBI->connect(
260          $self->{dsn}, $self->{user},
261          $self->{password},
262          {
263              RaiseError => $complain,
264              PrintError => $complain,
265              AutoCommit => 1,
266          }
267     );
268
269     die "Failed to connect to database: $DBI::errstr" if !$dbh;
270
271     return $dbh;
272 }
273
274 sub create {
275     my $self = shift;
276
277     my @statements = (
278         qq{
279             CREATE TABLE loader_test1 (
280                 id $self->{auto_inc_pk},
281                 dat VARCHAR(32)
282             ) $self->{innodb}
283         },
284
285         q{ INSERT INTO loader_test1 (dat) VALUES('foo') },
286         q{ INSERT INTO loader_test1 (dat) VALUES('bar') }, 
287         q{ INSERT INTO loader_test1 (dat) VALUES('baz') }, 
288
289         qq{ 
290             CREATE TABLE loader_test2 (
291                 id $self->{auto_inc_pk},
292                 dat VARCHAR(32)
293             ) $self->{innodb}
294         },
295
296         q{ INSERT INTO loader_test2 (dat) VALUES('aaa') }, 
297         q{ INSERT INTO loader_test2 (dat) VALUES('bbb') }, 
298         q{ INSERT INTO loader_test2 (dat) VALUES('ccc') }, 
299         q{ INSERT INTO loader_test2 (dat) VALUES('ddd') }, 
300     );
301
302     my @statements_reltests = (
303         qq{
304             CREATE TABLE loader_test3 (
305                 id INTEGER NOT NULL PRIMARY KEY,
306                 dat VARCHAR(32)
307             ) $self->{innodb}
308         },
309
310         q{ INSERT INTO loader_test3 (id,dat) VALUES(1,'aaa') }, 
311         q{ INSERT INTO loader_test3 (id,dat) VALUES(2,'bbb') }, 
312         q{ INSERT INTO loader_test3 (id,dat) VALUES(3,'ccc') }, 
313         q{ INSERT INTO loader_test3 (id,dat) VALUES(4,'ddd') }, 
314
315         qq{
316             CREATE TABLE loader_test4 (
317                 id INTEGER NOT NULL PRIMARY KEY,
318                 fkid INTEGER NOT NULL,
319                 dat VARCHAR(32),
320                 FOREIGN KEY (fkid) REFERENCES loader_test3 (id)
321             ) $self->{innodb}
322         },
323
324         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(123,1,'aaa') },
325         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(124,2,'bbb') }, 
326         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(125,3,'ccc') },
327         q{ INSERT INTO loader_test4 (id,fkid,dat) VALUES(126,4,'ddd') },
328
329         qq{
330             CREATE TABLE loader_test5 (
331                 id1 INTEGER NOT NULL,
332                 id2 INTEGER NOT NULL, -- , id2 INTEGER REFERENCES loader_test1,
333                 dat VARCHAR(8),
334                 PRIMARY KEY (id1,id2)
335             ) $self->{innodb}
336         },
337
338         q{ INSERT INTO loader_test5 (id1,id2,dat) VALUES (1,1,'aaa') },
339
340         qq{
341             CREATE TABLE loader_test6 (
342                 id INTEGER NOT NULL PRIMARY KEY,
343                 id2 INTEGER,
344                 loader_test2 INTEGER,
345                 dat VARCHAR(8),
346                 FOREIGN KEY (loader_test2) REFERENCES loader_test2 (id),
347                 FOREIGN KEY (id, id2 ) REFERENCES loader_test5 (id1,id2)
348             ) $self->{innodb}
349         },
350
351         (q{ INSERT INTO loader_test6 (id, id2,loader_test2,dat) } .
352          q{ VALUES (1, 1,1,'aaa') }),
353
354         qq{
355             CREATE TABLE loader_test7 (
356                 id INTEGER NOT NULL PRIMARY KEY,
357                 id2 VARCHAR(8) NOT NULL UNIQUE,
358                 dat VARCHAR(8)
359             ) $self->{innodb}
360         },
361
362         q{ INSERT INTO loader_test7 (id,id2,dat) VALUES (1,'aaa','bbb') },
363
364         qq{
365             CREATE TABLE loader_test8 (
366                 id INTEGER NOT NULL PRIMARY KEY,
367                 loader_test7 VARCHAR(8) NOT NULL,
368                 dat VARCHAR(8),
369                 FOREIGN KEY (loader_test7) REFERENCES loader_test7 (id2)
370             ) $self->{innodb}
371         },
372
373         (q{ INSERT INTO loader_test8 (id,loader_test7,dat) } .
374          q{ VALUES (1,'aaa','bbb') }),
375
376         qq{
377             CREATE TABLE loader_test9 (
378                 loader_test9 VARCHAR(8) NOT NULL
379             ) $self->{innodb}
380         },
381     );
382
383     my @statements_advanced = (
384         qq{
385             CREATE TABLE loader_test10 (
386                 id10 $self->{auto_inc_pk},
387                 subject VARCHAR(8),
388                 loader_test11 INTEGER
389             ) $self->{innodb}
390         },
391
392         qq{
393             CREATE TABLE loader_test11 (
394                 id11 $self->{auto_inc_pk},
395                 message VARCHAR(8) DEFAULT 'foo',
396                 loader_test10 INTEGER,
397                 FOREIGN KEY (loader_test10) REFERENCES loader_test10 (id10)
398             ) $self->{innodb}
399         },
400
401         (q{ ALTER TABLE loader_test10 ADD CONSTRAINT } .
402          q{ loader_test11_fk FOREIGN KEY (loader_test11) } .
403          q{ REFERENCES loader_test11 (id11) }),
404     );
405
406     my @statements_inline_rels = (
407         qq{
408             CREATE TABLE loader_test12 (
409                 id INTEGER NOT NULL PRIMARY KEY,
410                 id2 VARCHAR(8) NOT NULL UNIQUE,
411                 dat VARCHAR(8) NOT NULL UNIQUE
412             ) $self->{innodb}
413         },
414
415         q{ INSERT INTO loader_test12 (id,id2,dat) VALUES (1,'aaa','bbb') },
416
417         qq{
418             CREATE TABLE loader_test13 (
419                 id INTEGER NOT NULL PRIMARY KEY REFERENCES loader_test12,
420                 loader_test12 VARCHAR(8) NOT NULL REFERENCES loader_test12 (id2),
421                 dat VARCHAR(8) REFERENCES loader_test12 (dat)
422             ) $self->{innodb}
423         },
424
425         (q{ INSERT INTO loader_test13 (id,loader_test12,dat) } .
426          q{ VALUES (1,'aaa','bbb') }),
427     );
428
429
430     my @statements_implicit_rels = (
431         qq{
432             CREATE TABLE loader_test14 (
433                 id INTEGER NOT NULL PRIMARY KEY,
434                 dat VARCHAR(8)
435             ) $self->{innodb}
436         },
437  
438         q{ INSERT INTO loader_test14 (id,dat) VALUES (123,'aaa') },
439
440         qq{
441             CREATE TABLE loader_test15 (
442                 id INTEGER NOT NULL PRIMARY KEY,
443                 loader_test14 INTEGER NOT NULL,
444                 FOREIGN KEY (loader_test14) REFERENCES loader_test14
445             ) $self->{innodb}
446         },
447
448         q{ INSERT INTO loader_test15 (id,loader_test14) VALUES (1,123) },
449    );
450
451     $self->drop_tables;
452
453     $self->{created} = 1;
454
455     my $dbh = $self->dbconnect(1);
456
457     # Silence annoying but harmless postgres "NOTICE:  CREATE TABLE..."
458     local $SIG{__WARN__} = sub {
459         my $msg = shift;
460         print STDERR $msg unless $msg =~ m{^NOTICE:\s+CREATE TABLE};
461     };
462
463     $dbh->do($_) for (@statements);
464     unless($self->{skip_rels}) {
465         # hack for now, since DB2 doesn't like inline comments, and we need
466         # to test one for mysql, which works on everyone else...
467         # this all needs to be refactored anyways.
468         if($self->{vendor} =~ /DB2/i) {
469             @statements_reltests = map { s/--.*\n//; $_ } @statements_reltests;
470         }
471         $dbh->do($_) for (@statements_reltests);
472         unless($self->{vendor} =~ /sqlite/i) {
473             $dbh->do($_) for (@statements_advanced);
474         }
475         unless($self->{no_inline_rels}) {
476             $dbh->do($_) for (@statements_inline_rels);
477         }
478         unless($self->{no_implicit_rels}) {
479             $dbh->do($_) for (@statements_implicit_rels);
480         }
481     }
482     $dbh->disconnect();
483 }
484
485 sub drop_tables {
486     my $self = shift;
487
488     return unless $self->{created};
489
490     my @tables = qw/
491         loader_test1
492         loader_test2
493     /;
494
495     my @tables_reltests = qw/
496         loader_test4
497         loader_test3
498         loader_test6
499         loader_test5
500         loader_test8
501         loader_test7
502         loader_test9
503     /;
504
505     my @tables_advanced = qw/
506         loader_test11
507         loader_test10
508     /;
509
510     my @tables_inline_rels = qw/
511         loader_test13
512         loader_test12
513     /;
514
515     my @tables_implicit_rels = qw/
516         loader_test15
517         loader_test14
518     /;
519
520     my $drop_fk_mysql =
521         q{ALTER TABLE loader_test10 DROP FOREIGN KEY loader_test11_fk;};
522
523     my $drop_fk =
524         q{ALTER TABLE loader_test10 DROP CONSTRAINT loader_test11_fk;};
525
526     my $dbh = $self->dbconnect(0);
527
528     unless($self->{skip_rels}) {
529         $dbh->do("DROP TABLE $_") for (@tables_reltests);
530         unless($self->{vendor} =~ /sqlite/i) {
531             if($self->{vendor} =~ /mysql/i) {
532                 $dbh->do($drop_fk_mysql);
533             }
534             else {
535                 $dbh->do($drop_fk);
536             }
537             $dbh->do("DROP TABLE $_") for (@tables_advanced);
538         }
539         unless($self->{no_inline_rels}) {
540             $dbh->do("DROP TABLE $_") for (@tables_inline_rels);
541         }
542         unless($self->{no_implicit_rels}) {
543             $dbh->do("DROP TABLE $_") for (@tables_implicit_rels);
544         }
545     }
546     $dbh->do("DROP TABLE $_") for (@tables);
547     $dbh->disconnect;
548 }
549
550 sub DESTROY { shift->drop_tables; }
551
552 1;