Populate caches for related result sets even if they're empty
[dbsrgits/DBIx-Class.git] / t / 84serialize.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use Test::Exception;
6 use lib qw(t/lib);
7 use DBICTest;
8 use Storable qw(dclone freeze nfreeze thaw);
9 use Scalar::Util qw/refaddr/;
10 use Carp;
11
12 sub ref_ne {
13   my ($refa, $refb) = map { refaddr $_ or croak "$_ is not a reference!" } @_[0,1];
14   cmp_ok (
15     $refa,
16       '!=',
17     $refb,
18     sprintf ('%s (0x%07x != 0x%07x)',
19       $_[2],
20       $refa,
21       $refb,
22     ),
23   );
24 }
25
26 my $schema = DBICTest->init_schema;
27
28 my %stores = (
29     dclone_method           => sub { return $schema->dclone($_[0]) },
30     dclone_func             => sub {
31       local $DBIx::Class::ResultSourceHandle::thaw_schema = $schema;
32       return dclone($_[0])
33     },
34     "freeze/thaw_method"    => sub {
35       my $ice = $schema->freeze($_[0]);
36       return $schema->thaw($ice);
37     },
38     "nfreeze/thaw_func"      => sub {
39       my $ice = freeze($_[0]);
40       local $DBIx::Class::ResultSourceHandle::thaw_schema = $schema;
41       return thaw($ice);
42     },
43
44     "freeze/thaw_func (cdbi legacy)" => sub {
45       # this one is special-cased to leak the $schema all over
46       # the same way as cdbi-compat does
47       DBICTest::Artist->result_source_instance->schema($schema);
48       DBICTest::CD->result_source_instance->schema($schema);
49
50       my $fire = thaw(freeze($_[0]));
51
52       # clean up the mess
53       $_->result_source_instance->schema(undef)
54         for map { $schema->class ($_) } $schema->sources;
55
56       return $fire;
57     },
58
59 );
60
61 if ($ENV{DBICTEST_MEMCACHED}) {
62   if (DBIx::Class::Optional::Dependencies->req_ok_for ('test_memcached')) {
63     my $memcached = Cache::Memcached->new(
64       { servers => [ $ENV{DBICTEST_MEMCACHED} ] }
65     );
66
67     my $key = 'tmp_dbic_84serialize_memcached_test';
68
69     $stores{memcached} = sub {
70       $memcached->set( $key, $_[0], 60 )
71         or die "Unable to insert into $ENV{DBICTEST_MEMCACHED} - is server running?";
72       local $DBIx::Class::ResultSourceHandle::thaw_schema = $schema;
73       return $memcached->get($key);
74     };
75   }
76   else {
77     SKIP: {
78       skip 'Memcached tests need ' . DBIx::Class::Optional::Dependencies->req_missing_for ('test_memcached'), 1;
79     }
80   }
81 }
82 else {
83   SKIP: {
84     skip 'Set $ENV{DBICTEST_MEMCACHED} to run the memcached serialization tests', 1;
85   }
86 }
87
88
89
90 for my $name (keys %stores) {
91
92     my $store = $stores{$name};
93     my $copy;
94
95     my $artist = $schema->resultset('Artist')->find(1);
96
97     lives_ok { $copy = $store->($artist) } "serialize row object lives: $name";
98     ref_ne($copy, $artist, 'Simple row cloned');
99     is_deeply($copy, $artist, "serialize row object works: $name");
100
101     my $cd_rs = $artist->search_related("cds");
102
103     # test that a live result source can be serialized as well
104     is( $cd_rs->count, 3, '3 CDs in database');
105     ok( $cd_rs->next, 'Advance cursor' );
106
107     lives_ok {
108       $copy = $store->($cd_rs);
109
110       ref_ne($copy, $artist, 'Simple row cloned');
111
112       is_deeply (
113         [ $copy->all ],
114         [ $cd_rs->all ],
115         "serialize resultset works: $name",
116       );
117     } "serialize resultset lives: $name";
118
119     # Test that an object with a related_resultset can be serialized.
120     ok $artist->{related_resultsets}, 'has key: related_resultsets';
121
122     lives_ok { $copy = $store->($artist) } "serialize row object with related_resultset lives: $name";
123     for my $key (keys %$artist) {
124         next if $key eq 'related_resultsets';
125         next if $key eq '_inflated_column';
126
127         ref_ne($copy->{$key}, $artist->{$key}, "Simple row internals cloned '$key'")
128           if ref $artist->{$key};
129
130         is_deeply($copy->{$key}, $artist->{$key},
131                   qq[serialize with related_resultset '$key']);
132     }
133
134     lives_ok(
135       sub { $copy->discard_changes }, "Discard changes works: $name"
136     ) or diag $@;
137     is($copy->id, $artist->id, "IDs still match ");
138
139
140     # Test resultsource with cached rows
141     my $query_count;
142     $cd_rs = $cd_rs->search ({}, { cache => 1 });
143
144     my $orig_debug = $schema->storage->debug;
145     $schema->storage->debug(1);
146     $schema->storage->debugcb(sub { $query_count++ } );
147
148     # this will hit the database once and prime the cache
149     my @cds = $cd_rs->all;
150
151     lives_ok {
152       $copy = $store->($cd_rs);
153       ref_ne($copy, $cd_rs, 'Cached resultset cloned');
154       is_deeply (
155         [ $copy->all ],
156         [ $cd_rs->all ],
157         "serialize cached resultset works: $name",
158       );
159
160       is ($copy->count, $cd_rs->count, 'Cached count identical');
161     } "serialize cached resultset lives: $name";
162
163     is ($query_count, 1, 'Only one db query fired');
164
165     $schema->storage->debug($orig_debug);
166     $schema->storage->debugcb(undef);
167 }
168
169 # test schema-less detached thaw
170 {
171   my $artist = $schema->resultset('Artist')->find(1);
172
173   $artist = dclone $artist;
174
175   is( $artist->name, 'Caterwauler McCrae', 'getting column works' );
176
177   ok( $artist->update, 'Non-dirty update noop' );
178
179   ok( $artist->name( 'Beeeeeeees' ), 'setting works' );
180
181   ok( $artist->is_column_changed( 'name' ), 'Column dirtyness works' );
182   ok( $artist->is_changed, 'object dirtyness works' );
183
184   my $rs = $artist->result_source->resultset;
185   $rs->set_cache([ $artist ]);
186
187   is( $rs->count, 1, 'Synthetic resultset count works' );
188
189   my $exc = qr/Unable to perform storage-dependent operations with a detached result source.+use \$schema->thaw/;
190
191   throws_ok { $artist->update }
192     $exc,
193     'Correct exception on row op'
194   ;
195
196   throws_ok { $artist->discard_changes }
197     $exc,
198     'Correct exception on row op'
199   ;
200
201   throws_ok { $rs->find(1) }
202     $exc,
203     'Correct exception on rs op'
204   ;
205 }
206
207 done_testing;