default to a session cookie instead of setting an explicit timeout, weird timezone...
[freeside.git] / rt / t / ticket / linking.t
1
2 use strict;
3 use warnings;
4
5 use RT::Test tests => 102;
6 use Test::Warn;
7 use_ok('RT');
8 use_ok('RT::Ticket');
9 use_ok('RT::ScripConditions');
10 use_ok('RT::ScripActions');
11 use_ok('RT::Template');
12 use_ok('RT::Scrips');
13 use_ok('RT::Scrip');
14
15
16 my $filename = File::Spec->catfile( RT::Test->temp_directory, 'link_count' );
17 open my $fh, '>', $filename or die $!;
18 close $fh;
19
20 my $link_acl_checks_orig = RT->Config->Get( 'StrictLinkACL' );
21 RT->Config->Set( 'StrictLinkACL', 1);
22
23 my $condition = RT::ScripCondition->new( RT->SystemUser );
24 $condition->Load('User Defined');
25 ok($condition->id);
26 my $action = RT::ScripAction->new( RT->SystemUser );
27 $action->Load('User Defined');
28 ok($action->id);
29 my $template = RT::Template->new( RT->SystemUser );
30 $template->Load('Blank');
31 ok($template->id);
32
33 my $q1 = RT::Queue->new(RT->SystemUser);
34 my ($id,$msg) = $q1->Create(Name => "LinkTest1.$$");
35 ok ($id,$msg);
36 my $q2 = RT::Queue->new(RT->SystemUser);
37 ($id,$msg) = $q2->Create(Name => "LinkTest2.$$");
38 ok ($id,$msg);
39
40 my $commit_code = <<END;
41 open( my \$file, '<', "$filename" ) or die "couldn't open $filename";
42 my \$data = <\$file>;
43 \$data += 0;
44 close \$file;
45 \$RT::Logger->debug("Data is \$data");
46
47 open( \$file, '>', "$filename" ) or die "couldn't open $filename";
48 if (\$self->TransactionObj->Type eq 'AddLink') {
49     \$RT::Logger->debug("AddLink");
50     print \$file \$data+1, "\n";
51 }
52 elsif (\$self->TransactionObj->Type eq 'DeleteLink') {
53     \$RT::Logger->debug("DeleteLink");
54     print \$file \$data-1, "\n";
55 }
56 else {
57     \$RT::Logger->error("THIS SHOULDN'T HAPPEN");
58     print \$file "666\n";
59 }
60 close \$file;
61 1;
62 END
63
64 my $Scrips = RT::Scrips->new( RT->SystemUser );
65 $Scrips->UnLimit;
66 while ( my $Scrip = $Scrips->Next ) {
67     $Scrip->Delete if $Scrip->Description and $Scrip->Description =~ /Add or Delete Link \d+/;
68 }
69
70
71 my $scrip = RT::Scrip->new(RT->SystemUser);
72 ($id,$msg) = $scrip->Create( Description => "Add or Delete Link $$",
73                           ScripCondition => $condition->id,
74                           ScripAction    => $action->id,
75                           Template       => $template->id,
76                           Stage          => 'TransactionCreate',
77                           Queue          => 0,
78                   CustomIsApplicableCode => '$self->TransactionObj->Type =~ /(Add|Delete)Link/;',
79                        CustomPrepareCode => '1;',
80                        CustomCommitCode  => $commit_code,
81                            );
82 ok($id, "Scrip created");
83
84 my $u1 = RT::User->new(RT->SystemUser);
85 ($id,$msg) = $u1->Create(Name => "LinkTestUser.$$");
86 ok ($id,$msg);
87
88 # grant ShowTicket right to allow count transactions
89 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'ShowTicket');
90 ok ($id,$msg);
91 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'ShowTicket');
92 ok ($id,$msg);
93 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'CreateTicket');
94 ok ($id,$msg);
95
96 my $creator = RT::CurrentUser->new($u1->id);
97
98 diag('Create tickets without rights to link');
99 {
100     # on q2 we have no rights, yet
101     my $parent = RT::Ticket->new( RT->SystemUser );
102     my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
103     ok($id,$msg);
104     my $child = RT::Ticket->new( $creator );
105     ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id );
106     ok($id,$msg);
107     $child->CurrentUser( RT->SystemUser );
108     is($child->_Links('Base')->Count, 0, 'link was not created, no permissions');
109     is($child->_Links('Target')->Count, 0, 'link was not create, no permissions');
110 }
111
112 diag('Create tickets with rights checks on one end of a link');
113 {
114     # on q2 we have no rights, but use checking one only on thing
115     RT->Config->Set( StrictLinkACL => 0 );
116     my $parent = RT::Ticket->new( RT->SystemUser );
117     my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
118     ok($id,$msg);
119     my $child = RT::Ticket->new( $creator );
120     ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id, MemberOf => $parent->id );
121     ok($id,$msg);
122     $child->CurrentUser( RT->SystemUser );
123     is($child->_Links('Base')->Count, 1, 'link was created');
124     is($child->_Links('Target')->Count, 0, 'link was created only one');
125     # only one scrip run (on second ticket) since this is on a ticket Create txn
126     is(link_count($filename), 1, "scrips ok");
127     RT->Config->Set( StrictLinkACL => 1 );
128 }
129
130 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q1, Right => 'ModifyTicket');
131 ok ($id,$msg);
132
133 diag('try to add link without rights');
134 {
135     # on q2 we have no rights, yet
136     my $parent = RT::Ticket->new( RT->SystemUser );
137     my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
138     ok($id,$msg);
139     my $child = RT::Ticket->new( $creator );
140     ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id );
141     ok($id,$msg);
142     ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
143     ok(!$id, $msg);
144     is(link_count($filename), 0, "scrips ok");
145     $child->CurrentUser( RT->SystemUser );
146     is($child->_Links('Base')->Count, 0, 'link was not created, no permissions');
147     is($child->_Links('Target')->Count, 0, 'link was not create, no permissions');
148 }
149
150 diag('add link with rights only on base');
151 {
152     # on q2 we have no rights, but use checking one only on thing
153     RT->Config->Set( StrictLinkACL => 0 );
154     my $parent = RT::Ticket->new( RT->SystemUser );
155     my ($id,$tid,$msg) = $parent->Create( Subject => 'Link test 1', Queue => $q2->id );
156     ok($id,$msg);
157     my $child = RT::Ticket->new( $creator );
158     ($id,$tid,$msg) = $child->Create( Subject => 'Link test 1', Queue => $q1->id );
159     ok($id,$msg);
160     ($id, $msg) = $child->AddLink(Type => 'MemberOf', Target => $parent->id);
161     ok($id, $msg);
162     is(link_count($filename), 2, "scrips ok");
163     $child->CurrentUser( RT->SystemUser );
164     is($child->_Links('Base')->Count, 1, 'link was created');
165     is($child->_Links('Target')->Count, 0, 'link was created only one');
166     $child->CurrentUser( $creator );
167
168     # turn off feature and try to delete link, we should fail
169     RT->Config->Set( StrictLinkACL => 1 );
170     ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id);
171     ok(!$id, $msg);
172     is(link_count($filename), 0, "scrips ok");
173     $child->CurrentUser( RT->SystemUser );
174     $child->_Links('Base')->_DoCount;
175     is($child->_Links('Base')->Count, 1, 'link was not deleted');
176     $child->CurrentUser( $creator );
177
178     # try to delete link, we should success as feature is active
179     RT->Config->Set( StrictLinkACL => 0 );
180     ($id, $msg) = $child->DeleteLink(Type => 'MemberOf', Target => $parent->id);
181     ok($id, $msg);
182     is(link_count($filename), -2, "scrips ok");
183     $child->CurrentUser( RT->SystemUser );
184     $child->_Links('Base')->_DoCount;
185     is($child->_Links('Base')->Count, 0, 'link was deleted');
186     RT->Config->Set( StrictLinkACL => 1 );
187 }
188
189 my $tid;
190 my $ticket = RT::Ticket->new( $creator);
191 ok($ticket->isa('RT::Ticket'));
192 ($id,$tid, $msg) = $ticket->Create(Subject => 'Link test 1', Queue => $q1->id);
193 ok ($id,$msg);
194
195 diag('try link to itself');
196 {
197     warning_like {
198         ($id, $msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket->id);
199     } qr/Can't link a ticket to itself/;
200     ok(!$id, $msg);
201     is(link_count($filename), 0, "scrips ok");
202 }
203
204 my $ticket2 = RT::Ticket->new(RT->SystemUser);
205 ($id, $tid, $msg) = $ticket2->Create(Subject => 'Link test 2', Queue => $q2->id);
206 ok ($id, $msg);
207 ($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
208 ok(!$id,$msg);
209 is(link_count($filename), 0, "scrips ok");
210
211 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'CreateTicket');
212 ok ($id,$msg);
213 ($id,$msg) = $u1->PrincipalObj->GrantRight ( Object => $q2, Right => 'ModifyTicket');
214 ok ($id,$msg);
215 ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
216 ok($id,$msg);
217 is(link_count($filename), 2, "scrips ok");
218
219 warnings_like {
220     ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => -1);
221 } [
222     qr/Could not determine a URI scheme for -1/,
223 ];
224
225 ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
226 ok($id,$msg);
227 is(link_count($filename), 0, "scrips ok"); # already added
228
229 my $transactions = $ticket2->Transactions;
230 $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
231 is( $transactions->Count, 1, "Transaction found in other ticket" );
232 is( $transactions->First->Field , 'ReferredToBy');
233 is( $transactions->First->NewValue , $ticket->URI );
234
235 ($id,$msg) = $ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
236 ok($id,$msg);
237 is(link_count($filename), -2, "scrips ok");
238 $transactions = $ticket2->Transactions;
239 $transactions->Limit( FIELD => 'Type', VALUE => 'DeleteLink' );
240 is( $transactions->Count, 1, "Transaction found in other ticket" );
241 is( $transactions->First->Field , 'ReferredToBy');
242 is( $transactions->First->OldValue , $ticket->URI );
243
244 ($id,$msg) =$ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id);
245 ok($id,$msg);
246 is(link_count($filename), 2, "scrips ok");
247 ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id);
248 ok($id,$msg);
249 is(link_count($filename), -2, "scrips ok");
250
251 # tests for silent behaviour
252 ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1);
253 ok($id,$msg);
254 is(link_count($filename), 0, "scrips ok");
255 {
256     my $transactions = $ticket->Transactions;
257     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
258     is( $transactions->Count, 2, "Still two txns on the base" );
259
260     $transactions = $ticket2->Transactions;
261     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
262     is( $transactions->Count, 2, "Still two txns on the target" );
263
264 }
265 ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, Silent => 1);
266 ok($id,$msg);
267 is(link_count($filename), 0, "scrips ok");
268
269 ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1);
270 ok($id,$msg);
271 is(link_count($filename), 1, "scrips ok");
272 {
273     my $transactions = $ticket->Transactions;
274     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
275     is( $transactions->Count, 2, "still five txn on the base" );
276
277     $transactions = $ticket2->Transactions;
278     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
279     is( $transactions->Count, 3, "+1 txn on the target" );
280
281 }
282 ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentBase => 1);
283 ok($id,$msg);
284 is(link_count($filename), -1, "scrips ok");
285
286 ($id,$msg) = $ticket->AddLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1);
287 ok($id,$msg);
288 is(link_count($filename), 1, "scrips ok");
289 {
290     my $transactions = $ticket->Transactions;
291     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
292     is( $transactions->Count, 3, "+1 txn on the base" );
293
294     $transactions = $ticket2->Transactions;
295     $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' );
296     is( $transactions->Count, 3, "three txns on the target" );
297 }
298 ($id,$msg) =$ticket->DeleteLink(Type => 'RefersTo', Target => $ticket2->id, SilentTarget => 1);
299 ok($id,$msg);
300 is(link_count($filename), -1, "scrips ok");
301
302
303 # restore
304 RT->Config->Set( StrictLinkACL => $link_acl_checks_orig );
305
306 {
307     my $Scrips = RT::Scrips->new( RT->SystemUser );
308     $Scrips->Limit( FIELD => 'Description', OPERATOR => 'STARTSWITH', VALUE => 'Add or Delete Link ');
309     while ( my $s = $Scrips->Next ) { $s->Delete };
310 }
311
312
313 my $link = RT::Link->new( RT->SystemUser );
314 ($id,$msg) = $link->Create( Base => $ticket->URI, Target => $ticket2->URI, Type => 'MyLinkType' );
315 ok($id, $msg);
316 ok($link->LocalBase   == $ticket->id,  "LocalBase   set correctly");
317 ok($link->LocalTarget == $ticket2->id, "LocalTarget set correctly");
318
319 {
320     no warnings 'once';
321     *RT::NotTicket::Id = sub { return $$ };
322     *RT::NotTicket::id = \&RT::NotTicket::Id;
323 }
324
325 {
326     package RT::URI::not_ticket;
327     use RT::URI::base;
328     use vars qw(@ISA);
329     @ISA = qw/RT::URI::base/;
330     sub IsLocal { 1; }
331     sub Object { return bless {}, 'RT::NotTicket'; }
332 }
333
334 my $orig_getresolver = \&RT::URI::_GetResolver;
335 {
336     no warnings 'redefine';
337     *RT::URI::_GetResolver = sub {
338         my $self = shift;
339         my $scheme = shift;
340
341         $scheme =~ s/(\.|-)/_/g;
342         my $resolver;
343         my $module = "RT::URI::$scheme";
344         $resolver = $module->new($self->CurrentUser);
345
346        if ($resolver) {
347            $self->{'resolver'} = $resolver;
348         } else {
349             $self->{'resolver'} = RT::URI::base->new($self->CurrentUser);
350         }
351     };
352 }
353
354 ($id,$msg) = $link->Create( Base => "not_ticket::$RT::Organization/notticket/$$", Target => $ticket2->URI, Type => 'MyLinkType' );
355 ok($id, $msg);
356 ok($link->LocalBase   == 0,            "LocalBase set correctly");
357 ok($link->LocalTarget == $ticket2->id, "LocalTarget set correctly");
358
359 ($id,$msg) = $link->Create( Target => "not_ticket::$RT::Organization/notticket/$$", Base => $ticket->URI, Type => 'MyLinkType' );
360 ok($id, $msg);
361 ok($link->LocalTarget == 0,           "LocalTarget set correctly");
362 ok($link->LocalBase   == $ticket->id, "LocalBase set correctly");
363
364 ($id,$msg) = $link->Create(
365                        Target => "not_ticket::$RT::Organization/notticket/1$$",
366                        Base   => "not_ticket::$RT::Organization/notticket/$$",
367                        Type => 'MyLinkType' );
368
369 ok($id, $msg);
370 ok($link->LocalTarget == 0, "LocalTarget set correctly");
371 ok($link->LocalBase   == 0, "LocalBase set correctly");
372
373 # restore _GetResolver
374 {
375     no warnings 'redefine';
376     *RT::URI::_GetResolver = $orig_getresolver;
377 }
378
379 sub link_count {
380     my $file = shift;
381     open( my $fh, '<', $file ) or die "couldn't open $file";
382     my $data = <$fh>;
383     close $fh;
384     truncate($file, 0);
385
386     return 0 unless defined $data;
387     chomp $data;
388     return $data + 0;
389 }