default to a session cookie instead of setting an explicit timeout, weird timezone...
[freeside.git] / rt / t / web / command_line.t
1 use strict;
2 use warnings;
3 use File::Spec ();
4 use Test::Expect;
5 use RT::Test tests => undef, actual_server => 1;
6 my ($baseurl, $m) = RT::Test->started_ok;
7
8 use RT::User;
9 use RT::Queue;
10
11 my $rt_tool_path = "$RT::BinPath/rt";
12
13
14 # config directives:
15 #    (in $CWD/.rtrc)
16 #    - server <URL>          URL to RT server.
17 #    - user <username>       RT username.
18 #    - passwd <passwd>       RT user's password.
19 #    - query <RT Query>      Default RT Query for list action
20 #    - orderby <order>       Default RT order for list action
21 #
22 #    Blank and #-commented lines are ignored.
23
24 # environment variables
25 #    The following environment variables override any corresponding
26 #    values defined in configuration files:
27 #
28 #    - RTUSER
29 $ENV{'RTUSER'} = 'root';
30 #    - RTPASSWD
31 $ENV{'RTPASSWD'} = 'password';
32 #    - RTSERVER
33 $RT::Logger->debug("Connecting to server at ".RT->Config->Get('WebBaseURL'));
34 $ENV{'RTSERVER'} =RT->Config->Get('WebBaseURL') ;
35 #    - RTDEBUG       Numeric debug level. (Set to 3 for full logs.)
36 $ENV{'RTDEBUG'} = '1';
37 #    - RTCONFIG      Specifies a name other than ".rtrc" for the
38 #                    configuration file.
39 $ENV{'RTCONFIG'} = '/dev/null';
40 #
41 #    - RTQUERY       Default RT Query for rt list
42 #    - RTORDERBY     Default order for rt list
43
44
45
46
47 # create a ticket
48 expect_run(
49     command => "$rt_tool_path shell",
50     prompt => 'rt> ',
51     quit => 'quit',
52 );
53 expect_send(q{create -t ticket set subject='new ticket' add cc=foo@example.com}, "Creating a ticket...");
54
55 expect_like(qr/Ticket \d+ created/, "Created the ticket");
56 expect_handle->before() =~ /Ticket (\d+) created/;
57 my $ticket_id = $1;
58 ok($ticket_id, "Got ticket id=$ticket_id");
59 expect_send(q{create -t ticket set subject='new ticket'}, "Creating a ticket as just a subject...");
60 expect_like(qr/Ticket \d+ created/, "Created the ticket");
61
62 # make sure we can request things as 'rt foo'
63 expect_send(q{rt create -t ticket set subject='rt ticket'}, "Creating a ticket with 'rt create'...");
64 expect_like(qr/Ticket \d+ created/, "Created the ticket");
65
66
67 # creating queues
68 expect_send("create -t queue set Name='NewQueue$$'", 'Creating a queue...');
69 expect_like(qr/Queue \d+ created/, 'Created the queue');
70 expect_handle->before() =~ /Queue (\d+) created/;
71 my $queue_id = $1;
72 ok($queue_id, "Got queue id=$queue_id");
73 # updating users
74 expect_send("edit queue/$queue_id set Name='EditedQueue$$'", 'Editing the queue');
75 expect_like(qr/Queue $queue_id updated/, 'Edited the queue');
76 expect_send("show queue/$queue_id", 'Showing the queue...');
77 expect_like(qr/id: queue\/$queue_id/, 'Saw the queue');
78 expect_like(qr/Name: EditedQueue$$/, 'Saw the modification');
79 TODO: { 
80     todo_skip "Listing non-ticket items doesn't work", 2;
81     expect_send("list -t queue 'id > 0'", 'Listing the queues...');
82     expect_like(qr/$queue_id: EditedQueue$$/, 'Found the queue');
83 }
84
85 # Queues with spaces in their names
86 expect_send("create -t queue set Name='Spaced Out'", 'Creating a queue...');
87 expect_like(qr/Queue \d+ created/, 'Created the queue');
88 expect_handle->before() =~ /Queue (\d+) created/;
89 my $other_queue = $1;
90 ok($other_queue, "Got queue id=$other_queue");
91 expect_send("show 'queue/Spaced Out'", 'Showing the queue...');
92 expect_like(qr/id: queue\/$other_queue/, 'Saw the queue');
93 expect_like(qr/Name: Spaced Out/, 'Saw the modification');
94
95
96
97 # Set up a custom field for editing tests
98 my $cf = RT::CustomField->new(RT->SystemUser);
99 my ($val,$msg) = $cf->Create(Name => 'MyCF'.$$, Type => 'FreeformSingle', Queue => $queue_id);
100 ok($val,$msg);
101
102 my $othercf = RT::CustomField->new(RT->SystemUser);
103 ($val,$msg) = $othercf->Create(Name => 'My CF'.$$, Type => 'FreeformSingle', Queue => $queue_id);
104 ok($val,$msg);
105
106 my $multiple_cf = RT::CustomField->new(RT->SystemUser);
107 ($val,$msg) = $multiple_cf->Create(Name => 'MultipleCF'.$$, Type =>
108         'FreeformMultiple', Queue => $queue_id);
109 ok($val,$msg);
110
111
112 # add a comment to ticket
113     expect_send("comment -m 'comment-$$' $ticket_id", "Adding a comment...");
114     expect_like(qr/Comments added/, "Added the comment");
115     ### should test to make sure it actually got added
116     # add correspondance to ticket (?)
117     expect_send("correspond -m 'correspond-$$' $ticket_id", "Adding correspondence...");
118     expect_like(qr/Correspondence added/, "Added the correspondence");
119     ### should test to make sure it actually got added
120
121     my $test_email = RT::Test::get_relocatable_file('lorem-ipsum',
122         (File::Spec->updir(), 'data', 'emails'));
123     # add attachments to a ticket
124     # text attachment
125     check_attachment($test_email);
126     # binary attachment
127     check_attachment($RT::StaticPath . '/images/bpslogo.png');
128
129 # change a ticket's Owner
130 expect_send("edit ticket/$ticket_id set owner=root", 'Changing owner...');
131 expect_like(qr/Ticket $ticket_id updated/, 'Changed owner');
132 expect_send("show ticket/$ticket_id -f owner", 'Verifying change...');
133 expect_like(qr/Owner: root/, 'Verified change');
134 # change a ticket's Requestor
135 expect_send("edit ticket/$ticket_id set requestors=foo\@example.com", 'Changing Requestor...');
136 expect_like(qr/Ticket $ticket_id updated/, 'Changed Requestor');
137 expect_send("show ticket/$ticket_id -f requestors", 'Verifying change...');
138 expect_like(qr/Requestors: foo\@example.com/, 'Verified change');
139 # set multiple Requestors
140 expect_send("edit ticket/$ticket_id set requestors=foo\@example.com,bar\@example.com", 'Changing Requestor...');
141 expect_like(qr/Ticket $ticket_id updated/, 'Changed Requestor');
142 expect_send("show ticket/$ticket_id -f requestors", 'Verifying change...');
143 expect_like(qr/Requestors: bar\@example.com, foo\@example.com/, 'Verified change');
144 # change a ticket's Cc
145 expect_send("edit ticket/$ticket_id set cc=bar\@example.com", 'Changing Cc...');
146 expect_like(qr/Ticket $ticket_id updated/, 'Changed Cc');
147 expect_send("show ticket/$ticket_id -f cc", 'Verifying change...');
148 expect_like(qr/Cc: bar\@example.com/, 'Verified change');
149 # change a ticket's priority
150 expect_send("edit ticket/$ticket_id set priority=10", 'Changing priority...');
151 expect_like(qr/Ticket $ticket_id updated/, 'Changed priority');
152 expect_send("show ticket/$ticket_id -f priority", 'Verifying change...');
153 expect_like(qr/Priority: 10/, 'Verified change');
154 # move a ticket to a different queue
155 expect_send("edit ticket/$ticket_id set queue=EditedQueue$$", 'Changing queue...');
156 expect_like(qr/Ticket $ticket_id updated/, 'Changed queue');
157 expect_send("show ticket/$ticket_id -f queue", 'Verifying change...');
158 expect_like(qr/Queue: EditedQueue$$/, 'Verified change');
159 # cannot move ticket to a nonexistent queue
160 expect_send("edit ticket/$ticket_id set queue=nonexistent-$$", 'Changing to nonexistent queue...');
161 expect_like(qr/Queue nonexistent-$$ does not exist/i, 'Errored out');
162 expect_send("show ticket/$ticket_id -f queue", 'Verifying lack of change...');
163 expect_like(qr/Queue: EditedQueue$$/, 'Verified lack of change');
164
165 # Test reading and setting custom fields without spaces
166 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking initial value');
167 expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified initial empty value (CF-x syntax)');
168 expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking initial value');
169 expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified initial empty value (CF.{x} syntax)');
170
171 expect_send("edit ticket/$ticket_id set 'CF-myCF$$=VALUE' ", 'Changing CF...');
172 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
173 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
174 expect_like(qr/\QCF.{myCF$$}\E: VALUE/i, 'Verified change');
175 # Test setting 0 as value of the custom field
176 expect_send("edit ticket/$ticket_id set 'CF-myCF$$=0' ", 'Changing CF...');
177 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
178 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
179 expect_like(qr/\QCF.{myCF$$}\E: 0/i, 'Verified change');
180
181 expect_send("edit ticket/$ticket_id set 'CF.{myCF$$}=VALUE' ",'Changing CF...');
182 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
183 expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking new value');
184 expect_like(qr/\QCF.{myCF$$}\E: VALUE/i, 'Verified change');
185 # Test setting 0 as value of the custom field
186 expect_send("edit ticket/$ticket_id set 'CF.{myCF$$}=0' ", 'Changing CF...');
187 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
188 expect_send("show ticket/$ticket_id -f CF.{myCF$$}", 'Checking new value');
189 expect_like(qr/\QCF.{myCF$$}\E: 0/i, 'Verified change');
190
191 # Test reading and setting custom fields with spaces
192 expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking initial value');
193 expect_like(qr/\QCF.{my CF$$}\E:/i, 'Verified change');
194 expect_send("edit ticket/$ticket_id set 'CF-my CF$$=VALUE' ", 'Changing CF...');
195 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
196 expect_send("show ticket/$ticket_id -f 'CF-my CF$$'", 'Checking new value');
197 expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
198 expect_send("ls -l 'id = $ticket_id' -f 'CF-my CF$$'", 'Checking new value');
199 expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
200
201 expect_send("show ticket/$ticket_id -f 'CF.{my CF$$}'", 'Checking initial value');
202 expect_like(qr/\QCF.{my CF$$}\E: VALUE/i, 'Verified change');
203 expect_send("edit ticket/$ticket_id set 'CF.{my CF$$}=NEW' ", 'Changing CF...');
204 expect_send("show ticket/$ticket_id -f 'CF.{my CF$$}'", 'Checking new value');
205 expect_like(qr/\QCF.{my CF$$}\E: NEW/i, 'Verified change');
206 expect_send("ls -l 'id = $ticket_id' -f 'CF.{my CF$$}'", 'Checking new value');
207 expect_like(qr/\QCF.{my CF$$}\E: NEW/i, 'Verified change');
208
209 # Test reading and setting single value custom field with commas or quotes
210 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking initial value');
211 expect_like(qr/\QCF.{myCF$$}\E:/i, 'Verified change');
212 expect_send("edit ticket/$ticket_id set CF-myCF$$=1,2,3", 'Changing CF...');
213 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
214 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
215 expect_like(qr/\QCF.{myCF$$}\E: 1,2,3/i, 'Verified change');
216 expect_send(qq{edit ticket/$ticket_id set CF-myCF$$="1's,2,3"}, 'Changing CF...');
217 expect_like(qr/Ticket $ticket_id updated/, 'Changed cf');
218 expect_send("show ticket/$ticket_id -f CF-myCF$$", 'Checking new value');
219 expect_like(qr/\QCF.{myCF$$}\E: 1's,2,3/i, 'Verified change');
220
221 # Test reading and setting custom fields with multiple values
222 expect_send("show ticket/$ticket_id -f CF-MultipleCF$$", 'Checking initial value');
223 expect_like(qr/\QCF.{MultipleCF$$}\E:/i, 'Verified multiple cf change');
224 expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=1,2,3 ", 'Changing CF...');
225 expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
226 expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
227 expect_like(qr/\QCF.{MultipleCF$$}\E: 1,\s*2,\s*3/i, 'Verified multiple cf change');
228 expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=a,b,c ", 'Changing CF...');
229 expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
230 expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
231 expect_like(qr/\QCF.{MultipleCF$$}\E: a,\s*b,\s*c/i, 'Verified change');
232 expect_send("edit ticket/$ticket_id del CF.{MultipleCF$$}=a", 'Changing CF...');
233 expect_like(qr/Ticket $ticket_id updated/, 'del multiple cf');
234 expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
235 expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c/i, 'Verified multiple cf change');
236 expect_send("edit ticket/$ticket_id add CF.{MultipleCF$$}=o", 'Changing CF...');
237 expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
238 expect_send("show ticket/$ticket_id -f CF.{MultipleCF$$}", 'Checking new value');
239 expect_like(qr/\QCF.{MultipleCF$$}\E: b,\s*c,\s*o/i, 'Verified multiple cf change');
240
241 sub multi_round_trip {
242     my ($op, $value, $regex) = @_;
243     $Test::Builder::Level++;
244     # The outer double quotes are for the argument parsing that the
245     # command-line does; the extra layer of escaping is to for them, as
246     # well.  It is equivilent to the quoting that the shell would
247     # require.
248     my $quoted = $value;
249     $quoted =~ s/(["\\])/\\$1/g;
250     expect_send(qq{edit ticket/$ticket_id $op CF.{MultipleCF$$}="$quoted"}, qq{CF $op $value});
251     expect_like(qr/Ticket $ticket_id updated/,                              qq{Got expected "updated" answer});
252     expect_send(qq{show ticket/$ticket_id -f CF.{MultipleCF$$}},            qq{Sent "show"});
253     expect_like(qr/\QCF.{MultipleCF$$}\E: $regex$/i,                        qq{Answer matches $regex});
254 }
255
256 # Test simple quoting
257 my $ticket = RT::Ticket->new($RT::SystemUser);
258 $ticket->Load($ticket_id);
259 multi_round_trip(set => q|'a,b,c'|,  qr/'a,b,c'/);
260 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1,     "Has only one CF value");
261 is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
262
263 multi_round_trip(del => q|a|,        qr/'a,b,c'/);
264 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1,     "Still has only one CF value");
265 is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
266
267 multi_round_trip(set => q|q{a,b,c}|, qr/'a,b,c'/);
268 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
269 is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
270
271 multi_round_trip(del => q|a|,        qr/'a,b,c'/);
272 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
273 is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{a,b,c}, "And that CF value is as expected");
274
275 multi_round_trip(del => q|'a,b,c'|,  qr/\s*/);
276 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values");
277
278 multi_round_trip(set => q|q{1,2's,3}|, qr/'1,2\\'s,3'/);
279 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Still has only one CF value");
280 is($ticket->FirstCustomFieldValue("MultipleCF$$"), q{1,2's,3}, "And that CF value is as expected");
281
282 multi_round_trip(del => q|q{1,2's,3}|, qr/\s*/);
283 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 0, "Now has no CF values");
284
285 # Test escaping of quotes - generate (foo)(bar') with no escapes
286 multi_round_trip(set => q|'foo',bar'|, qr/foo,bar'/);
287 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values");
288 is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo|,  "Direct value checks out");
289 is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content,  q|bar'|, "Direct value checks out");
290 multi_round_trip(del => q|bar'|,       qr/foo/);
291
292 # With one \, generate (foo',bar)
293
294 # We obviously need two \s in the following q|| string in order to
295 # generate a string with one actual \ in it; this causes the string to,
296 # in general, have twice as many \s in it as we wish to test.
297 multi_round_trip(set => q|'foo\\',bar'|, qr/'foo\\',bar'/);
298 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value");
299 is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo',bar|,  "Direct value checks out");
300
301 # With two \, generate (foo\)(bar')
302 multi_round_trip(set => q|'foo\\\\',bar'|, qr/foo\\,bar'/);
303 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two values");
304 is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\|,  "Direct value checks out");
305 is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content,  q|bar'|, "Direct value checks out");
306 multi_round_trip(del => q|bar'|,           qr/foo\\/);
307
308 # With three \, generate (foo\',bar)
309 multi_round_trip(set => q|'foo\\\\\\',bar'|, qr/'foo\\\\\\',bar'/);
310 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 1, "Has one value");
311 is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|foo\\',bar|,  "Direct value checks out");
312
313 # Check that we don't infinite-loop on 'foo'bar,baz; this should be ('foo'bar)(baz)
314 expect_send("edit ticket/$ticket_id set CF.{MultipleCF$$}=\"'foo'bar,baz\"", 'Changing CF to have quotes not at commas');
315 expect_like(qr/Ticket $ticket_id updated/, 'Changed multiple cf');
316 is($ticket->CustomFieldValues("MultipleCF$$")->Count, 2, "Has two value");
317 is($ticket->CustomFieldValues("MultipleCF$$")->First->Content, q|'foo'bar|,  "Direct value checks out");
318 is($ticket->CustomFieldValues("MultipleCF$$")->Last->Content, q|baz|,  "Direct value checks out");
319
320
321 # ...
322 # change a ticket's ...[other properties]...
323 # ...
324 # stall a ticket
325 expect_send("edit ticket/$ticket_id set status=stalled", 'Changing status to "stalled"...');
326 expect_like(qr/Ticket $ticket_id updated/, 'Changed status');
327 expect_send("show ticket/$ticket_id -f status", 'Verifying change...');
328 expect_like(qr/Status: stalled/, 'Verified change');
329 # resolve a ticket
330 expect_send("edit ticket/$ticket_id set status=resolved", 'Changing status to "resolved"...');
331 expect_like(qr/Ticket $ticket_id updated/, 'Changed status');
332 expect_send("show ticket/$ticket_id -f status", 'Verifying change...');
333 expect_like(qr/Status: resolved/, 'Verified change');
334 # try to set status to an illegal value
335 expect_send("edit ticket/$ticket_id set status=quux", 'Changing status to an illegal value...');
336 expect_like(qr/isn't a valid status/i, 'Errored out');
337 expect_send("show ticket/$ticket_id -f status", 'Verifying lack of change...');
338 expect_like(qr/Status: resolved/, 'Verified change');
339
340
341
342 # show ticket list
343 expect_send("ls -s -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets...');
344 expect_like(qr/$ticket_id: new ticket/, 'Found our ticket');
345
346 expect_send("ls -s -t ticket -f Requestors $ticket_id", 'getting Requestors');
347 expect_like(qr/$ticket_id\s+bar\@example.com,\s+foo\@example.com/, 'got Requestors');
348
349 # show ticket list verbosely
350 expect_send("ls -l -t ticket -o +id \"Status='resolved'\"", 'Listing resolved tickets verbosely...');
351 expect_like(qr/id: ticket\/$ticket_id/, 'Found our ticket');
352 # show ticket
353 expect_send("show -s -t ticket $ticket_id", 'Showing our ticket...');
354 expect_like(qr/id: ticket\/$ticket_id/, 'Got our ticket');
355 # show ticket history
356 expect_send("show ticket/$ticket_id/history", 'Showing our ticket\'s history...');
357 expect_like(qr/Ticket created by root/, 'Got our history');
358
359 expect_send("show -v ticket/$ticket_id/history", 'Showing our ticket\'s history verbosely...');
360 TODO: {
361     local $TODO = "Cannot show verbose ticket history right now";
362     # show ticket history verbosely
363     expect_like(qr/Ticket created by root/, 'Got our history');
364 }
365 # get attachments from a ticket
366 expect_send("show -s ticket/$ticket_id/attachments", 'Showing ticket attachments...');
367 expect_like(qr/id: ticket\/$ticket_id\/attachments/, 'Got our ticket\'s attachments');
368 expect_like(qr/Attachments: \d+: \(Unnamed\) \(\S+ \/ \d+\w+\)/, 'Our ticket has an attachment');
369 expect_handle->before() =~ /Attachments: (\d+): \(Unnamed\) \((\S+)/;
370 my $attachment_id = $1;
371 my $attachment_type = $2;
372 ok($attachment_id, "Got attachment id=$attachment_id $attachment_type");
373 expect_send("show -s ticket/$ticket_id/attachments/$attachment_id", "Showing attachment $attachment_id...");
374 expect_like(qr/ContentType: $attachment_type/, 'Got the attachment');
375
376
377
378 # creating users
379 expect_send("create -t user set Name='NewUser$$' EmailAddress='fbar$$\@example.com'", 'Creating a user...');
380 expect_like(qr/User \d+ created/, 'Created the user');
381 expect_handle->before() =~ /User (\d+) created/;
382 my $user_id = $1;
383 ok($user_id, "Got user id=$user_id");
384 # updating users
385 expect_send("edit user/$user_id set Name='EditedUser$$'", 'Editing the user');
386 expect_like(qr/User $user_id updated/, 'Edited the user');
387 expect_send("show user/$user_id", 'Showing the user...');
388 expect_like(qr/id: user\/$user_id/, 'Saw the user');
389 expect_like(qr/Name: EditedUser$$/, 'Saw the modification');
390 TODO: { 
391     todo_skip "Listing non-ticket items doesn't work", 2;
392     expect_send("list -t user 'id > 0'", 'Listing the users...');
393     expect_like(qr/$user_id: EditedUser$$/, 'Found the user');
394 }
395
396
397
398 TODO: {
399 todo_skip "Group manipulation doesn't work right now", 8;
400 # creating groups
401 expect_send("create -t group set Name='NewGroup$$'", 'Creating a group...');
402 expect_like(qr/Group \d+ created/, 'Created the group');
403 expect_handle->before() =~ /Group (\d+) created/;
404 my $group_id = $1;
405 ok($group_id, "Got group id=$group_id");
406 # updating groups
407 expect_send("edit group/$group_id set Name='EditedGroup$$'", 'Editing the group');
408 expect_like(qr/Group $group_id updated/, 'Edited the group');
409 expect_send("show group/$group_id", 'Showing the group...');
410 expect_like(qr/id: group\/$group_id/, 'Saw the group');
411 expect_like(qr/Name: EditedGroup$$/, 'Saw the modification');
412 TODO: { 
413     local $TODO = "Listing non-ticket items doesn't work";
414     expect_send("list -t group 'id > 0'", 'Listing the groups...');
415     expect_like(qr/$group_id: EditedGroup$$/, 'Found the group');
416 }
417 }
418
419
420 TODO: {
421 todo_skip "Custom field manipulation not yet implemented", 8;
422
423 # creating custom fields
424 expect_send("create -t custom_field set Name='NewCF$$'", 'Creating a custom field...');
425 expect_like(qr/Custom Field \d+ created/, 'Created the custom field');
426 expect_handle->before() =~ /Custom Field (\d+) created/;
427 my $cf_id = $1;
428 ok($cf_id, "Got custom field id=$cf_id");
429 # updating custom fields
430 expect_send("edit cf/$cf_id set Name='EditedCF$$'", 'Editing the custom field');
431 expect_like(qr/Custom field $cf_id updated/, 'Edited the custom field');
432 expect_send("show cf/$cf_id", 'Showing the queue...');
433 expect_like(qr/id: custom_field\/$cf_id/, 'Saw the custom field');
434 expect_like(qr/Name: EditedCF$$/, 'Saw the modification');
435 TODO: { 
436     todo_skip "Listing non-ticket items doesn't work", 2;
437     expect_send("list -t custom_field 'id > 0'", 'Listing the CFs...');
438     expect_like(qr/$cf_id: EditedCF$$/, 'Found the custom field');
439 }
440 }
441
442
443 expect_send("create -t ticket set subject='CLIMergeTest1-$$'", 'Creating first ticket to merge...');
444 expect_like(qr/Ticket \d+ created/, 'Created first ticket');
445 expect_handle->before() =~ /Ticket (\d+) created/;
446 my $merge_ticket_A = $1;
447 ok($merge_ticket_A, "Got first ticket to merge id=$merge_ticket_A");
448 expect_send("create -t ticket set subject='CLIMergeTest2-$$'", 'Creating second ticket to merge...');
449 expect_like(qr/Ticket \d+ created/, 'Created second ticket');
450 expect_handle->before() =~ /Ticket (\d+) created/;
451 my $merge_ticket_B = $1;
452 ok($merge_ticket_B, "Got second ticket to merge id=$merge_ticket_B");
453 expect_send("merge $merge_ticket_B $merge_ticket_A", 'Merging the tickets...');
454 expect_like(qr/Merge completed/, 'Merged the tickets');
455
456 expect_send("show ticket/$merge_ticket_A/history", 'Checking merge on first ticket');
457 expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in first ticket');
458 expect_send("show ticket/$merge_ticket_B/history", 'Checking merge on second ticket');
459 expect_like(qr/Merged into #$merge_ticket_A: CLIMergeTest1-$$ by root/, 'Merge recorded in second ticket');
460
461 {
462     # create a user; give them privileges to take and steal
463     ### TODO: implement 'grant' in the CLI tool; use that here instead.
464     ###       this breaks the abstraction barrier, like, a lot.
465     my $steal_user = RT::User->new(RT->SystemUser);
466     my ($steal_user_id, $msg) = $steal_user->Create( Name => "fooser$$",
467                                           EmailAddress => "fooser$$\@localhost",
468                                           Privileged => 1,
469                                           Password => 'foobar',
470                                         );
471     ok($steal_user_id, "Created the user? $msg");
472     my $steal_queue = RT::Queue->new(RT->SystemUser);
473     my $steal_queue_id;
474     ($steal_queue_id, $msg) = $steal_queue->Create( Name => "Steal$$" );
475     ok($steal_queue_id, "Got the queue? $msg");
476     ok($steal_queue->id, "queue obj has id");
477     my $status;
478     ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'ShowTicket', Object => $steal_queue );
479     ok($status, "Gave 'ShowTicket' to our user? $msg");
480     ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'OwnTicket', Object => $steal_queue );
481     ok($status, "Gave 'OwnTicket' to our user? $msg");
482     ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'StealTicket', Object => $steal_queue );
483     ok($status, "Gave 'StealTicket' to our user? $msg");
484     ($status, $msg) = $steal_user->PrincipalObj->GrantRight( Right => 'TakeTicket', Object => $steal_queue );
485     ok($status, "Gave 'TakeTicket' to our user? $msg");
486
487     # create a ticket to take/steal
488     expect_send("create -t ticket set queue=$steal_queue_id subject='CLIStealTest-$$'", 'Creating ticket to steal...');
489     expect_like(qr/Ticket \d+ created/, 'Created ticket');
490     expect_handle->before() =~ /Ticket (\d+) created/;
491     my $steal_ticket_id = $1;
492     ok($steal_ticket_id, "Got ticket to steal id=$steal_ticket_id");
493
494     # root takes the ticket
495     expect_send("take $steal_ticket_id", 'root takes the ticket...');
496     expect_like(qr/Owner changed from Nobody to root/, 'root took the ticket');
497     expect_quit();
498
499     # log in as the non-root user
500     $ENV{'RTUSER'} = "fooser$$";
501     $ENV{'RTPASSWD'} = 'foobar';
502     expect_run( command => "$rt_tool_path shell", prompt => 'rt> ', quit => 'quit',);
503
504     # user tries to take the ticket, fails
505     # shouldn't be able to 'take' a ticket which someone else has taken out from
506     # under you; that should produce an error.  should have to explicitly 
507     # 'steal' it back from them.  'steal' can automatically 'take' a ticket,
508     # though.
509     expect_send("take $steal_ticket_id", 'user tries to take the ticket...');
510     expect_like(qr/You can only take tickets that are unowned/, '...and fails.');
511     expect_send("show ticket/$steal_ticket_id -f owner", 'Double-checking...');
512     expect_like(qr/Owner: root/, '...no change.');
513
514     # user steals the ticket
515     expect_send("steal $steal_ticket_id", 'user tries to *steal* the ticket...');
516     expect_like(qr/Owner changed from root to fooser$$/, '...and succeeds!');
517     expect_send("show ticket/$steal_ticket_id -f owner", 'Double-checking...');
518     expect_like(qr/Owner: fooser$$/, '...yup, it worked.');
519     expect_quit();
520
521     # log back in as root
522     $ENV{'RTUSER'} = 'root';
523     $ENV{'RTPASSWD'} = 'password';
524     expect_run( command => "$rt_tool_path shell", prompt => 'rt> ', quit => 'quit',);
525
526     # root steals the ticket back
527     expect_send("steal $steal_ticket_id", 'root steals the ticket back...');
528     expect_like(qr/Owner changed from fooser$$ to root/, '...and succeeds.');
529 }
530
531     my @link_relns = ( 'DependsOn', 'DependedOnBy', 'RefersTo', 'ReferredToBy',
532                        'MemberOf', 'HasMember', );
533     my %display_relns = map { $_ => $_ } @link_relns;
534     $display_relns{HasMember} = 'Members';
535
536     my $link1_id = ok_create_ticket( "LinkTicket1-$$" );
537     my $link2_id = ok_create_ticket( "LinkTicket2-$$" );
538
539     foreach my $reln (@link_relns) {
540         # create link
541         expect_send("link $link1_id $reln $link2_id", "Link by $reln...");
542         expect_like(qr/Created link $link1_id $reln $link2_id/, 'Linked');
543         expect_send("show -s ticket/$link1_id/links", "Checking creation of $reln...");
544         expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Created link $reln");
545         expect_send("show ticket/$link1_id/links", "Checking show links without format");
546         expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Found link $reln");
547
548         # delete link
549         expect_send("link -d $link1_id $reln $link2_id", "Delete $reln...");
550         expect_like(qr/Deleted link $link1_id $reln $link2_id/, 'Deleted');
551         expect_send("show ticket/$link1_id/links", "Checking removal of $reln...");
552         ok( expect_handle->before() !~ /\Q$display_relns{$reln}: \E[\w\d\.\-]+:\/\/[w\d\.]+\/ticket\/$link2_id/, "Removed link $reln" );
553         #expect_unlike(qr/\Q$reln: \E[\w\d\.]+\Q://\E[w\d\.]+\/ticket\/$link2_id/, "Removed link $reln");
554
555     }
556
557 expect_quit(); # We need to do this ourselves, so that we quit
558                # *before* we tear down the webserver.
559
560 # helper function
561 sub ok_create_ticket {
562     my $subject = shift;
563
564     expect_send("create -t ticket set subject='$subject'", 'Creating ticket...');
565     expect_like(qr/Ticket \d+ created/, "Created ticket '$subject'");
566     expect_handle->before() =~ /Ticket (\d+) created/;
567     my $id = $1;
568     ok($id, "Got ticket id=$id");
569     
570     return $id;
571 }
572
573 # wrap up all the file handling stuff for attachment testing
574 sub check_attachment {
575     my $attachment_path = shift;
576     (my $filename = $attachment_path) =~ s/.*\/(.*)$/$1/;
577     expect_send("comment -m 'attach file' -a $attachment_path $ticket_id", "Adding an attachment ($filename)");
578     expect_like(qr/Comments added/, "Added the attachment");
579     expect_send("show ticket/$ticket_id/attachments","Finding Attachment");
580     my $attachment_regex = qr/(\d+):\s+$filename/;
581     expect_like($attachment_regex,"Attachment Uploaded");
582     expect_handle->before() =~ $attachment_regex;
583     my $attachment_id = $1;
584     expect_send("show ticket/$ticket_id/attachments/$attachment_id/content","Fetching Attachment");
585     open( my $fh, '<', $attachment_path ) or die "Can't open $attachment_path: $!";
586     my $attachment_content = do { local($/); <$fh> };
587     close $fh;
588     chomp $attachment_content;
589     TODO: {
590         local $TODO = "Binary PNG content is getting mangled somewhere along the way"
591             if $attachment_path =~ /\.png$/;
592         is(
593             MIME::Base64::encode_base64(Test::Expect::before()),
594             MIME::Base64::encode_base64($attachment_content),
595             "Attachment contains original text"
596        );
597     }
598 }
599
600 # you may encounter warning like Use of uninitialized value $ampm
601 # ... in Time::ParseDate
602 my @warnings = grep { $_ !~ /\$ampm/ } $m->get_warnings;
603 is( scalar @warnings, 0, 'no extra warnings' );
604
605 undef $m;
606 done_testing;
607
608 1; # needed to avoid a weird exit value from expect_quit