Recognise underscores in version markers
[dbsrgits/DBIx-Class-Schema-Loader.git] / t / 23dumpmore.t
1 use strict;
2 use warnings;
3 use Test::More;
4 use DBIx::Class::Schema::Loader::Utils qw/slurp_file write_file/;
5 use namespace::clean;
6 use File::Temp ();
7 use lib qw(t/lib);
8 use dbixcsl_dumper_tests;
9 my $t = 'dbixcsl_dumper_tests';
10
11 $t->cleanup;
12
13 # test loading external content
14 $t->dump_test(
15     classname => 'DBICTest::Schema::_no_skip_load_external',
16     regexes => {
17         Foo => [
18             qr/package DBICTest::Schema::_no_skip_load_external::Foo;.*\nour \$skip_me = "bad mojo";\n1;/s
19         ],
20     },
21 );
22
23 # test skipping external content
24 $t->dump_test(
25     classname => 'DBICTest::Schema::_skip_load_external',
26     options => {
27         skip_load_external => 1,
28     },
29     neg_regexes => {
30         Foo => [
31             qr/package DBICTest::Schema::_skip_load_external::Foo;.*\nour \$skip_me = "bad mojo";\n1;/s
32         ],
33     },
34 );
35
36 $t->cleanup;
37 # test config_file
38 {
39     my $config_file = File::Temp->new (UNLINK => 1);
40
41     print $config_file "{ skip_relationships => 1 }\n";
42     close $config_file;
43
44     $t->dump_test(
45         classname => 'DBICTest::Schema::_config_file',
46         options => { config_file => "$config_file" },
47         neg_regexes => {
48             Foo => [
49                 qr/has_many/,
50             ],
51         },
52     );
53 }
54
55 # proper exception
56 $t->dump_test(
57     classname => 'DBICTest::Schema::_clashing_monikers',
58     test_db_class => 'make_dbictest_db_clashing_monikers',
59     error => qr/tables (?:"bar", "bars"|"bars", "bar") reduced to the same source moniker 'Bar'/,
60 );
61
62
63 $t->cleanup;
64
65 # test naming => { column_accessors => 'preserve' }
66 # also test POD for unique constraint
67 $t->dump_test(
68     classname => 'DBICTest::Schema::_preserve_column_accessors',
69     test_db_class => 'make_dbictest_db_with_unique',
70     options => { naming => { column_accessors => 'preserve' } },
71     neg_regexes => {
72         RouteChange => [
73             qr/\baccessor\b/,
74         ],
75     },
76     regexes => {
77         Baz => [
78             qr/\n\n=head1 UNIQUE CONSTRAINTS\n\n=head2 C<baz_num_unique>\n\n=over 4\n\n=item \* L<\/baz_num>\n\n=back\n\n=cut\n\n__PACKAGE__->add_unique_constraint\("baz_num_unique"\, \["baz_num"\]\);\n\n/,
79         ],
80     }
81 );
82
83 $t->cleanup;
84
85 # test that rels are sorted
86 $t->dump_test(
87     classname => 'DBICTest::Schema::_sorted_rels',
88     test_db_class => 'make_dbictest_db_with_unique',
89     regexes => {
90         Baz => [
91             qr/->might_have\(\n  "quux".*->belongs_to\(\n  "station_visited"/s,
92         ],
93     }
94 );
95
96 $t->cleanup;
97
98 $t->dump_test(
99     classname => 'DBICTest::Schema::_sorted_uniqs',
100     test_db_class => 'make_dbictest_db_multi_unique',
101     regexes => {
102         Bar => [
103             qr/->add_unique_constraint\("uniq1_unique".*->add_unique_constraint\("uniq2_unique"/s,
104         ],
105     },
106 );
107
108 $t->cleanup;
109
110 # test naming => { monikers => 'plural' }
111 $t->dump_test(
112     classname => 'DBICTest::Schema::_plural_monikers',
113     options => { naming => { monikers => 'plural' } },
114     regexes => {
115         Foos => [
116             qr/\n=head1 NAME\n\nDBICTest::Schema::_plural_monikers::Foos\n\n=cut\n\n/,
117         ],
118         Bars => [
119             qr/\n=head1 NAME\n\nDBICTest::Schema::_plural_monikers::Bars\n\n=cut\n\n/,
120         ],
121     },
122 );
123
124 $t->cleanup;
125
126 # test naming => { monikers => 'singular' }
127 $t->dump_test(
128     classname => 'DBICTest::Schema::_singular_monikers',
129     test_db_class => 'make_dbictest_db_plural_tables',
130     options => { naming => { monikers => 'singular' } },
131     regexes => {
132         Foo => [
133             qr/\n=head1 NAME\n\nDBICTest::Schema::_singular_monikers::Foo\n\n=cut\n\n/,
134         ],
135         Bar => [
136             qr/\n=head1 NAME\n\nDBICTest::Schema::_singular_monikers::Bar\n\n=cut\n\n/,
137         ],
138     },
139 );
140
141 $t->cleanup;
142
143 # test naming => { monikers => 'preserve' }
144 $t->dump_test(
145     classname => 'DBICTest::Schema::_preserve_monikers',
146     test_db_class => 'make_dbictest_db_plural_tables',
147     options => { naming => { monikers => 'preserve' } },
148     regexes => {
149         Foos => [
150             qr/\n=head1 NAME\n\nDBICTest::Schema::_preserve_monikers::Foos\n\n=cut\n\n/,
151         ],
152         Bars => [
153             qr/\n=head1 NAME\n\nDBICTest::Schema::_preserve_monikers::Bars\n\n=cut\n\n/,
154         ],
155     },
156 );
157
158 $t->cleanup;
159
160 # test out the POD and "use utf8;"
161 $t->dump_test(
162     classname => 'DBICTest::DumpMore::1',
163     options => {
164         custom_column_info => sub {
165             my ($table, $col, $info) = @_;
166             return +{ extra => { is_footext => 1 } } if $col eq 'footext';
167         },
168         result_base_class => 'My::ResultBaseClass',
169         additional_classes => 'TestAdditional',
170         additional_base_classes => 'TestAdditionalBase',
171         left_base_classes => 'TestLeftBase',
172         components => [ 'TestComponent', '+TestComponentFQN' ],
173     },
174     regexes => {
175         schema => [
176             qr/^use utf8;\n/,
177             qr/package DBICTest::DumpMore::1;/,
178             qr/->load_classes/,
179         ],
180         Foo => [
181             qr/^use utf8;\n/,
182             qr/package DBICTest::DumpMore::1::Foo;/,
183             qr/\n=head1 NAME\n\nDBICTest::DumpMore::1::Foo\n\n=cut\n\nuse strict;\nuse warnings;\n\n/,
184             qr/\n=head1 BASE CLASS: L<My::ResultBaseClass>\n\n=cut\n\nuse base 'My::ResultBaseClass';\n\n/,
185             qr/\n=head1 ADDITIONAL CLASSES USED\n\n=over 4\n\n=item \* L<TestAdditional>\n\n=back\n\n=cut\n\n/,
186             qr/\n=head1 ADDITIONAL BASE CLASSES\n\n=over 4\n\n=item \* L<TestAdditionalBase>\n\n=back\n\n=cut\n\n/,
187             qr/\n=head1 LEFT BASE CLASSES\n\n=over 4\n\n=item \* L<TestLeftBase>\n\n=back\n\n=cut\n\n/,
188             qr/\n=head1 COMPONENTS LOADED\n\n=over 4\n\n=item \* L<DBIx::Class::TestComponent>\n\n=item \* L<TestComponentFQN>\n\n=back\n\n=cut\n\n/,
189             qr/\n=head1 TABLE: C<foo>\n\n=cut\n\n__PACKAGE__->table\("foo"\);\n\n/,
190             qr/\n=head1 ACCESSORS\n\n/,
191             qr/\n=head2 fooid\n\n  data_type: 'integer'\n  is_auto_increment: 1\n  is_nullable: 0\n\n/,
192             qr/\n=head2 footext\n\n  data_type: 'text'\n  default_value: 'footext'\n  extra: \{is_footext => 1\}\n  is_nullable: 1\n\n/,
193             qr/\n=head1 PRIMARY KEY\n\n=over 4\n\n=item \* L<\/fooid>\n\n=back\n\n=cut\n\n__PACKAGE__->set_primary_key\("fooid"\);\n/,
194             qr/\n=head1 RELATIONS\n\n/,
195             qr/\n=head2 bars\n\nType: has_many\n\nRelated object: L<DBICTest::DumpMore::1::Bar>\n\n=cut\n\n/,
196             qr/1;\n$/,
197         ],
198         Bar => [
199             qr/^use utf8;\n/,
200             qr/package DBICTest::DumpMore::1::Bar;/,
201             qr/\n=head1 NAME\n\nDBICTest::DumpMore::1::Bar\n\n=cut\n\nuse strict;\nuse warnings;\n\n/,
202             qr/\n=head1 BASE CLASS: L<My::ResultBaseClass>\n\n=cut\n\nuse base 'My::ResultBaseClass';\n\n/,
203             qr/\n=head1 ADDITIONAL CLASSES USED\n\n=over 4\n\n=item \* L<TestAdditional>\n\n=back\n\n=cut\n\n/,
204             qr/\n=head1 ADDITIONAL BASE CLASSES\n\n=over 4\n\n=item \* L<TestAdditionalBase>\n\n=back\n\n=cut\n\n/,
205             qr/\n=head1 LEFT BASE CLASSES\n\n=over 4\n\n=item \* L<TestLeftBase>\n\n=back\n\n=cut\n\n/,
206             qr/\n=head1 COMPONENTS LOADED\n\n=over 4\n\n=item \* L<DBIx::Class::TestComponent>\n\n=item \* L<TestComponentFQN>\n\n=back\n\n=cut\n\n/,
207             qr/\n=head1 TABLE: C<bar>\n\n=cut\n\n__PACKAGE__->table\("bar"\);\n\n/,
208             qr/\n=head1 ACCESSORS\n\n/,
209             qr/\n=head2 barid\n\n  data_type: 'integer'\n  is_auto_increment: 1\n  is_nullable: 0\n\n/,
210             qr/\n=head2 fooref\n\n  data_type: 'integer'\n  is_foreign_key: 1\n  is_nullable: 1\n\n/,
211             qr/\n=head1 PRIMARY KEY\n\n=over 4\n\n=item \* L<\/barid>\n\n=back\n\n=cut\n\n__PACKAGE__->set_primary_key\("barid"\);\n/,
212             qr/\n=head1 RELATIONS\n\n/,
213             qr/\n=head2 fooref\n\nType: belongs_to\n\nRelated object: L<DBICTest::DumpMore::1::Foo>\n\n=cut\n\n/,
214             qr/\n1;\n$/,
215         ],
216     },
217 );
218
219 $t->append_to_class('DBICTest::DumpMore::1::Foo',q{# XXX This is my custom content XXX});
220
221
222 $t->dump_test(
223     classname => 'DBICTest::DumpMore::1',
224     regexes => {
225         schema => [
226             qr/package DBICTest::DumpMore::1;/,
227             qr/->load_classes/,
228         ],
229         Foo => [
230             qr/package DBICTest::DumpMore::1::Foo;/,
231             qr/->set_primary_key/,
232             qr/1;\n# XXX This is my custom content XXX/,
233         ],
234         Bar => [
235             qr/package DBICTest::DumpMore::1::Bar;/,
236             qr/->set_primary_key/,
237             qr/1;\n$/,
238         ],
239     },
240 );
241
242
243 $t->dump_test(
244     classname => 'DBICTest::DumpMore::1',
245     options => {
246         really_erase_my_files => 1
247     },
248     regexes => {
249         schema => [
250             qr/package DBICTest::DumpMore::1;/,
251             qr/->load_classes/,
252         ],
253         Foo => [
254             qr/package DBICTest::DumpMore::1::Foo;/,
255             qr/->set_primary_key/,
256             qr/1;\n$/,
257         ],
258         Bar => [
259             qr/package DBICTest::DumpMore::1::Bar;/,
260             qr/->set_primary_key/,
261             qr/1;\n$/,
262         ],
263     },
264     neg_regexes => {
265         Foo => [
266             qr/# XXX This is my custom content XXX/,
267         ],
268     },
269 );
270
271
272 $t->cleanup;
273
274 # test namespaces
275 $t->dump_test(
276     classname => 'DBICTest::DumpMore::1',
277     options => {
278         use_namespaces => 1,
279         generate_pod => 0
280     },
281     neg_regexes => {
282         'Result/Foo' => [
283             qr/^=/m,
284         ],
285     },
286 );
287
288
289 $t->dump_test(
290     classname => 'DBICTest::DumpMore::1',
291     options => {
292         db_schema => 'foo_schema',
293         qualify_objects => 1,
294         use_namespaces => 1
295     },
296     warnings => [
297         qr/^db_schema is not supported on SQLite/,
298     ],
299     regexes => {
300         'Result/Foo' => [
301             qr/^\Q__PACKAGE__->table("foo_schema.foo");\E/m,
302             # the has_many relname should not have the schema in it!
303             qr/^__PACKAGE__->has_many\(\n  "bars"/m,
304         ],
305     },
306 );
307
308 # test qualify_objects
309 $t->dump_test(
310     classname => 'DBICTest::DumpMore::1',
311     options => {
312         db_schema => [ 'foo_schema', 'bar_schema' ],
313         qualify_objects => 0,
314         use_namespaces => 1,
315     },
316     warnings => [
317         qr/^db_schema is not supported on SQLite/,
318     ],
319     regexes => {
320         'Result/Foo' => [
321             # the table name should not include the db schema
322             qr/^\Q__PACKAGE__->table("foo");\E/m,
323         ],
324         'Result/Bar' => [
325             # the table name should not include the db schema
326             qr/^\Q__PACKAGE__->table("bar");\E/m,
327         ],
328     },
329 );
330
331 # test moniker_parts
332 $t->dump_test(
333     classname => 'DBICTest::DumpMore::1',
334     options => {
335         db_schema => 'my_schema',
336         moniker_parts => ['_schema', 'name'],
337         qualify_objects => 1,
338         use_namespaces => 1,
339     },
340     warnings => [
341         qr/^db_schema is not supported on SQLite/,
342     ],
343     regexes => {
344         'Result/MySchemaFoo' => [
345             qr/^\Q__PACKAGE__->table("my_schema.foo");\E/m,
346             # the has_many relname should not have the schema in it, but the class should
347             qr/^__PACKAGE__->has_many\(\n  "bars",\n  "DBICTest::DumpMore::1::Result::MySchemaBar"/m,
348         ],
349     },
350 );
351
352 # test moniker_part_separator
353 $t->dump_test(
354     classname => 'DBICTest::DumpMore::1',
355     options => {
356         db_schema => 'my_schema',
357         moniker_parts => ['_schema', 'name'],
358         moniker_part_separator => '::',
359         qualify_objects => 1,
360         use_namespaces => 1,
361     },
362     warnings => [
363         qr/^db_schema is not supported on SQLite/,
364     ],
365     regexes => {
366         'Result/MySchema/Foo' => [
367             qr/^package DBICTest::DumpMore::1::Result::MySchema::Foo;/m,
368             qr/^\Q__PACKAGE__->table("my_schema.foo");\E/m,
369             # the has_many relname should not have the schema in it, but the class should
370             qr/^__PACKAGE__->has_many\(\n  "bars",\n  "DBICTest::DumpMore::1::Result::MySchema::Bar"/m,
371         ],
372     },
373 );
374
375 # test moniker_part_separator + moniker_map + recursive constraints
376 $t->dump_test(
377     classname => 'DBICTest::DumpMore::1',
378     options => {
379         db_schema => 'my_schema',
380         moniker_parts => ['_schema', 'name'],
381         moniker_part_separator => '::',
382         qualify_objects => 1,
383         use_namespaces => 1,
384         moniker_map => {
385             my_schema => { foo => "MySchema::Floop" },
386         },
387         constraint => [ [ qr/my_schema/ => qr/foo|bar/ ] ],
388         exclude => [ [ qr/my_schema/ => qr/bar/ ] ],
389     },
390     generated_results => [qw(MySchema::Floop)],
391     warnings => [
392         qr/^db_schema is not supported on SQLite/,
393     ],
394     regexes => {
395         'Result/MySchema/Floop' => [
396             qr/^package DBICTest::DumpMore::1::Result::MySchema::Floop;/m,
397             qr/^\Q__PACKAGE__->table("my_schema.foo");\E/m,
398         ],
399     },
400     neg_regexes => {
401         'Result/MySchema/Floop' => [
402             # the bar table should not be loaded, so no relationship should exist
403             qr/^__PACKAGE__->has_many\(\n  "bars"/m,
404         ],
405     },
406 );
407
408 # test moniker_map + moniker_part_map
409 $t->dump_test(
410     classname => 'DBICTest::DumpMore::1',
411     options => {
412         db_schema => 'my_schema',
413         moniker_parts => ['_schema', 'name'],
414         moniker_part_separator => '::',
415         moniker_part_map => {
416             _schema => {
417                 my_schema => 'OtherSchema',
418             },
419         },
420         moniker_map => {
421             my_schema => {
422                 foo => 'MySchema::Floop',
423             },
424         },
425         qualify_objects => 1,
426         use_namespaces => 1,
427     },
428     warnings => [
429         qr/^db_schema is not supported on SQLite/,
430     ],
431     regexes => {
432         'Result/MySchema/Floop' => [
433             qr/^package DBICTest::DumpMore::1::Result::MySchema::Floop;/m,
434             qr/^\Q__PACKAGE__->table("my_schema.foo");\E/m,
435             # the has_many relname should not have the schema in it, but the class should
436             qr/^__PACKAGE__->has_many\(\n  "bars",\n  "DBICTest::DumpMore::1::Result::OtherSchema::Bar"/m,
437         ],
438         'Result/OtherSchema/Bar' => [
439             qr/^package DBICTest::DumpMore::1::Result::OtherSchema::Bar;/m,
440             qr/^\Q__PACKAGE__->table("my_schema.bar");\E/m,
441             # the has_many relname should not have the schema in it, but the class should
442             qr/^__PACKAGE__->belongs_to\(\n  "fooref",\n  "DBICTest::DumpMore::1::Result::MySchema::Floop"/m,
443         ],
444
445     },
446 );
447
448
449
450 $t->dump_test(
451     classname => 'DBICTest::DumpMore::1',
452     options => {
453         use_namespaces => 1
454     },
455     regexes => {
456         schema => [
457             qr/package DBICTest::DumpMore::1;/,
458             qr/->load_namespaces/,
459         ],
460         'Result/Foo' => [
461             qr/package DBICTest::DumpMore::1::Result::Foo;/,
462             qr/->set_primary_key/,
463             qr/1;\n$/,
464         ],
465         'Result/Bar' => [
466             qr/package DBICTest::DumpMore::1::Result::Bar;/,
467             qr/->set_primary_key/,
468             qr/1;\n$/,
469         ],
470     },
471 );
472
473
474 $t->dump_test(
475     classname => 'DBICTest::DumpMore::1',
476     options => {
477         use_namespaces => 1,
478         result_namespace => 'Res',
479         resultset_namespace => 'RSet',
480         default_resultset_class => 'RSetBase',
481     },
482     regexes => {
483         schema => [
484             qr/package DBICTest::DumpMore::1;/,
485             qr/->load_namespaces/,
486             qr/result_namespace => "Res"/,
487             qr/resultset_namespace => "RSet"/,
488             qr/default_resultset_class => "RSetBase"/,
489         ],
490         'Res/Foo' => [
491             qr/package DBICTest::DumpMore::1::Res::Foo;/,
492             qr/->set_primary_key/,
493             qr/1;\n$/,
494         ],
495         'Res/Bar' => [
496             qr/package DBICTest::DumpMore::1::Res::Bar;/,
497             qr/->set_primary_key/,
498             qr/1;\n$/,
499         ],
500     },
501 );
502
503
504 $t->dump_test(
505     classname => 'DBICTest::DumpMore::1',
506     options => {
507         use_namespaces => 1,
508         result_namespace => '+DBICTest::DumpMore::1::Res',
509         resultset_namespace => 'RSet',
510         default_resultset_class => 'RSetBase',
511         result_base_class => 'My::ResultBaseClass',
512         schema_base_class => 'My::SchemaBaseClass',
513     },
514     regexes => {
515         schema => [
516             qr/package DBICTest::DumpMore::1;/,
517             qr/->load_namespaces/,
518             qr/result_namespace => "\+DBICTest::DumpMore::1::Res"/,
519             qr/resultset_namespace => "RSet"/,
520             qr/default_resultset_class => "RSetBase"/,
521             qr/use base 'My::SchemaBaseClass'/,
522         ],
523         'Res/Foo' => [
524             qr/package DBICTest::DumpMore::1::Res::Foo;/,
525             qr/use base 'My::ResultBaseClass'/,
526             qr/->set_primary_key/,
527             qr/1;\n$/,
528         ],
529         'Res/Bar' => [
530             qr/package DBICTest::DumpMore::1::Res::Bar;/,
531             qr/use base 'My::ResultBaseClass'/,
532             qr/->set_primary_key/,
533             qr/1;\n$/,
534         ],
535     },
536 );
537
538 $t->dump_test(
539     classname => 'DBICTest::DumpMore::1',
540     options => {
541         use_namespaces    => 1,
542         result_base_class => 'My::MissingResultBaseClass',
543     },
544     error => qr/My::MissingResultBaseClass.*is not installed/,
545 );
546
547 # test quote_char in connect_info for dbicdump
548 $t->dump_test(
549     classname => 'DBICTest::DumpMore::1',
550     extra_connect_info => [
551         '',
552         '',
553         { quote_char => '"' },
554     ],
555 );
556
557 # test fix for RT#70507 (end comment and 1; gets lost if left with actual
558 # custom content)
559
560 $t->dump_test(
561     classname => 'DBICTest::DumpMore::Upgrade',
562     options => {
563         use_namespaces => 0,
564     },
565 );
566
567 my $file = $t->class_file('DBICTest::DumpMore::Upgrade::Foo');
568
569 my $code = slurp_file $file;
570
571 $code =~ s/(?=# You can replace)/sub custom_method { 'custom_method works' }\n0;\n\n/;
572
573 write_file $file, $code;
574
575 $t->dump_test(
576     classname => 'DBICTest::DumpMore::Upgrade',
577     options => {
578         use_namespaces => 1,
579     },
580     generated_results => [qw(Foo Bar)],
581     regexes => {
582         'Result/Foo' => [
583             qr/sub custom_method \{ 'custom_method works' \}\n0;\n\n# You can replace.*\n1;\n\z/,
584         ],
585     },
586 );
587
588 # test dry-run mode
589 $t->dump_test(
590     classname => 'DBICTest::DumpMore::DryRun',
591     options => {
592         dry_run => 1,
593     },
594     generated_results => [qw(Foo Bar)],
595 );
596
597 my $schema_file = $t->class_file('DBICTest::DumpMore::DryRun');
598 ok( !-e $schema_file, "dry-run doesn't create file for schema class" );
599 (my $schema_dir = $schema_file) =~ s/\.pm\z//;
600 ok( !-e $schema_dir, "dry-run doesn't create subdirectory for schema namespace" );
601
602 # test omit_version (RT#92300)
603 $t->dump_test(
604     classname => 'DBICTest::DumpMore::omit_version',
605     options => {
606         omit_version => 1,
607     },
608     regexes => {
609         Foo => [
610             qr/^\# Created by DBIx::Class::Schema::Loader @ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/m,
611         ],
612     },
613 );
614
615 # test omit_timestamp (RT#92300)
616 $t->dump_test(
617     classname => 'DBICTest::DumpMore::omit_timestamp',
618     options => {
619         omit_timestamp => 1,
620     },
621     regexes => {
622         Foo => [
623             qr/^\# Created by DBIx::Class::Schema::Loader v[\d._]+$/m,
624         ],
625     },
626 );
627
628 # test omit_version and omit_timestamp simultaneously (RT#92300)
629 $t->dump_test(
630     classname => 'DBICTest::DumpMore::omit_both',
631     options => {
632         omit_version => 1,
633         omit_timestamp => 1,
634     },
635     # A positive regex here would match the top comment
636     neg_regexes => {
637         Foo => [
638             qr/^\# Created by DBIx::Class::Schema::Loader.+$/m,
639         ],
640     },
641 );
642
643 done_testing;
644 # vim:et sts=4 sw=4 tw=0: