File Coverage

lib/Test/Neo4j/Types.pm
Criterion Covered Total %
statement 349 354 98.5
branch 28 36 77.7
condition n/a
subroutine 45 45 100.0
pod 6 6 100.0
total 428 441 97.0


line stmt bran cond sub pod time code
1 4     4   481651 use v5.10;
  4         15  
2 4     4   20 use strict;
  4         6  
  4         130  
3 4     4   16 use warnings;
  4         9  
  4         438  
4              
5             package Test::Neo4j::Types;
6             # ABSTRACT: Tools for testing Neo4j type modules
7             $Test::Neo4j::Types::VERSION = '0.05';
8              
9 4     4   19 use Test::More 0.94;
  4         54  
  4         29  
10 4     4   2878 use Test::Exception;
  4         13358  
  4         10  
11 4     4   2795 use Test::Warnings qw(warnings :no_end_test);
  4         10153  
  4         19  
12              
13 4     4   591 use Exporter 'import';
  4         20  
  4         187  
14 4     4   148 BEGIN { our @EXPORT = qw(
15             neo4j_node_ok
16             neo4j_relationship_ok
17             neo4j_path_ok
18             neo4j_point_ok
19             neo4j_datetime_ok
20             neo4j_duration_ok
21             )}
22              
23             {
24             # This happens within Neo4j/Types.pm version 1.08 and newer,
25             # but we can't be sure the installed version is that new:
26             package # local
27             Neo4j::Types;
28 4     4   23 use warnings::register;
  4         4  
  4         348  
29             }
30              
31              
32             sub _load_module_ok {
33 18     18   92 my ($name, $package) = @_;
34            
35             # We want the test to fail if the module hasn't been loaded, but the
36             # error message you get normally isn't very helpful. So this sub will
37             # check if the module is loaded and return true if that's the case.
38             # Otherwise, it will try to load the module. No eval means that if
39             # loading fails, users get the original error message. If loading
40             # succeeds, we fail the test anyway because the user is supposed to
41             # load the module (checking for this can detect bugs where the
42             # user expects their code to load the module, but it actually
43             # doesn't get loaded).
44             {
45             # Look for entries in the package's symbol table
46 4     4   18 no strict 'refs';
  4         8  
  4         1826  
  18         39  
47 18 50       66 return 1 if keys %{"${package}::"};
  18         353  
48             }
49 0         0 diag "$package is not loaded";
50 0         0 $package =~ s<::>g;
51 0         0 require "$package.pm";
52 0         0 fail $name;
53 0         0 return 0;
54             }
55              
56              
57             sub _element_id_test {
58 16     16   103 my ($BOTH, $ID_ONLY, $new, $class, $prefix) = @_;
59            
60             subtest "${prefix}element_id", sub {
61 16     16   22854 plan tests => 6;
62            
63 16         19487 my $both = $new->($class, {%$BOTH});
64 16         313 my $id_only = $new->($class, {%$ID_ONLY});
65 16 100       342 lives_ok { $both->element_id } 'optional op element_id' if $both->can('element_id');
  4         147  
66 16 100       1590 dies_ok { $both->element_id } 'optional op element_id' if ! $both->can('element_id');
  12         878  
67             SKIP: {
68 16 100       6280 skip 'optional op element_id unimplemented', 2+3 unless $class->can('element_id');
  16         187  
69 4         10 my ($element_id, $id) = map { "$prefix$_" } qw( element_id id );
  8         21  
70            
71             # When both IDs are present, id() MAY warn
72 4         14 is $both->$element_id(), $BOTH->{$element_id}, "$element_id";
73 4         1443 warnings { is $both->$id(), $BOTH->{$id}, "legacy $id" };
  4         70  
74            
75             # For a missing element ID, element_id() returns the numeric ID and MUST warn
76 4         1475 my @w_eid = warnings { is $id_only->$element_id(), $ID_ONLY->{$id}, "no $element_id with legacy $id" };
  4         45  
77 4         1391 ok @w_eid, "no $element_id warns";
78 4 50       1488 warn @w_eid if @w_eid > 1;
79 4     4   25 no warnings 'Neo4j::Types';
  4         6  
  4         13848  
80 4         30 is warnings { $id_only->$element_id() }, @w_eid - 1, "no $element_id warn cat is Neo4j::Types";
  4         49  
81             };
82 16         194 };
83             }
84              
85              
86             sub _node_test {
87 4     4   28 my ($node_class, $new) = @_;
88            
89 4         16 plan tests => 12 + 5 + 7 + 1 + 1;
90            
91 4         4981 my ($n, @l, $p);
92            
93 4         55 $n = $new->($node_class, my $id_only = {
94             id => 42,
95             labels => ['Foo', 'Bar'],
96             properties => { foofoo => 11, barbar => 22, '123' => [1, 2, 3] },
97             });
98 4         99 is $n->id(), 42, 'id';
99 4         2363 @l = $n->labels;
100 4         54 is scalar(@l), 2, 'label count';
101 4         2067 is $l[0], 'Foo', 'label Foo';
102 4         1927 is $l[1], 'Bar', 'label Bar';
103 4     4   2317 lives_and { is scalar($n->labels), 2 } 'scalar context';
  4         82  
104 4         6310 is $n->get('foofoo'), 11, 'get foofoo';
105 4         2193 is $n->get('barbar'), 22, 'get barbar';
106 4         1814 is_deeply $n->get('123'), [1, 2, 3], 'get 123';
107 4         3459 $p = $n->properties;
108 4         43 is ref($p), 'HASH', 'props ref';
109 4         1824 is $p->{foofoo}, 11, 'props foofoo';
110 4         1774 is $p->{barbar}, 22, 'props barbar';
111 4         1794 is_deeply $p->{123}, [1, 2, 3], 'props 123';
112            
113 4         2842 $n = $new->($node_class, {
114             id => 0,
115             properties => { '0' => [] },
116             });
117 4         77 is $n->id(), 0, 'id 0';
118 4         1725 is ref($n->get('0')), 'ARRAY', 'get 0 ref';
119 4     4   1774 lives_and { is scalar(@{$n->get('0')}), 0 } 'get 0 empty';
  4         64  
  4         18  
120 4         1833 $p = $n->properties;
121 4         39 is_deeply $p, {0=>[]}, 'props deeply';
122 4         3650 is_deeply [$n->properties], [{0=>[]}], 'props list context';
123            
124 4         4371 $n = $new->($node_class, { });
125 4         67 ok ! defined($n->id), 'id gigo';
126 4         1651 @l = $n->labels;
127 4         60 is scalar(@l), 0, 'no labels';
128 4     4   1740 lives_and { is scalar($n->labels), 0 } 'scalar context no labels';
  4         77  
129 4         5946 $p = $n->properties;
130 4         42 is ref($p), 'HASH', 'empty props ref';
131 4         1788 is scalar(keys %$p), 0, 'empty props empty';
132 4         1815 is_deeply [$n->get('whatever')], [undef], 'prop undef';
133 4         2996 ok ! exists $n->properties->{whatever}, 'prop remains non-existent';
134            
135             # element ID
136 4         1626 my $both = { element_id => 'e17', id => 17 };
137 4         24 _element_id_test($both, $id_only, $new, $node_class, '');
138            
139 4         21817 ok $n->DOES('Neo4j::Types::Node'), 'does role';
140             }
141              
142              
143             sub neo4j_node_ok {
144 4     4 1 775481 my ($class, $new, $name) = @_;
145 4 100       23 $name = "neo4j_node_ok '$class'" unless defined $name;
146             _load_module_ok($name, $class) and
147 4 50   4   23 subtest $name, sub { _node_test($class, $new) };
  4         5189  
148             }
149              
150              
151             sub _relationship_test {
152 4     4   12 my ($rel_class, $new) = @_;
153            
154 4         20 plan tests => 11 + 5 + 8 + 3 + 1;
155            
156 4         3299 my ($r, $p);
157            
158 4         53 $r = $new->($rel_class, my $id_only = {
159             id => 55,
160             type => 'TEST',
161             start_id => 34,
162             end_id => 89,
163             properties => { foo => 144, bar => 233, '358' => [3, 5, 8] },
164             });
165 4         93 is $r->id, 55, 'id';
166 4         1789 is $r->type, 'TEST', 'type';
167 4         1727 is $r->start_id, 34, 'start id';
168 4         1663 is $r->end_id, 89, 'end id';
169 4         1696 is $r->get('foo'), 144, 'get foo';
170 4         3019 is $r->get('bar'), 233, 'get bar';
171 4         1761 is_deeply $r->get('358'), [3, 5, 8], 'get 358';
172 4         3112 $p = $r->properties;
173 4         32 is ref($p), 'HASH', 'props ref';
174 4         1737 is $p->{foo}, 144, 'props foo';
175 4         1651 is $p->{bar}, 233, 'props bar';
176 4         1725 is_deeply $p->{358}, [3, 5, 8], 'props 358';
177            
178 4         2831 $r = $new->($rel_class, {
179             id => 0,
180             properties => { '0' => [] },
181             });
182 4         108 is $r->id(), 0, 'id 0';
183 4         1701 is ref($r->get('0')), 'ARRAY', 'get 0 ref';
184 4     4   1679 lives_and { is scalar(@{$r->get('0')}), 0 } 'get 0 empty';
  4         86  
  4         18  
185 4         1784 $p = $r->properties;
186 4         41 is_deeply $p, {0=>[]}, 'props deeply';
187 4         4567 is_deeply [$r->properties], [{0=>[]}], 'props list context';
188            
189 4         4106 $r = $new->($rel_class, { });
190 4         58 ok ! defined($r->id), 'id gigo';
191 4         1586 ok ! defined($r->type), 'no type';
192 4         1732 ok ! defined($r->start_id), 'no start id';
193 4         1560 ok ! defined($r->end_id), 'no end id';
194 4         1681 $p = $r->properties;
195 4         35 is ref($p), 'HASH', 'empty props ref';
196 4         1718 is scalar(keys %$p), 0, 'empty props empty';
197 4         1736 is_deeply [$r->get('whatever')], [undef], 'prop undef';
198 4         2706 ok ! exists $r->properties->{whatever}, 'prop remains non-existent';
199            
200             # element ID
201 4         1636 my $both = {
202             element_id => 'e60', id => 60,
203             start_element_id => 'e61', start_id => 61,
204             end_element_id => 'e62', end_id => 62,
205             };
206 4         24 _element_id_test($both, $id_only, $new, $rel_class, '');
207 4         20336 _element_id_test($both, $id_only, $new, $rel_class, 'start_');
208 4         23644 _element_id_test($both, $id_only, $new, $rel_class, 'end_');
209            
210 4         23563 ok $r->DOES('Neo4j::Types::Relationship'), 'does role';
211             }
212              
213              
214             sub neo4j_relationship_ok {
215 4     4 1 14471 my ($class, $new, $name) = @_;
216 4 100       31 $name = "neo4j_relationship_ok '$class'" unless defined $name;
217             _load_module_ok($name, $class) and
218 4 50   4   19 subtest $name, sub { _relationship_test($class, $new) };
  4         4775  
219             }
220              
221              
222             sub _path_test {
223 3     3   11 my ($path_class, $new) = @_;
224            
225 3         17 plan tests => 3 + 3 + 6 + 6 + 1;
226            
227 3         3213 my (@p, $p, @e);
228            
229             my $new_path = sub {
230 9     9   21 my $i = 0;
231 9 100       23 map { my $o = $_; bless \$o, 'Test::Neo4j::Types::Path' . ($i++ & 1 ? 'Rel' : 'Node') } @_;
  27         42  
  27         181  
232 3         25 };
233            
234 3         13 @p = $new_path->( \6, \7, \8 );
235 3         23 $p = $new->($path_class, { elements => \@p });
236 3         46 @e = $p->elements;
237 3         97 is_deeply [@e], [@p], 'deeply elements 3';
238 3         5795 @e = $p->nodes;
239 3         52 is_deeply [@e], [$p[0],$p[2]], 'deeply nodes 2';
240 3         4158 @e = $p->relationships;
241 3         53 is_deeply [@e], [$p[1]], 'deeply rel 1';
242            
243 3         3772 @p = $new_path->( \9 );
244 3         25 $p = $new->($path_class, { elements => \@p });
245 3         52 @e = $p->elements;
246 3         61 is_deeply [@e], [@p], 'deeply elements 1';
247 3         3039 @e = $p->nodes;
248 3         38 is_deeply [@e], [$p[0]], 'deeply nodes 1';
249 3         2910 @e = $p->relationships;
250 3         38 is_deeply [@e], [], 'deeply rel 0';
251            
252 3         2506 @p = $new_path->( \1, \2, \3, \4, \5 );
253 3         21 $p = $new->($path_class, { elements => \@p });
254 3         35 @e = $p->elements;
255 3         66 is_deeply [@e], [@p], 'deeply elements 5';
256 3     3   6237 lives_and { is scalar($p->elements), 5 } 'scalar context elements';
  3         72  
257 3         1784 @e = $p->nodes;
258 3         93 is_deeply [@e], [$p[0],$p[2],$p[4]], 'deeply nodes 3';
259 3     3   4125 lives_and { is scalar($p->nodes), 3 } 'scalar context nodes';
  3         63  
260 3         1733 @e = $p->relationships;
261 3         48 is_deeply [@e], [$p[1],$p[3]], 'deeply rel 2';
262 3     3   3515 lives_and { is scalar($p->relationships), 2 } 'scalar context relationships';
  3         59  
263            
264 3         1734 $p = $new->($path_class, { elements => [] });
265 3         53 @e = $p->elements;
266 3         36 is scalar(@e), 0, 'no elements gigo';
267 3     3   1711 lives_and { is scalar($p->elements), 0 } 'scalar context no elements';
  3         60  
268 3         1719 @e = $p->nodes;
269 3         33 is scalar(@e), 0, 'no nodes 0 gigo';
270 3     3   1611 lives_and { is scalar($p->nodes), 0 } 'scalar context no nodes';
  3         57  
271 3         1689 @e = $p->relationships;
272 3         34 is scalar(@e), 0, 'no relationships 0 gigo';
273 3     3   1603 lives_and { is scalar($p->relationships), 0 } 'scalar context no relationships';
  3         58  
274            
275 3         1747 ok $p->DOES('Neo4j::Types::Path'), 'does role';
276             }
277              
278              
279             sub neo4j_path_ok {
280 3     3 1 18976 my ($class, $new, $name) = @_;
281 3 100       18 $name = "neo4j_path_ok '$class'" unless defined $name;
282             _load_module_ok($name, $class) and
283 3 50   3   17 subtest $name, sub { _path_test($class, $new) };
  3         8068  
284             }
285              
286              
287             sub _point_test {
288 3     3   9 my ($point_class, $new) = @_;
289            
290 3         16 plan tests => 3+3 + 3+3+3+3+2 + 1;
291            
292 3         3206 my (@c, $p);
293            
294            
295             # Simple point, location in real world
296 3         15 @c = ( 2.294, 48.858, 396 );
297 3         28 $p = $new->( $point_class, { srid => 4979, coordinates => [@c] });
298 3         53 is $p->srid(), 4979, 'eiffel srid';
299 3         1663 is_deeply [$p->coordinates], [@c], 'eiffel coords';
300 3         2894 is scalar ($p->coordinates), 3, 'scalar context eiffel coords';
301            
302 3         1554 @c = ( 2.294, 48.858 );
303 3         22 $p = $new->( $point_class, { srid => 4326, coordinates => [@c] });
304 3         56 is $p->srid(), 4326, 'eiffel 2d srid';
305 3         1667 is_deeply [$p->coordinates], [@c], 'eiffel 2d coords';
306 3         2961 is scalar ($p->coordinates), 2, 'scalar context eiffel 2d coords';
307            
308            
309             # Other SRSs, location not in real world
310 3         1730 @c = ( 12, 34 );
311 3         74 $p = $new->( $point_class, { srid => 7203, coordinates => [@c] });
312 3         43 is $p->srid(), 7203, 'plane srid';
313 3         3020 is_deeply [$p->coordinates], [@c], 'plane coords';
314 3         2623 is scalar ($p->coordinates), 2, 'scalar context plane coords';
315            
316 3         1455 @c = ( 56, 78, 90 );
317 3         20 $p = $new->( $point_class, { srid => 9157, coordinates => [@c] });
318 3         37 is $p->srid(), 9157, 'space srid';
319 3         1428 is_deeply [$p->coordinates], [@c], 'space coords';
320 3         2320 is scalar ($p->coordinates), 3, 'scalar context space coords';
321            
322 3         1400 @c = ( 361, -91 );
323 3         52 $p = $new->( $point_class, { srid => 4326, coordinates => [@c] });
324 3         39 is $p->srid(), 4326, 'ootw srid';
325 3         1448 is_deeply [$p->coordinates], [@c], 'ootw coords';
326 3         2155 is scalar ($p->coordinates), 2, 'scalar context ootw coords';
327            
328 3         1366 @c = ( 'what', 'ever' );
329 3         20 $p = $new->( $point_class, { srid => '4326', coordinates => [@c] });
330 3         45 is $p->srid(), '4326', 'string srid';
331 3         1442 is_deeply [$p->coordinates], [@c], 'string coords';
332 3         1966 is scalar ($p->coordinates), 2, 'scalar context string coords';
333            
334 3         1353 @c = ( undef, 45 );
335 3         20 $p = $new->( $point_class, { srid => 7203, coordinates => [@c] });
336 3         32 is_deeply [$p->coordinates], [@c], 'undef coord';
337 3         2053 is scalar ($p->coordinates), 2, 'scalar context undef coord';
338            
339            
340 3         1288 ok $p->DOES('Neo4j::Types::Point'), 'does role';
341             }
342              
343              
344             sub neo4j_point_ok {
345 3     3 1 10962 my ($class, $new, $name) = @_;
346 3 100       21 $name = "neo4j_point_ok '$class'" unless defined $name;
347             _load_module_ok($name, $class) and
348 3 50   3   18 subtest $name, sub { _point_test($class, $new) };
  3         5147  
349             }
350              
351              
352             sub _datetime_test {
353 2     2   5 my ($datetime_class, $new) = @_;
354            
355 2         7 plan tests => 9 * 7 + 1;
356            
357 2         1426 my ($dt, $p, $type);
358            
359 2         5 $type = 'DATE';
360 2         12 $dt = $new->($datetime_class, $p = {
361             days => 18645, # 2021-01-18
362             });
363 2         31 is $dt->days, $p->{days}, 'date: days';
364 2         858 is $dt->epoch, 1610928000, 'date: epoch';
365 2         844 is $dt->nanoseconds, $p->{nanoseconds}, 'date: no nanoseconds';
366 2         919 is $dt->seconds, $p->{seconds}, 'date: no seconds';
367 2         936 is $dt->type, $type, 'date: type';
368 2         840 is $dt->tz_name, $p->{tz_name}, 'date: no tz_name';
369 2         894 is $dt->tz_offset, $p->{tz_offset}, 'date: no tz_offset';
370            
371 2         906 $type = 'LOCAL TIME';
372 2         10 $dt = $new->($datetime_class, $p = {
373             nanoseconds => 1,
374             });
375 2         30 is $dt->days, $p->{days}, 'local time: no days';
376 2         899 is $dt->epoch, 0, 'local time: epoch';
377 2         813 is $dt->nanoseconds, $p->{nanoseconds}, 'local time: nanoseconds';
378 2         839 is $dt->seconds, 0, 'local time: seconds';
379 2         755 is $dt->type, $type, 'local time: type';
380 2         790 is $dt->tz_name, $p->{tz_name}, 'local time: no tz_name';
381 2         934 is $dt->tz_offset, $p->{tz_offset}, 'local time: no tz_offset';
382            
383 2         907 $type = 'ZONED TIME';
384 2         13 $dt = $new->($datetime_class, $p = {
385             seconds => 86340, # 23:59
386             nanoseconds => 5e8, # 0.5 s
387             tz_offset => -28800, # -8 h
388             });
389 2         20 is $dt->days, $p->{days}, 'zoned time: no days';
390 2         882 is $dt->epoch, 86340, 'zoned time: epoch';
391 2         836 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned time: nanoseconds';
392 2         817 is $dt->seconds, $p->{seconds}, 'zoned time: seconds';
393 2         1328 is $dt->type, $type, 'zoned time: type';
394 2         845 is $dt->tz_name, 'Etc/GMT+8', 'zoned time: tz_name';
395 2         1567 is $dt->tz_offset, $p->{tz_offset}, 'zoned time: tz_offset';
396            
397 2         917 $type = 'LOCAL DATETIME';
398 2         14 $dt = $new->($datetime_class, $p = {
399             days => -1,
400             seconds => 86399,
401             });
402 2         22 is $dt->days, $p->{days}, 'local datetime: days';
403 2         1136 is $dt->epoch, -1, 'local datetime: epoch';
404 2         1130 is $dt->nanoseconds, 0, 'local datetime: nanoseconds';
405 2         1070 is $dt->seconds, $p->{seconds}, 'local datetime: seconds';
406 2         1096 is $dt->type, $type, 'local datetime: type';
407 2         1036 is $dt->tz_name, $p->{tz_name}, 'local datetime: no tz_name';
408 2         1360 is $dt->tz_offset, $p->{tz_offset}, 'local datetime: no tz_offset';
409            
410 2         1309 $type = 'ZONED DATETIME';
411 2         20 $dt = $new->($datetime_class, $p = {
412             days => 7252, # 1989-11-09
413             seconds => 61043, # 17:57:23 UTC
414             nanoseconds => 0,
415             tz_offset => 5400, # +1.5 h
416             });
417 2         27 is $dt->days, $p->{days}, 'zoned datetime (offset): days';
418 2         1186 is $dt->epoch, 626633843, 'zoned datetime (offset): epoch';
419 2         1161 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned datetime (offset): nanoseconds';
420 2         1150 is $dt->seconds, $p->{seconds}, 'zoned datetime (offset): seconds';
421 2         1130 is $dt->type, $type, 'zoned datetime (offset): type';
422 2         1102 is $dt->tz_name, undef, 'zoned datetime (half hour offset): no tz_name';
423 2         1291 is $dt->tz_offset, $p->{tz_offset}, 'zoned datetime (offset): tz_offset';
424            
425 2         1085 $dt = $new->($datetime_class, $p = {
426             days => 6560, # 1987-12-18
427             seconds => 72000, # 20:00 UTC
428             nanoseconds => 0,
429             tz_name => 'America/Los_Angeles',
430             });
431 2         28 is $dt->days, $p->{days}, 'zoned datetime: days';
432 2         1078 is $dt->epoch, 566856000, 'zoned datetime: epoch';
433 2         1088 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned datetime: nanoseconds';
434 2         1079 is $dt->seconds, $p->{seconds}, 'zoned datetime: seconds';
435 2         1126 is $dt->type, $type, 'zoned datetime: type';
436 2         1134 is $dt->tz_name, $p->{tz_name}, 'zoned datetime: tz_name';
437 2         1136 is $dt->tz_offset, $p->{tz_offset}, 'zoned datetime: no tz_offset';
438            
439 2         1231 $dt = $new->($datetime_class, $p = {
440             days => 0,
441             seconds => 0,
442             tz_offset => 0, # GMT
443             });
444 2         29 is $dt->days, 0, 'zoned datetime (zero offset): days';
445 2         1109 is $dt->epoch, 0, 'zoned datetime (zero offset): epoch';
446 2         1088 is $dt->nanoseconds, 0, 'zoned datetime (zero offset): nanoseconds';
447 2         1095 is $dt->seconds, 0, 'zoned datetime (zero offset): seconds';
448 2         1075 is $dt->type, $type, 'zoned datetime (zero offset): type';
449 2         1085 like $dt->tz_name, qr<^Etc/GMT(?:[-+]0)?$>, 'zoned datetime (zero offset): tz_name';
450 2         1070 is $dt->tz_offset, 0, 'zoned datetime (zero offset): tz_offset';
451            
452 2         1135 $dt = $new->($datetime_class, $p = {
453             days => 0,
454             seconds => 0,
455             tz_offset => 86400, # Zone Etc/GMT-24 doesn't exist
456             });
457 2         28 is $dt->days, 0, 'zoned datetime (too high offset): days';
458 2         1085 is $dt->epoch, 0, 'zoned datetime (too high offset): epoch';
459 2         1107 is $dt->nanoseconds, 0, 'zoned datetime (too high offset): nanoseconds';
460 2         1096 is $dt->seconds, 0, 'zoned datetime (too high offset): seconds';
461 2         1169 is $dt->type, $type, 'zoned datetime (too high offset): type';
462 2         1116 is $dt->tz_name, undef, 'zoned datetime (too high offset): no tz_name';
463 2         1207 is $dt->tz_offset, 86400, 'zoned datetime (too high offset): tz_offset';
464            
465 2         1019 $dt = $new->($datetime_class, $p = {
466             days => 0,
467             nanoseconds => 0,
468             tz_offset => -72000, # Zone Etc/GMT+20 doesn't exist
469             });
470 2         27 is $dt->days, 0, 'zoned datetime (too low offset): days';
471 2         1044 is $dt->epoch, 0, 'zoned datetime (too low offset): epoch';
472 2         1078 is $dt->nanoseconds, 0, 'zoned datetime (too low offset): nanoseconds';
473 2         1132 is $dt->seconds, 0, 'zoned datetime (too low offset): seconds';
474 2         1047 is $dt->type, $type, 'zoned datetime (too low offset): type';
475 2         1004 is $dt->tz_name, undef, 'zoned datetime (too low offset): no tz_name';
476 2         1349 is $dt->tz_offset, -72000, 'zoned datetime (too low offset): tz_offset';
477            
478 2         1095 ok $dt->DOES('Neo4j::Types::DateTime'), 'does role';
479             }
480              
481              
482             sub neo4j_datetime_ok {
483 2     2 1 5518 my ($class, $new, $name) = @_;
484 2 100       11 $name = "neo4j_datetime_ok '$class'" unless defined $name;
485             _load_module_ok($name, $class) and
486 2 50   2   9 subtest $name, sub { _datetime_test($class, $new) };
  2         2058  
487             }
488              
489              
490             sub _duration_test {
491 2     2   7 my ($duration_class, $new) = @_;
492            
493 2         11 plan tests => 2 * 4 + 1;
494            
495 2         2188 my $d;
496            
497             # Whether ISO 8601 allows negative quantities isn't entirely clear.
498             # But it does seem to make sense to allow them.
499             # However, the Neo4j server may have bugs related to this;
500             # e. g. in Neo4j 5.6, duration({months: 1, days: -1}) yields P29D,
501             # which is definitely wrong: A month must not be assumed to have a
502             # length of any particular number of days, therefore subtracting
503             # one day from a duration never changes the months count.
504            
505 2         19 $d = $new->($duration_class, {
506             months => 18,
507             seconds => -172800,
508             });
509 2         37 is $d->months, 18, 'months';
510 2         1137 is $d->days, 0, 'no days yields zero';
511 2         1058 is $d->seconds, -172800, 'seconds';
512 2         1285 is $d->nanoseconds, 0, 'no nanoseconds yields zero';
513            
514 2         1221 $d = $new->($duration_class, {
515             days => -42,
516             nanoseconds => 2000,
517             });
518 2         42 is $d->months, 0, 'no months yields zero';
519 2         1307 is $d->days, -42, 'days';
520 2         1190 is $d->seconds, 0, 'no seconds yields zero';
521 2         1550 is $d->nanoseconds, 2000, 'nanoseconds';
522            
523 2         1521 ok $d->DOES('Neo4j::Types::Duration'), 'does role';
524             }
525              
526              
527             sub neo4j_duration_ok {
528 2     2 1 13241 my ($class, $new, $name) = @_;
529 2 100       13 $name = "neo4j_duration_ok '$class'" unless defined $name;
530             _load_module_ok($name, $class) and
531 2 50   2   12 subtest $name, sub { _duration_test($class, $new) };
  2         2992  
532             }
533              
534              
535             package Test::Neo4j::Types::PathNode;
536             $Test::Neo4j::Types::PathNode::VERSION = '0.05';
537 24     24   152 sub DOES { $_[1] eq 'Neo4j::Types::Node' }
538              
539              
540             package Test::Neo4j::Types::PathRel;
541             $Test::Neo4j::Types::PathRel::VERSION = '0.05';
542 12     12   44 sub DOES { $_[1] eq 'Neo4j::Types::Relationship' }
543              
544              
545             1;