root/branches/release-40/t/driver-tests.pl @ 2562

Revision 2562, 26.6 kB (checked in by bchoate, 18 months ago)

Test suite cleanup. Use MT::Test to force t/ based configuration file for all tests. Fixed several tests that had incorrect expected values.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl
2
3# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
4# This program is distributed under the terms of the
5# GNU General Public License, version 2.
6#
7# $Id$
8
9use strict;
10use warnings;
11use Data::Dumper;
12use English qw( -no_match_vars );
13
14$OUTPUT_AUTOFLUSH = 1;
15
16# Run this script as a symlink, in the form of 99-driver.t, ie:
17# ln -s driver-tests.pl 99-driver.t
18
19BEGIN {
20    # Set config to driver-test.cfg when run as /path/to/99-driver.t
21    $ENV{MT_CONFIG} = "$1-test.cfg"
22        if __FILE__ =~ m{ [\\/] \d+- ([^\\/]+) \.t \z }xms;
23}
24
25use Test::More;
26use Test::Deep;
27use lib 't/lib';
28
29BEGIN {
30    plan skip_all => "Configuration file $ENV{MT_CONFIG} not found"
31        if !-r "t/$ENV{MT_CONFIG}";
32}
33
34use MT::Test qw(:testdb :time);
35plan tests => 186;
36
37package Zot;
38use base 'MT::Object';
39__PACKAGE__->install_properties({
40    column_defs => {
41        'id' => 'integer not null auto_increment',
42        'x' => 'string(255)',
43    },
44    primary_key => 'id',
45    datasource => 'zot',
46});
47
48package main;
49
50my($foo, @foo, @bar);
51my($tmp, @tmp);
52
53# Test for existing table
54ok(MT::Object->driver->dbd->ddl_class->column_defs('Foo'), "table mt_foo exists after upgrade");
55# Test for non-existent table
56ok(!MT::Object->driver->dbd->ddl_class->column_defs('Zot'), "table mt_zot does not exist after upgrade where undefined");
57
58## Test creating object with new
59##     test column access through column, then through AUTOLOAD
60$foo = Foo->new;
61isa_ok($foo, 'Foo', 'New Foo could be created');
62$foo->column('name', 'foo');
63is($foo->column('name'), 'foo', 'Setting name field with column() persists through access');
64$foo->name('foo');
65is($foo->name, 'foo', 'Setting name field with mutator method persists through access');
66$foo->status(2);
67$foo->text('bar');
68
69## Test saving created object
70ok($foo->save, 'A Foo could be saved');
71is($foo->id, 1, 'First Foo was given an id of 1, says accessor method');
72is($foo->column('id'), 1, 'First Foo was given an id of 1, says column()');
73
74sub _is_object {
75    my ($got, $expected, $name) = @_;
76
77    if (!defined $got) {
78        fail($name);
79        diag('    got undef, not an object');
80        return;
81    }
82
83    if (!$got->isa(ref $expected)) {
84        fail($name);
85        diag('    got a ', ref($got), ' but expected a ', ref $expected);
86        return;
87    }
88
89    if ($got == $expected) {
90        fail($name);
91        diag('    got the exact same instance as expected, when really expected a different but equivalent object');
92        return;
93    }
94
95    # Ignore object columns that have undefined values.
96    my (%got_values, %expected_values);
97    while (my ($field, $value) = each %{ $got->{column_values} }) {
98        $got_values{$field} = $value if defined $value;
99    }
100    while (my ($field, $value) = each %{ $expected->{column_values} }) {
101        $expected_values{$field} = $value if defined $value;
102    }
103
104    if (!eq_deeply(\%got_values, \%expected_values)) {
105        # 'Test' again so the helpful failure diagnostics are output.
106        is_deeply(\%got_values, \%expected_values, $name);
107        return;
108    }
109
110    return 1;
111}
112
113sub is_object {
114    my ($got, $expected, $name) = @_;
115    pass($name) if _is_object(@_);
116}
117
118sub are_objects {
119    my ($got, $expected, $name) = @_;
120
121    my $count = scalar @$expected;
122    if ($count != scalar @$got) {
123        fail($name);
124        diag('    got ', scalar(@$got), ' objects but expected ', $count);
125        return;
126    }
127
128    for my $i (0..$count-1) {
129        return if !_is_object($$got[$i], $$expected[$i], "$name (#$i)");
130    }
131    pass($name);
132}
133
134is_object(scalar Foo->load(1), $foo, 'Foo #1 by id is Foo #1');
135is_object(scalar Foo->load({ id => 1 }), $foo, 'Foo #1 by id hash is Foo #1');
136is_object(scalar Foo->load({ id => 1, name => 'foo' }), $foo, 'Foo #1 by id-name hash is Foo #1');
137is_object(scalar Foo->load({ name => 'foo' }), $foo, 'Foo #1 by name hash is Foo #1');
138is_object(scalar Foo->load({ created_on => $foo->created_on }), $foo, 'Foo #1 by created_on hash is Foo #1');
139is_object(scalar Foo->load({ status => 2 }), $foo, 'Foo #1 by status hash is Foo #1');
140
141##     Change column value, save, try to load using old value (fail?),
142##     then load again using new value
143$foo->status(0);
144ok($foo->save, 'Foo #1 saved with new status (0)');
145$tmp = Foo->load({ status => 2 });
146ok(!$tmp, 'Foo #1 no longer loads with old status (2)');
147$tmp = Foo->load({ status => 0 });
148is_object($tmp, $foo, 'Foo #1 by new status (0) is Foo #1');
149
150## Create a new object so we can do range and last/first lookups.
151## Sleep first so that they get different created_on timestamps.
152sleep(3);
153
154## Create new object for iterator testing
155$foo[0] = $foo;
156$foo[1] = Foo->new;
157$foo[1]->name('baz');
158$foo[1]->text('quux');
159$foo[1]->status(1);
160$foo[1]->save;
161
162## TEST LOADING IN VARIOUS WAYS
163
164## Load all objects via iterator
165my $iter = Foo->load_iter(undef, { sort => 'created_on', direction => 'ascend' });
166isa_ok($iter, 'CODE', "Iterator for all Foos");
167ok($tmp = $iter->(), 'Iterator for our two Foos had one object');
168is_object($tmp, $foo[0], "All Foo iterator's first Foo is Foo #1");
169ok($tmp = $iter->(), 'Iterator for our two Foos had two objects');
170is_object($tmp, $foo[1], "All Foo iterator's second Foo is Foo #2");
171ok(!$iter->(), 'Iterator for our two Foos did not have a third object');
172
173## Load all objects with status == 1 via iterator
174$iter = Foo->load_iter({ status => 1 });
175isa_ok($iter, 'CODE', "Iterator for status=1 Foos");
176ok($tmp = $iter->(), 'Iterator for our status=1 Foos had one object');
177is_object($tmp, $foo[1], "Status=1 Foo iterator's first Foo is Foo #2");
178ok(!$iter->(), "Iterator for our status=1 Foos did not have a second object");
179
180## Load using non-existent ID (should fail)
181$tmp = Foo->load(3);
182ok(!$tmp, 'There is no Foo #3');
183
184## Load using descending sort (newest)
185$tmp = Foo->load(undef, {
186    sort => 'created_on',
187    direction => 'descend',
188    limit => 1 });
189is_object($tmp, $foo[1], 'Newest Foo is Foo #2');
190
191## Load using ascending sort (oldest)
192$tmp = Foo->load(undef, {
193    sort => 'created_on',
194    direction => 'ascend',
195    limit => 1 });
196is_object($tmp, $foo[0], 'Oldest Foo is Foo #1');
197
198## Load using descending sort with limit = 2
199@tmp = Foo->load(undef, {
200    sort => 'created_on',
201    direction => 'descend',
202    limit => 2 });
203are_objects(\@tmp, [ reverse @foo ], 'Two Foos newest-first load() finds Foos #2 and #1');
204
205## Load using descending sort by created_on, no limit
206@tmp = Foo->load(undef, {
207    sort => 'created_on',
208    direction => 'descend' });
209are_objects(\@tmp, [ reverse @foo ], 'All Foos newest-first load() finds Foos #2 and #1');
210
211## Load using ascending sort by status, no limit
212@tmp = Foo->load(undef, { sort => 'status', });
213are_objects(\@tmp, \@foo, 'All Foos lowest-status-first load() finds Foos #1 and #2');
214
215## Load using 'last' where status == 0
216$tmp = Foo->load({ status => 0 }, {
217    sort => 'created_on',
218    direction => 'descend',
219    limit => 1 });
220is_object($tmp, $foo[0], 'Newest status=0 Foo is Foo #1');
221
222## Load using range search, one less than foo[1]->created_on and newer
223$tmp = Foo->load(
224    { created_on => [ $foo[1]->column('created_on')-1 ] },
225    { range => { created_on => 1 } });
226is_object($tmp, $foo[1], 'Foo from open-ended date range before Foo #2 is Foo #2');
227
228## Load using EXCLUSIVE range search, up through the momment $foo[1] created
229$tmp = Foo->load(
230    { created_on => [ $foo[1]->column('created_on')-1, 
231                      $foo[1]->column('created_on') ] },
232    { range => { created_on => 1 } });
233ok(!$tmp, "Exclusive date range load() ending at Foo #1's date found no Foos");
234
235$tmp = Foo->load(
236    { created_on => [ $foo[1]->column('created_on'), 
237                      $foo[1]->column('created_on')+1 ] },
238    { range => { created_on => 1 } });
239ok(!$tmp, "Exclusive date range load() starting at Foo #1's date found no Foos");
240
241## Load using INCLUSIVE range search, up through the momment $foo[1] created
242$tmp = Foo->load(
243    { created_on => [ $foo[1]->column('created_on')-1, 
244                      $foo[1]->column('created_on') ] },
245    { range_incl => { created_on => 1 } });
246ok($tmp, 'Loaded an object based on range_incl (ts-1 to ts)');
247is_object($tmp, $foo[1], "Foo from inclusive date-range load() ending at Foo #1's date is Foo #2");
248
249$tmp = Foo->load(
250    { created_on => [ $foo[1]->column('created_on'), 
251                      $foo[1]->column('created_on')+1 ] },
252    { range_incl => { created_on => 1 } });
253ok($tmp, 'Loaded an object based on range_incl (ts to ts+1)');
254is_object($tmp, $foo[1], "Foo from inclusive date-range load() starting at Foo #1's date is Foo #2");
255
256## Check that range searches return nothing when nothing is in the range.
257$tmp = Foo->load( { created_on => [ undef, '19690101000000' ] },
258                  { range => { created_on => 1 } });
259ok(!$tmp, 'Prehistoric date range load() found no Foos');
260
261## Range search, all items with created_on less than foo[1]->created_on
262$tmp = Foo->load(
263    { created_on => [ undef, $foo[1]->column('created_on')-1 ] },
264    { range => { created_on => 1 } });
265is_object($tmp, $foo[0], "Foo from exclusive open-started date-range load() ending before Foo #1 is Foo #1");
266
267## Get count of objects
268is(Foo->count(), 2, 'Count of all Foos finds both');
269is(Foo->count({ status => 0 }), 1, 'Count of all status=0 Foos finds all one');
270my $ranged_count = Foo->count(
271    { created_on => [ $foo[1]->column('created_on')-1 ] },
272    { range => { created_on => 1 } }
273);
274is($ranged_count, 1, 'Count of all Foos in open-ended date range starting before Foo #1 finds all one');
275
276## Update status for later tests.
277$foo[0]->status(2);
278$foo[0]->save;
279
280## Test start_val loads.
281## Given the first Foo object, should load the "next" one
282## (the one with a larger created_on time)
283$tmp = Foo->load(undef, {
284    limit => 1,
285    sort => 'created_on',
286    direction => 'ascend',
287    start_val => $foo[0]->created_on });
288is_object($tmp, $foo[1], 'Next newer Foo after Foo #1 is Foo #2');
289
290## Given the first Foo object, try to load the "previous" one
291## (the one with a smaller created_on time). This should fail.
292$tmp = Foo->load(undef, {
293    limit => 1,
294    sort => 'created_on',
295    direction => 'descend',
296    start_val => $foo[0]->created_on });
297ok(!$tmp, 'Search for next older Foo before Foo #1 found none');
298
299## Given the second Foo object, try to load the "previous" one
300## (the one with a smaller created_on time). This should work.
301$tmp = Foo->load(undef, {
302    limit => 1,
303    sort => 'created_on',
304    direction => 'descend',
305    start_val => $foo[1]->created_on });
306is_object($tmp, $foo[0], 'Next older Foo before Foo #2 is Foo #1');
307
308## Given the second Foo object, try to load the "next" one
309## (the one with a larger created_on time). This should fail.
310$tmp = Foo->load(undef, {
311    limit => 1,
312    sort => 'created_on',
313    direction => 'ascend',
314    start_val => $foo[1]->created_on });
315ok(!$tmp, 'Search for next newer Foo after Foo #2 found none');
316
317## Now, given the second Foo object's created_on - 1, try to
318## load the "previous" one. This should work.
319$tmp = Foo->load(undef, {
320    limit => 1,
321    sort => 'created_on',
322    direction => 'descend',
323    start_val => $foo[1]->created_on-1 });
324is_object($tmp, $foo[0], 'Next older Foo before just before Foo #2 is Foo #1');
325
326## Now, given the second Foo object's created_on - 1, try to
327## load the "next" one. This should work.
328$tmp = Foo->load(undef, {
329    limit => 1,
330    sort => 'created_on',
331    direction => 'ascend',
332    start_val => $foo[1]->created_on-1 });
333is_object($tmp, $foo[1], 'Next newer Foo after just before Foo #2 is Foo #2');
334
335## Override created_on timestamp, make sure it works
336my $ts = substr($foo[1]->created_on, 0, -4) . '0000';
337$foo[1]->created_on($ts);
338$foo[1]->save;
339
340@tmp = Foo->load(undef, {
341    sort => 'created_on',
342    direction => 'descend',
343    limit => 2 });
344are_objects(\@tmp, \@foo, 'Time-traveled Foos newest-first are Foos #1 and #2');
345
346## Test limit of 2 with direction descend, but without
347## a sort option. This should sort by the most recently-added
348## records, ie. sorted by ID, basically.
349@tmp = Foo->load(undef, {
350    direction => 'descend',
351    limit => 2 });
352are_objects(\@tmp, [ reverse @foo ], 'Foos highest-id-first are Foos #2 and #1');
353
354## Test loading using offset.
355## Should load the second Foo object.
356$tmp = Foo->load(undef, {
357    direction => 'descend',
358    sort => 'created_on',
359    limit => 1,
360    offset => 1 });
361is_object($tmp, $foo[1], 'Second newest Foo is Foo #2');
362
363## We only have 2 Foo objects, so this should load
364## only the second Foo object (because offset is 1).
365@tmp = Foo->load(undef, {
366    direction => 'descend',
367    sort => 'created_on',
368    limit => 2,
369    offset => 1 });
370are_objects(\@tmp, [ $foo[1] ], 'Second and third newest Foos is just Foo #2');
371
372## Should load the first Foo object (ascend with offset of 1).
373$tmp = Foo->load(undef, {
374    direction => 'ascend',
375    sort => 'created_on',
376    limit => 1,
377    offset => 1 });
378is_object($tmp, $foo[0], 'Second oldest Foo is Foo #1');
379
380## Now test join loads.
381## First we need to create a couple of Bar objects.
382$bar[0] = Bar->new;
383$bar[0]->foo_id($foo[1]->id);
384$bar[0]->name('bar0');
385$bar[0]->status(0);
386ok($bar[0]->save, 'saved');
387sleep(2);  ## Sleep to ensure created_on timestamps are unique
388
389$bar[1] = Bar->new;
390$bar[1]->foo_id($foo[1]->id);
391$bar[1]->name('bar1');
392$bar[1]->status(1);
393ok($bar[1]->save, 'saved');
394sleep(2);  ## Sleep to ensure created_on timestamps are unique
395
396$bar[2] = Bar->new;
397$bar[2]->foo_id($foo[0]->id);
398$bar[2]->name('bar2');
399$bar[2]->status(0);
400ok($bar[2]->save, 'saved');
401sleep(2);  ## Sleep to ensure created_on timestamps are unique
402
403# legacy way of specifying sort direction
404my $cgb_iter = Bar->count_group_by({
405        status => '0',
406    }, {
407        group => [ 'foo_id' ],
408        sort => 'foo_id desc',
409    });
410my ($count, $bfid, $month);
411isa_ok($cgb_iter, 'CODE');
412ok(($count, $bfid) = $cgb_iter->(), 'set');
413is($bfid, $bar[1]->id, 'id');
414is($count, 1, 'count4');
415ok(($count, $bfid) = $cgb_iter->(), 'set');
416is($bfid, $bar[0]->id, 'id');
417is($count, 1, 'count5');
418ok(!$cgb_iter->(), 'no $iter');
419
420# new way of specifying sort direction
421my $cgb_iter2 = Bar->count_group_by({
422        status => '0',
423    }, {
424        group => [ 'foo_id' ],
425        sort => 'foo_id',
426        direction => 'descend'
427    });
428
429isa_ok($cgb_iter2, 'CODE');
430ok(($count, $bfid) = $cgb_iter2->(), 'set');
431is($bfid, $bar[1]->id, 'id');
432is($count, 1, 'count4');
433ok(($count, $bfid) = $cgb_iter2->(), 'set');
434is($bfid, $bar[0]->id, 'id');
435is($count, 1, 'count5');
436ok(!$cgb_iter2->(), 'no $iter');
437
438# legacy way of specifying sort direction
439my $cgb_iter3 = Bar->count_group_by(undef, {
440        group => [ 'extract(month from created_on)' ],
441        sort => 'extract(month from created_on) desc',
442    });
443isa_ok($cgb_iter3, 'CODE');
444ok(($count, $month) = $cgb_iter3->(), 'set');
445use POSIX qw(strftime);
446is(int($month), int(strftime("%m", localtime)), 'month');
447is($count, 3, 'count6');
448ok(!$cgb_iter3->(), 'no $iter');
449
450# new way of specifying sort direction
451my $cgb_iter4 = Bar->count_group_by(undef, {
452        group => [ 'extract(month from created_on)' ],
453        sort => [{ column => 'extract(month from created_on)',
454            desc => 'desc' }]
455    });
456isa_ok($cgb_iter4, 'CODE');
457ok(($count, $month) = $cgb_iter4->(), 'set');
458is(int($month), int(strftime("%m", localtime)), 'month');
459is($count, 3, 'count6');
460ok(!$cgb_iter4->(), 'no $iter');
461
462## Get a count of all Foo objects in order of most recently
463## created Bar object. No uniqueness requirement. This tests
464## the on_load_complete temporary table stuff with count.
465
466is(Foo->count(undef,
467    { join => [ 'Bar', 'foo_id',
468                undef,
469                { unique => 1,
470                  sort => 'created_on',
471                  direction => 'descend', } ] }), 2, 'There are 2 unique Foos associated with Bars');
472
473## Now load all Foo objects in order of most recently
474## created Bar object. Make sure they are unique.
475@tmp = Foo->load(undef,
476    { join => [ 'Bar', 'foo_id',
477                undef,
478                { sort => 'created_on',
479                  direction => 'descend',
480                  unique => 1 } ] });
481are_objects(\@tmp, \@foo, 'unique Foos associated with Bars, oldest first');
482
483## Load all Foo objects in order of most recently
484## created Bar object. No uniqueness requirement.
485@tmp = Foo->load(undef,
486    { join => [ 'Bar', 'foo_id',
487                undef,
488                { sort => 'created_on',
489                  direction => 'descend', } ] });
490are_objects(\@tmp, [ @foo, $foo[1] ], 'Foos associated with Bars, oldest first');
491
492## Load last 1 Foo object in order of most recently
493## created Bar object.
494@tmp = Foo->load(undef,
495    { join => [ 'Bar', 'foo_id',
496                undef,
497                { sort => 'created_on',
498                  direction => 'descend',
499                  unique => 1,
500                  limit => 1, } ] });
501are_objects(\@tmp, [ $foo[0] ], 'Foos associated with oldest Bar');
502
503## Load all Foo objects where Bar.name = 'bar0'
504@tmp = Foo->load(undef,
505    { join => [ 'Bar', 'foo_id',
506                { name => 'bar0' },
507                { sort => 'created_on',
508                  direction => 'descend',
509                  unique => 1, } ] });
510are_objects(\@tmp, [ $foo[1] ], 'Foos associated with Bars named bar0');
511
512## foo[1] is older than foo[0] because we overrode the timestamp,
513## so this should load foo[0]
514@tmp = Foo->load(undef,
515    { sort => 'created_on', direction => 'descend', limit => 1,
516    join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
517are_objects(\@tmp, [ $foo[0] ], 'One Foo associated with Bars of status=0');
518
519## This is the same join as the last one, but without the limit--so
520## we should get both Foo objects this time, in descending order.
521@tmp = Foo->load(undef,
522    { sort => 'created_on', direction => 'descend',
523      join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
524are_objects(\@tmp, \@foo, 'All Foos associated with Bars of status=0');
525
526## Filter join results by providing a value for 'status'; only Foo[0]
527## has a 'status' == 2, so only that record should be returned.
528@tmp = Foo->load({ status => 2 },
529    { sort => 'created_on', direction => 'descend',
530      join => [ 'Bar', 'foo_id', { status => 0 }, { unique => 1 } ] });
531are_objects(\@tmp, [ $foo[0] ], 'Foos of status=2 associated with Bars of status=0');
532
533# Join across a column.
534@tmp = Foo->load({},
535    { sort => 'created_on', direction => 'descend',
536      join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 0 }, { unique => 1 } ] });
537are_objects(\@tmp, \@foo, 'Foos loaded by explicit join across columns');
538
539@tmp = Foo->load({ status => 2 },
540    { sort => 'created_on', direction => 'descend',
541      join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 0 }, { unique => 1 } ] });
542are_objects(\@tmp, [ $foo[0] ], 'Foos of status=2 loaded by explicit join across columns');
543
544## TEST EXISTS METHOD
545ok($foo->exists, 'First Foo long saved exists in db');
546$tmp = Foo->new;
547ok(!$tmp->exists, 'New Foo just created does not exist in db');
548$tmp->id(5);
549ok(!$tmp->exists, 'New Foo just created with fake id does not exist in db');
550
551## Change foo[1]->status so that its value is unique (for index)
552$foo[1]->status(5);
553ok($foo[1]->save, 'saved');
554ok(Foo->load({ status => 5 }), 'loaded' );
555
556## Test remove
557ok($foo[1]->remove, 'removed');
558ok(! Foo->load(2), 'not loaded');
559ok(! Foo->load({ status => 5 }), 'not loaded');
560ok(! Foo->load({ name => $foo[1]->name }), 'not loaded');
561ok(! Foo->load({ created_on => $foo[1]->created_on }), 'not loaded');
562
563## Test methods:
564##     * properties
565my $props1 = Foo->properties;
566is($props1->{audit}, 1, 'audit');
567is(scalar keys %{ $props1->{indexes} }, 3, 'indexes');
568is($props1->{primary_key}, 'id', 'id');
569is(scalar @{ $props1->{columns} }, 9, 'columns');
570my $props2 = $foo->properties;
571is($props1, $props2, "$props1 is $props2");  ## Same address, because same hashref
572
573##     * column_names
574my $cols = $foo->column_names;
575isa_ok($cols, 'ARRAY');
576my %cols = map { $_ => 1 } @$cols;
577for (qw(id name status text data created_on created_by modified_on modified_by)) {
578    ok($cols{$_}, 'cols');
579}
580
581##     * column_values
582my $vals = $foo->column_values;
583isa_ok($vals, 'HASH');
584is($vals->{id}, $foo->id, 'id');
585is($vals->{name}, $foo->name, 'name');
586is($vals->{status}, $foo->status, 'status');
587is($vals->{text}, $foo->text, 'text');
588is($vals->{created_on}, $foo->created_on, 'created_on');
589is($vals->{created_by}, $foo->created_by, 'created_by');
590is($vals->{modified_on}, $foo->modified_on, 'modified_on');
591is($vals->{modified_by}, $foo->modified_by, 'modified_by');
592
593##     * set_values
594$vals = {
595    id => 5,
596    name => 'baz',
597    status => 7,
598    text => 'quux',
599    created_on => 13209,
600    created_by => 'bar',
601    #modified_on => 39023, modified_by auto-set modified_on in our new code.
602    modified_by => 'foo',
603};
604$foo->set_values($vals);
605for my $col (keys %$vals) {
606    is($vals->{$col}, $foo->column($col), $col);
607}
608
609##     * binary data
610
611my $binmonster = Foo->new;
612
613$vals = {
614    funky => "yes",
615    monkey => "no",
616};
617
618require MT::Serialize;
619my $srlzr = MT::Serialize->new('MT');
620$binmonster->data($srlzr->serialize(\$vals));
621my $x = $binmonster->save();
622warn 'Failed binary data test: ' . $binmonster->errstr() unless $x;
623ok($x, 'saved');
624ok($binmonster->id, 'id');
625Foo->driver->clear_cache if Foo->driver->can('clear_cache');
626my $chk = Foo->load($binmonster->id);
627if ($chk) {
628    my $chk_data = $chk->data;
629    my $chk_vals = $srlzr->unserialize($chk_data);
630    foreach (keys %$vals) {
631        is($$chk_vals->{$_}, $vals->{$_}, $_);
632    }
633} else {
634    foreach (keys %$vals) {
635        ok(0, $_);
636    }
637}
638
639##     * datasource
640is($foo->table_name, 'mt_' . $foo->datasource, 'datasource');
641
642##     * clone
643my $clone = $foo->clone_all;
644for my $col (@$cols) {
645    is($clone->column($col), $foo->column($col), $col);
646}
647
648## Sleep first so that they get different created_on timestamps.
649sleep(3);
650
651Foo->set_by_key({name => "this"});
652my $obj = Foo->load({name => "this"});
653isa_ok($obj, 'Foo');
654
655Foo->set_by_key({name => "this"}, {status => 42});
656$obj = Foo->load({name => "this"});
657is($obj && $obj->status, 42, 'status');
658
659Foo->set_by_key({name => "this"}, {status => 47});
660$obj = Foo->load({name => "this"});
661is($obj && $obj->status, 47, 'status');
662
663Foo->set_by_key({name => "this", status => 47}, {text => "spiffy"});
664$obj = Foo->load({name => "this", status => 47});
665is($obj && $obj->text, 'spiffy', 'text');
666
667sleep(3);
668
669Foo->set_by_key({name => "that"}, {text => "Once"});
670$obj = Foo->load({name => "that"});
671is($obj && $obj->text, 'Once', 'text');
672
673Foo->driver->clear_cache if Foo->driver->can('clear_cache');
674## Load use direct set of values for non-PK column
675@tmp = Foo->load({ name => [qw(foo this)] });
676@tmp = sort {$a->name cmp $b->name} @tmp;
677is(@tmp, 2, 'array length 2');
678
679is(Foo->count(), 4, 'check number of Foos');
680
681## check offsets without limits
682## Should load the third and fourth Foo objects.
683my $foo4 = Foo->load({name => "this"});
684my $foo5 = Foo->load({name => "that"});
685my $foo1 = Foo->load(undef, { 'sort' => 'created_on', 'direction' => 'ascend' });
686my @offs = Foo->load(undef, {
687    direction => 'ascend',
688    sort => 'created_on',
689    offset => 2 });
690is(@offs, 2, 'array length 2');
691isa_ok($offs[0], 'Foo');
692is($offs[0]->id, $foo4->id, 'id');
693isa_ok($offs[1], 'Foo');
694is($offs[1]->id, $foo5->id, 'id');
695
696## Should load the third and fourth Foo objects.
697@offs = Foo->load(undef, {
698    direction => 'descend',
699    sort => 'created_on',
700    offset => 1 });
701is(@offs, 3, 'array length 3');
702isa_ok($offs[0], 'Foo');
703is($offs[0]->id, $foo4->id, 'id');
704isa_ok($offs[1], 'Foo');
705is($offs[1]->id, $binmonster->id, 'id');
706isa_ok($offs[2], 'Foo');
707is($offs[2]->id, $foo1->id, 'id');
708
709#sum_group_by
710my $cnt = 0;
711my @data = Foo->load(undef, {direction => 'ascend'});
712my @foos;
713foreach my $f (@data) {
714    $f->status($cnt + 1);
715    $f->save;
716    unshift @foos, $f;
717    $cnt++;
718}
719
720my $sgb = Foo->sum_group_by(undef, { sum => 'status', group => ['id'], direction => 'ascend' });
721while (my ($status, $id2) = $sgb->()) {
722    my $f = pop @foos;
723    is($f->id, $id2, 'id sgb');
724    is($f->status, $status, 'status sgb');
725}
726
727SKIP: {
728    skip(1, '$tmp[0] undefined') unless $tmp[0];
729    ok($tmp[0] && ($tmp[0]->name eq 'foo'), 'name')
730}
731SKIP: {
732    skip(1, '$tmp[1] undefined') unless $tmp[1];
733    ok($tmp[1] && ($tmp[1]->name eq 'this'), 'name');
734}
735
736# -or
737my $newdata = Foo->new;
738$newdata->status(11);
739$newdata->name('Apple');
740$newdata->text('MacBook');
741$newdata->save;
742$newdata = Foo->new;
743$newdata->status(12);
744$newdata->name('Linux');
745$newdata->text('Ubuntu');
746$newdata->save;
747$newdata = Foo->new;
748$newdata->status(13);
749$newdata->name('Microsoft');
750$newdata->text('Vista');
751$newdata->save;
752$newdata = Foo->new;
753$newdata->status(10);
754$newdata->name('Microsoft');
755$newdata->text('XP');
756$newdata->save;
757$newdata = Foo->new;
758$newdata->status(10);
759$newdata->name('Apple');
760$newdata->text('iBook');
761$newdata->save;
762
763$count = Foo->count( [{status => 10}, -or => {name => 'Apple'}] );
764# ==> select count(*) from mt_foo where foo_status = 10 or foo_name = 'Apple'
765is($count, 3, '-or count');
766
767$count = Foo->count( [ { status => { '<=' => 20 }, name => 'Apple' }, -and_not => { status => 11 } ] );
768# ==> select count(*) from mt_foo where (foo_status <= 20 and foo_name = 'Apple') and not (foo_status = 11)
769is($count, 1, '-and_not count');
770
771$count = Foo->count( [
772    { status => 10 },
773    -or => { name => 'Apple' },
774    -or => { name => { like => '%nux' } },
775] );
776# ==> select count(*) from mt_foo where (foo_status = 10) or (foo_name = 'Apple') or (foo_name like '%nux')
777# (selects Apple+MacBook, Apple+iBook, Microsoft+XP, Linux+Ubuntu)
778is($count, 4, '-or count, 3 clauses');
779
780# alias support
781my $vista = Foo->load({name=>'Microsoft', status=>13});
782my $newbar = Bar->new;
783$newbar->foo_id($vista->id);
784$newbar->name('Silverlight');
785$newbar->status(2);
786$newbar->save;
787sleep(3);
788$newbar = Bar->new;
789$newbar->foo_id($vista->id);
790$newbar->name('IronPython');
791$newbar->status(3);
792$newbar->save;
793sleep(3);
794my $mb = Foo->load({name=>'Apple', status=>11});
795$newbar = Bar->new;
796$newbar->foo_id($mb->id);
797$newbar->name('IronRuby');
798$newbar->status(0);
799$newbar->save;
800
801# select * from foo, bar bar1, bar bar2
802# where bar1.bar_foo_id = foo_id
803# and bar2.bar_foo_id = bar1.bar_foo_id
804# and bar1.status = 2
805# and bar2.status = 3
806my @a_foos = Foo->load(
807    undef,
808    { join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 2 },
809        { join => [ 'Bar', undef, { foo_id => \'= bar1.bar_foo_id', status => 3 },
810            { alias => 'bar2' } ],
811          alias => 'bar1'
812        }
813      ],
814      sort => 'created_on', direction => 'descend',
815    }
816);
817is(scalar(@a_foos), 1, 'join the same table using alias 1');
818is($a_foos[0]->id, $vista->id, 'join the same table using alias 2');
819
820@a_foos = Foo->load(
821    undef,
822    { join => [ 'Bar', undef, { foo_id => \'= foo_id', status => 2 },
823        { join => [ 'Bar', undef, { foo_id => \'= bar1.bar_foo_id', status => 0 },
824            { alias => 'bar2' } ],
825          alias => 'bar1'
826        }
827      ],
828      sort => 'created_on', direction => 'descend',
829    }
830);
831is(scalar(@a_foos), 0, 'join the same table using alias 3');
832 
833
8341;
Note: See TracBrowser for help on using the browser.