allow creating multiple related columns when updating or creating new records
[catagits/Catalyst-Controller-DBIC-API.git] / t / rest / update.t
1 use strict;
2 use warnings;
3
4 use lib 't/lib';
5
6 my $base = 'http://localhost';
7 my $content_type = [ 'Content-Type', 'application/x-www-form-urlencoded' ];
8
9 use RestTest;
10 use DBICTest;
11 use Test::More;
12 use Test::WWW::Mechanize::Catalyst 'RestTest';
13 use HTTP::Request::Common;
14 use JSON;
15
16 my $json = JSON->new->utf8;
17
18 my $mech = Test::WWW::Mechanize::Catalyst->new;
19 ok( my $schema = DBICTest->init_schema(), 'got schema' );
20
21 my $track         = $schema->resultset('Track')->first;
22 my %original_cols = $track->get_columns;
23
24 my $track_url         = "$base/api/rest/track/";
25 my $track_update_url  = $track_url . $track->id;
26 my $tracks_update_url = $track_url;
27
28 my $artist_url        = "$base/api/rest/artist/";
29 my $artist_update_url = $artist_url . "1";
30
31 # test invalid track id caught
32 {
33     diag 'DBIx::Class warns about a non-numeric id which is ok because we test for that too';
34     foreach my $wrong_id ( 'sdsdsdsd', 3434234 ) {
35         my $incorrect_url = "$base/api/rest/track/" . $wrong_id;
36         my $test_data     = $json->encode( { title => 'value' } );
37         my $req           = POST( $incorrect_url, Content => $test_data );
38         $req->content_type('text/x-json');
39         $mech->request($req);
40
41         cmp_ok( $mech->status, '==', 400,
42             'Attempt with invalid track id caught' );
43
44         my $response = $json->decode( $mech->content );
45         like(
46             $response->{messages}->[0],
47             qr/No object found for id/,
48             'correct message returned'
49         );
50
51         $track->discard_changes;
52         is_deeply(
53             { $track->get_columns },
54             \%original_cols,
55             'no update occurred'
56         );
57     }
58 }
59
60 # validation when no params sent
61 {
62     my $test_data = $json->encode( { wrong_param => 'value' } );
63     my $req = POST( $track_update_url, Content => $test_data );
64     $req->content_type('text/x-json');
65     $mech->request($req);
66
67     cmp_ok( $mech->status, '==', 400, 'Update with no keys causes error' );
68
69     my $response = $json->decode( $mech->content );
70     is_deeply( $response->{messages}, ['No valid keys passed'],
71         'correct message returned' );
72
73     $track->discard_changes;
74     is_deeply(
75         { $track->get_columns },
76         \%original_cols,
77         'no update occurred'
78     );
79 }
80
81 {
82     my $test_data = $json->encode( { title => undef } );
83     my $req = POST( $track_update_url, Content => $test_data );
84     $req->content_type('text/x-json');
85     $mech->request($req);
86     cmp_ok( $mech->status, '==', 200, 'Update with key with no value okay' );
87
88     $track->discard_changes;
89     isnt( $track->title, $original_cols{title}, 'Title changed' );
90     is( $track->title, undef, 'Title changed to undef' );
91 }
92
93 {
94     my $test_data = $json->encode(
95         { title => 'monkey monkey', 'cd' => { year => 2009 } } );
96     my $req = POST( $track_update_url, Content => $test_data );
97     $req->content_type('text/x-json');
98     $mech->request($req);
99
100     cmp_ok( $mech->status, '==', 200, 'Update with key with value okay' );
101
102     $track->discard_changes;
103     is( $track->title, 'monkey monkey', 'Title changed to "monkey monkey"' );
104     is( $track->cd->year, 2009, 'related row updated' );
105 }
106
107 {
108     my $test_data = $json->encode(
109         { name => 'Caterwauler B. McCrae',
110           cds => [
111             {
112                 cdid => 1,
113                 title => 'All the bees are gone',
114                 year => 3030,
115             },
116             {
117                 cdid => 2,
118                 title => 'All the crops are gone',
119                 year => 3031
120             }
121           ]
122         }
123     );
124
125     my $req = POST( $artist_update_url, Content => $test_data );
126     $req->content_type('text/x-json');
127     $mech->request($req);
128
129     cmp_ok( $mech->status, '==', 200, 'Multi-row update returned 200 OK' );
130
131     my $artist = $schema->resultset('Artist')->search({ artistid => 1 });
132     ok ($artist->next->name eq "Caterwauler B. McCrae", "mutliple related row parent record update");
133
134     # make sure the old cds don't exist, it's possible we just inserted the new rows instead of updating them
135     my $old_cds = $artist->search_related('cds', { title => ['Spoonful of bees', 'Forkful of bees'] } )->count;
136     ok ($old_cds == 0, 'update performed update and not create on related rows');
137
138     my @cds = $artist->search_related('cds', { year => ['3030', '3031'] }, { order_by => 'year' })->all;
139     ok (@cds == 2, 'update modified proper number of related rows');
140     ok ($cds[0]->title eq 'All the bees are gone', 'update modified column on related row');
141     ok ($cds[1]->title eq 'All the crops are gone', 'update modified column on second related row');
142 }
143
144 # update related rows using only unique_constraint on CD vs. primary key
145 # update the year on constraint match
146 {
147     my $test_data = $json->encode(
148         { name => 'Caterwauler McCrae',
149           cds => [
150             {
151                 artist => 1,
152                 title => 'All the bees are gone',
153                 year => 3032,
154             },
155             {
156                 artist => 1,
157                 title => 'All the crops are gone',
158                 year => 3032
159             }
160           ]
161         }
162     );
163
164     my $req = POST( $artist_update_url, Content => $test_data );
165     $req->content_type('text/x-json');
166     $mech->request($req);
167
168     cmp_ok( $mech->status, '==', 200, 'Multi-row unique constraint update returned 200 OK' );
169
170     my $artist = $schema->resultset('Artist')->search({ artistid => 1 });
171     ok ($artist->next->name eq "Caterwauler McCrae", "multi-row unique constraint related row parent record updated");
172
173     my $old_cds = $artist->search_related('cds', { year => ['3030', '3031'] }, { order_by => 'year' })->count;
174     ok ( $old_cds == 0, 'multi-row update with unique constraint updated year' );
175
176     my $cds = $artist->search_related('cds', { 'year' => 3032 } )->count;
177     ok ( $cds == 2, 'multi-row update with unique constraint okay' );
178 }
179
180 # bulk_update existing objects
181 {
182
183     # order to get a stable order of rows
184     my $tracks_rs =
185         $schema->resultset('Track')
186         ->search( undef, { order_by => 'trackid', rows => 3 } );
187     my $test_data = $json->encode(
188         {   list => [
189                 map +{ id => $_->id, title => 'Track ' . $_->id },
190                 $tracks_rs->all
191             ]
192         }
193     );
194     my $req = PUT( $tracks_update_url, Content => $test_data );
195     $req->content_type('text/x-json');
196     $mech->request($req);
197     cmp_ok( $mech->status, '==', 200, 'Attempt to update three tracks ok' );
198
199     $tracks_rs->reset;
200     while ( my $track = $tracks_rs->next ) {
201         is( $track->title, 'Track ' . $track->id, 'Title changed' );
202     }
203 }
204
205 # bulk_update nonexisting objects
206 {
207
208     # order to get a stable order of rows
209     my $test_data = $json->encode(
210         {   list => [
211                 map +{ id => $_, title => 'Track ' . $_ },
212                 ( 1000 .. 1002 )
213             ]
214         }
215     );
216     my $req = PUT( $tracks_update_url, Content => $test_data );
217     $req->content_type('text/x-json');
218     $mech->request($req);
219     cmp_ok( $mech->status, '==', 400,
220         'Attempt to update three nonexisting tracks fails' );
221     my $response = $json->decode( $mech->content );
222     is( $response->{success}, JSON::false,
223         'success property returns unquoted false' );
224     like(
225         $response->{messages}->[0],
226         qr/No object found for id/,
227         'correct message returned'
228     );
229 }
230
231 done_testing();