File Coverage

lib/Test/Neo4j/Types.pm
Criterion Covered Total %
statement 370 370 100.0
branch 39 40 100.0
condition n/a
subroutine 48 48 100.0
pod 7 7 100.0
total 464 465 100.0


line stmt bran cond sub pod time code
1 4     4   587575 use v5.10.1;
  4         15  
2 4     4   25 use strict;
  4         9  
  4         163  
3 4     4   23 use warnings;
  4         24  
  4         559  
4              
5             package Test::Neo4j::Types;
6             # ABSTRACT: Tools for testing Neo4j type modules
7             $Test::Neo4j::Types::VERSION = '2.00';
8              
9 4     4   27 use Test::More 0.94;
  4         75  
  4         33  
10 4     4   3965 use Test::Exception;
  4         18043  
  4         16  
11 4     4   3951 use Test::Warnings qw(warnings :no_end_test);
  4         14454  
  4         30  
12              
13 4     4   825 use Exporter 'import';
  4         8  
  4         295  
14 4     4   210 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             neo4j_bytearray_ok
22             )}
23              
24             BEGIN {
25             # Workaround for warnings::register_categories() being unavailable
26             # in Perl v5.12 and earlier
27             package # private
28             Neo4j::Types;
29 4     4   28 require warnings::register;
30 4         444 warnings::register->import;
31             }
32              
33              
34             sub _load_module_ok {
35 21     21   67 my ($name, $package) = @_;
36            
37             # We want the test to fail if the module hasn't been loaded, but the
38             # error message you get normally isn't very helpful. So this sub will
39             # check if the module is loaded and return true if that's the case.
40             # Otherwise, it will try to load the module. No eval means that if
41             # loading fails, users get the original error message. If loading
42             # succeeds, we fail the test anyway because the user is supposed to
43             # load the module (checking for this can detect bugs where the
44             # user expects their code to load the module, but it actually
45             # doesn't get loaded).
46             {
47             # Look for entries in the package's symbol table
48 4     4   24 no strict 'refs';
  4         7  
  4         2241  
  21         37  
49 21 100       31 return 1 if keys %{"${package}::"};
  21         327  
50             }
51 7         25 diag "$package is not loaded";
52 7         45 $package =~ s<::>g;
53 7         2544 require "$package.pm";
54 7         924 fail $name;
55 7         3588 return 0;
56             }
57              
58              
59             sub _element_id_test {
60 12     12   62 my ($BOTH, $ID_ONLY, $new, $class, $prefix) = @_;
61            
62             subtest "${prefix}element_id", sub {
63 12     12   33729 plan tests => 6;
64            
65 12         17795 my $both = $new->($class, {%$BOTH});
66 12         316 my $id_only = $new->($class, {%$ID_ONLY});
67 12 100       344 lives_ok { $both->element_id } 'optional op element_id' if $both->can('element_id');
  4         240  
68 12 100       2742 dies_ok { $both->element_id } 'optional op element_id' if ! $both->can('element_id');
  8         737  
69             SKIP: {
70 12 100       4757 skip 'optional op element_id unimplemented', 2+3 unless $class->can('element_id');
  12         159  
71 4         17 my ($element_id, $id) = map { "$prefix$_" } qw( element_id id );
  8         34  
72            
73             # When both IDs are present, id() MAY warn
74 4         28 is $both->$element_id(), $BOTH->{$element_id}, "$element_id";
75 4         2526 warnings { is $both->$id(), $BOTH->{$id}, "legacy $id" };
  4         107  
76            
77             # For a missing element ID, element_id() returns the numeric ID and MUST warn
78 4         2728 my @w_eid = warnings { is $id_only->$element_id(), $ID_ONLY->{$id}, "no $element_id with legacy $id" };
  4         77  
79 4         2572 ok @w_eid, "no $element_id warns";
80 4 50       2245 warn @w_eid if @w_eid > 1; # uncoverable branch true
81 4     4   32 no warnings 'Neo4j::Types';
  4         6  
  4         22043  
82 4         44 ok 1 + warnings { $id_only->$element_id() } == @w_eid, "no $element_id warn cat is Neo4j::Types";
  4         125  
83             };
84 12         174 };
85             }
86              
87              
88             sub _node_test {
89 3     3   13 my ($node_class, $new) = @_;
90            
91 3         19 plan tests => 12 + 5 + 7 + 1 + 1;
92            
93 3         3324 my ($n, @l, $p);
94            
95 3         71 $n = $new->($node_class, my $id_only = {
96             id => 42,
97             labels => ['Foo', 'Bar'],
98             properties => { foofoo => 11, barbar => 22, '123' => [1, 2, 3] },
99             });
100 3         97 is $n->id(), 42, 'id';
101 3         1939 @l = $n->labels;
102 3         28 is scalar(@l), 2, 'label count';
103 3         1710 is $l[0], 'Foo', 'label Foo';
104 3         1623 is $l[1], 'Bar', 'label Bar';
105 3     3   1705 lives_and { is scalar($n->labels), 2 } 'scalar context';
  3         124  
106 3         1760 is $n->get('foofoo'), 11, 'get foofoo';
107 3         1699 is $n->get('barbar'), 22, 'get barbar';
108 3         1700 is_deeply $n->get('123'), [1, 2, 3], 'get 123';
109 3         3213 $p = $n->properties;
110 3         60 is ref($p), 'HASH', 'props ref';
111 3         1758 is $p->{foofoo}, 11, 'props foofoo';
112 3         1645 is $p->{barbar}, 22, 'props barbar';
113 3         1634 is_deeply $p->{123}, [1, 2, 3], 'props 123';
114            
115 3         4591 $n = $new->($node_class, {
116             id => 0,
117             properties => { '0' => [] },
118             });
119 3         82 is $n->id(), 0, 'id 0';
120 3         1634 is ref($n->get('0')), 'ARRAY', 'get 0 ref';
121 3     3   1630 lives_and { is scalar(@{$n->get('0')}), 0 } 'get 0 empty';
  3         62  
  3         15  
122 3         1795 $p = $n->properties;
123 3         31 is_deeply $p, {0=>[]}, 'props deeply';
124 3         3382 is_deeply [$n->properties], [{0=>[]}], 'props list context';
125            
126 3         4061 $n = $new->($node_class, { });
127 3         71 ok ! defined($n->id), 'id gigo';
128 3         1659 @l = $n->labels;
129 3         31 is scalar(@l), 0, 'no labels';
130 3     3   1667 lives_and { is scalar($n->labels), 0 } 'scalar context no labels';
  3         68  
131 3         1653 $p = $n->properties;
132 3         30 is ref($p), 'HASH', 'empty props ref';
133 3         1644 is scalar(keys %$p), 0, 'empty props empty';
134 3         1693 is_deeply [$n->get('whatever')], [undef], 'prop undef';
135 3         2912 ok ! exists $n->properties->{whatever}, 'prop remains non-existent';
136            
137             # element ID
138 3         1595 my $both = { element_id => 'e17', id => 17 };
139 3         20 _element_id_test($both, $id_only, $new, $node_class, '');
140            
141 3         20553 isa_ok $n, 'Neo4j::Types::Node';
142             }
143              
144              
145             sub neo4j_node_ok {
146 4     4 1 908953 my ($class, $new, $name) = @_;
147 4 100       49 $name = "neo4j_node_ok '$class'" unless defined $name;
148             _load_module_ok($name, $class) and
149 4 100   3   22 subtest $name, sub { _node_test($class, $new) };
  3         4896  
150             }
151              
152              
153             sub _relationship_test {
154 3     3   12 my ($rel_class, $new) = @_;
155            
156 3         20 plan tests => 11 + 5 + 8 + 3 + 1;
157            
158 3         3339 my ($r, $p);
159            
160 3         52 $r = $new->($rel_class, my $id_only = {
161             id => 55,
162             type => 'TEST',
163             start_id => 34,
164             end_id => 89,
165             properties => { foo => 144, bar => 233, '358' => [3, 5, 8] },
166             });
167 3         141 is $r->id, 55, 'id';
168 3         3550 is $r->type, 'TEST', 'type';
169 3         1780 is $r->start_id, 34, 'start id';
170 3         1686 is $r->end_id, 89, 'end id';
171 3         1733 is $r->get('foo'), 144, 'get foo';
172 3         2962 is $r->get('bar'), 233, 'get bar';
173 3         3457 is_deeply $r->get('358'), [3, 5, 8], 'get 358';
174 3         4651 $p = $r->properties;
175 3         27 is ref($p), 'HASH', 'props ref';
176 3         1803 is $p->{foo}, 144, 'props foo';
177 3         1695 is $p->{bar}, 233, 'props bar';
178 3         1663 is_deeply $p->{358}, [3, 5, 8], 'props 358';
179            
180 3         2819 $r = $new->($rel_class, {
181             id => 0,
182             properties => { '0' => [] },
183             });
184 3         112 is $r->id(), 0, 'id 0';
185 3         1685 is ref($r->get('0')), 'ARRAY', 'get 0 ref';
186 3     3   1678 lives_and { is scalar(@{$r->get('0')}), 0 } 'get 0 empty';
  3         98  
  3         19  
187 3         1815 $p = $r->properties;
188 3         31 is_deeply $p, {0=>[]}, 'props deeply';
189 3         3395 is_deeply [$r->properties], [{0=>[]}], 'props list context';
190            
191 3         3890 $r = $new->($rel_class, { });
192 3         104 ok ! defined($r->id), 'id gigo';
193 3         1623 ok ! defined($r->type), 'no type';
194 3         1573 ok ! defined($r->start_id), 'no start id';
195 3         1677 ok ! defined($r->end_id), 'no end id';
196 3         1596 $p = $r->properties;
197 3         108 is ref($p), 'HASH', 'empty props ref';
198 3         1743 is scalar(keys %$p), 0, 'empty props empty';
199 3         1767 is_deeply [$r->get('whatever')], [undef], 'prop undef';
200 3         2744 ok ! exists $r->properties->{whatever}, 'prop remains non-existent';
201            
202             # element ID
203 3         1666 my $both = {
204             element_id => 'e60', id => 60,
205             start_element_id => 'e61', start_id => 61,
206             end_element_id => 'e62', end_id => 62,
207             };
208 3         40 _element_id_test($both, $id_only, $new, $rel_class, '');
209 3         18866 _element_id_test($both, $id_only, $new, $rel_class, 'start_');
210 3         19903 _element_id_test($both, $id_only, $new, $rel_class, 'end_');
211            
212 3         20178 isa_ok $r, 'Neo4j::Types::Relationship';
213             }
214              
215              
216             sub neo4j_relationship_ok {
217 4     4 1 18095 my ($class, $new, $name) = @_;
218 4 100       23 $name = "neo4j_relationship_ok '$class'" unless defined $name;
219             _load_module_ok($name, $class) and
220 4 100   3   21 subtest $name, sub { _relationship_test($class, $new) };
  3         4782  
221             }
222              
223              
224             sub _path_test {
225 2     2   9 my ($path_class, $new) = @_;
226            
227 2         13 plan tests => 3 + 3 + 6 + 6 + 1;
228            
229 2         2148 my (@p, $p, @e);
230            
231             my $new_path = sub {
232 6     6   16 my $i = 0;
233 6 100       18 map { my $o = $_; bless \$o, 'Test::Neo4j::Types::Path' . ($i++ & 1 ? 'Rel' : 'Node') } @_;
  18         29  
  18         109  
234 2         18 };
235            
236 2         10 @p = $new_path->( \6, \7, \8 );
237 2         16 $p = $new->($path_class, { elements => \@p });
238 2         79 @e = $p->elements;
239 2         44 is_deeply [@e], [@p], 'deeply elements 3';
240 2         2945 @e = $p->nodes;
241 2         18 is_deeply [@e], [$p[0],$p[2]], 'deeply nodes 2';
242 2         2341 @e = $p->relationships;
243 2         20 is_deeply [@e], [$p[1]], 'deeply rel 1';
244            
245 2         2076 @p = $new_path->( \9 );
246 2         15 $p = $new->($path_class, { elements => \@p });
247 2         53 @e = $p->elements;
248 2         128 is_deeply [@e], [@p], 'deeply elements 1';
249 2         2068 @e = $p->nodes;
250 2         17 is_deeply [@e], [$p[0]], 'deeply nodes 1';
251 2         2001 @e = $p->relationships;
252 2         18 is_deeply [@e], [], 'deeply rel 0';
253            
254 2         1646 @p = $new_path->( \1, \2, \3, \4, \5 );
255 2         16 $p = $new->($path_class, { elements => \@p });
256 2         59 @e = $p->elements;
257 2         215 is_deeply [@e], [@p], 'deeply elements 5';
258 2     2   3326 lives_and { is scalar($p->elements), 5 } 'scalar context elements';
  2         49  
259 2         1210 @e = $p->nodes;
260 2         22 is_deeply [@e], [$p[0],$p[2],$p[4]], 'deeply nodes 3';
261 2     2   2762 lives_and { is scalar($p->nodes), 3 } 'scalar context nodes';
  2         42  
262 2         1140 @e = $p->relationships;
263 2         23 is_deeply [@e], [$p[1],$p[3]], 'deeply rel 2';
264 2     2   2410 lives_and { is scalar($p->relationships), 2 } 'scalar context relationships';
  2         43  
265            
266 2         1225 $p = $new->($path_class, { elements => [] });
267 2         40 @e = $p->elements;
268 2         22 is scalar(@e), 0, 'no elements gigo';
269 2     2   1159 lives_and { is scalar($p->elements), 0 } 'scalar context no elements';
  2         44  
270 2         1161 @e = $p->nodes;
271 2         17 is scalar(@e), 0, 'no nodes 0 gigo';
272 2     2   1112 lives_and { is scalar($p->nodes), 0 } 'scalar context no nodes';
  2         44  
273 2         1209 @e = $p->relationships;
274 2         17 is scalar(@e), 0, 'no relationships 0 gigo';
275 2     2   1134 lives_and { is scalar($p->relationships), 0 } 'scalar context no relationships';
  2         112  
276            
277 2         1293 isa_ok $p, 'Neo4j::Types::Path';
278             }
279              
280              
281             sub neo4j_path_ok {
282 3     3 1 16424 my ($class, $new, $name) = @_;
283 3 100       19 $name = "neo4j_path_ok '$class'" unless defined $name;
284             _load_module_ok($name, $class) and
285 3 100   2   16 subtest $name, sub { _path_test($class, $new) };
  2         3056  
286             }
287              
288              
289             sub _point_test {
290 2     2   7 my ($point_class, $new) = @_;
291            
292 2         13 plan tests => 3+3 + 3+3+3+3+2 + 1;
293            
294 2         2234 my (@c, $p);
295            
296            
297             # Simple point, location in real world
298 2         12 @c = ( 2.294, 48.858, 396 );
299 2         18 $p = $new->( $point_class, { srid => 4979, coordinates => [@c] });
300 2         26 is $p->srid(), 4979, 'eiffel srid';
301 2         1172 is_deeply [$p->coordinates], [@c], 'eiffel coords';
302 2         1943 is scalar ($p->coordinates), 3, 'scalar context eiffel coords';
303            
304 2         1079 @c = ( 2.294, 48.858 );
305 2         18 $p = $new->( $point_class, { srid => 4326, coordinates => [@c] });
306 2         27 is $p->srid(), 4326, 'eiffel 2d srid';
307 2         1108 is_deeply [$p->coordinates], [@c], 'eiffel 2d coords';
308 2         1946 is scalar ($p->coordinates), 2, 'scalar context eiffel 2d coords';
309            
310            
311             # Other SRSs, location not in real world
312 2         1318 @c = ( 12, 34 );
313 2         21 $p = $new->( $point_class, { srid => 7203, coordinates => [@c] });
314 2         24 is $p->srid(), 7203, 'plane srid';
315 2         1181 is_deeply [$p->coordinates], [@c], 'plane coords';
316 2         1782 is scalar ($p->coordinates), 2, 'scalar context plane coords';
317            
318 2         1122 @c = ( 56, 78, 90 );
319 2         20 $p = $new->( $point_class, { srid => 9157, coordinates => [@c] });
320 2         23 is $p->srid(), 9157, 'space srid';
321 2         1168 is_deeply [$p->coordinates], [@c], 'space coords';
322 2         1840 is scalar ($p->coordinates), 3, 'scalar context space coords';
323            
324 2         1169 @c = ( 361, -91 );
325 2         19 $p = $new->( $point_class, { srid => 4326, coordinates => [@c] });
326 2         22 is $p->srid(), 4326, 'ootw srid';
327 2         1160 is_deeply [$p->coordinates], [@c], 'ootw coords';
328 2         1797 is scalar ($p->coordinates), 2, 'scalar context ootw coords';
329            
330 2         1196 @c = ( 'what', 'ever' );
331 2         19 $p = $new->( $point_class, { srid => '4326', coordinates => [@c] });
332 2         23 is $p->srid(), '4326', 'string srid';
333 2         1163 is_deeply [$p->coordinates], [@c], 'string coords';
334 2         1813 is scalar ($p->coordinates), 2, 'scalar context string coords';
335            
336 2         1161 @c = ( undef, 45 );
337 2         22 $p = $new->( $point_class, { srid => 7203, coordinates => [@c] });
338 2         22 is_deeply [$p->coordinates], [@c], 'undef coord';
339 2         1753 is scalar ($p->coordinates), 2, 'scalar context undef coord';
340            
341            
342 2         1167 isa_ok $p, 'Neo4j::Types::Point';
343             }
344              
345              
346             sub neo4j_point_ok {
347 3     3 1 9673 my ($class, $new, $name) = @_;
348 3 100       20 $name = "neo4j_point_ok '$class'" unless defined $name;
349             _load_module_ok($name, $class) and
350 3 100   2   17 subtest $name, sub { _point_test($class, $new) };
  2         2974  
351             }
352              
353              
354             sub _datetime_test {
355 2     2   6 my ($datetime_class, $new) = @_;
356            
357 2         11 plan tests => 9 * 7 + 1;
358            
359 2         1704 my ($dt, $p, $type);
360            
361 2         5 $type = 'DATE';
362 2         11 $dt = $new->($datetime_class, $p = {
363             days => 18645, # 2021-01-18
364             });
365 2         9 is $dt->days, $p->{days}, 'date: days';
366 2         1161 is $dt->epoch, 1610928000, 'date: epoch';
367 2         1080 is $dt->nanoseconds, $p->{nanoseconds}, 'date: no nanoseconds';
368 2         1218 is $dt->seconds, $p->{seconds}, 'date: no seconds';
369 2         1218 is $dt->type, $type, 'date: type';
370 2         1044 is $dt->tz_name, $p->{tz_name}, 'date: no tz_name';
371 2         1272 is $dt->tz_offset, $p->{tz_offset}, 'date: no tz_offset';
372            
373 2         1268 $type = 'LOCAL TIME';
374 2         17 $dt = $new->($datetime_class, $p = {
375             nanoseconds => 1,
376             });
377 2         9 is $dt->days, $p->{days}, 'local time: no days';
378 2         1301 is $dt->epoch, 0, 'local time: epoch';
379 2         1099 is $dt->nanoseconds, $p->{nanoseconds}, 'local time: nanoseconds';
380 2         1168 is $dt->seconds, 0, 'local time: seconds';
381 2         1201 is $dt->type, $type, 'local time: type';
382 2         1185 is $dt->tz_name, $p->{tz_name}, 'local time: no tz_name';
383 2         1257 is $dt->tz_offset, $p->{tz_offset}, 'local time: no tz_offset';
384            
385 2         1211 $type = 'ZONED TIME';
386 2         19 $dt = $new->($datetime_class, $p = {
387             seconds => 86340, # 23:59
388             nanoseconds => 5e8, # 0.5 s
389             tz_offset => -28800, # -8 h
390             });
391 2         8 is $dt->days, $p->{days}, 'zoned time: no days';
392 2         1416 is $dt->epoch, 86340, 'zoned time: epoch';
393 2         1093 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned time: nanoseconds';
394 2         1055 is $dt->seconds, $p->{seconds}, 'zoned time: seconds';
395 2         1037 is $dt->type, $type, 'zoned time: type';
396 2         1013 is $dt->tz_name, 'Etc/GMT+8', 'zoned time: tz_name';
397 2         1203 is $dt->tz_offset, $p->{tz_offset}, 'zoned time: tz_offset';
398            
399 2         1085 $type = 'LOCAL DATETIME';
400 2         19 $dt = $new->($datetime_class, $p = {
401             days => -1,
402             seconds => 86399,
403             });
404 2         9 is $dt->days, $p->{days}, 'local datetime: days';
405 2         1138 is $dt->epoch, -1, 'local datetime: epoch';
406 2         1108 is $dt->nanoseconds, 0, 'local datetime: nanoseconds';
407 2         1084 is $dt->seconds, $p->{seconds}, 'local datetime: seconds';
408 2         1067 is $dt->type, $type, 'local datetime: type';
409 2         1078 is $dt->tz_name, $p->{tz_name}, 'local datetime: no tz_name';
410 2         1283 is $dt->tz_offset, $p->{tz_offset}, 'local datetime: no tz_offset';
411            
412 2         1273 $type = 'ZONED DATETIME';
413 2         23 $dt = $new->($datetime_class, $p = {
414             days => 7252, # 1989-11-09
415             seconds => 61043, # 17:57:23 UTC
416             nanoseconds => 0,
417             tz_offset => 5400, # +1.5 h
418             });
419 2         11 is $dt->days, $p->{days}, 'zoned datetime (offset): days';
420 2         2864 is $dt->epoch, 626633843, 'zoned datetime (offset): epoch';
421 2         2847 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned datetime (offset): nanoseconds';
422 2         2828 is $dt->seconds, $p->{seconds}, 'zoned datetime (offset): seconds';
423 2         2806 is $dt->type, $type, 'zoned datetime (offset): type';
424 2         2882 is $dt->tz_name, undef, 'zoned datetime (half hour offset): no tz_name';
425 2         2949 is $dt->tz_offset, $p->{tz_offset}, 'zoned datetime (offset): tz_offset';
426            
427 2         2329 $dt = $new->($datetime_class, $p = {
428             days => 6560, # 1987-12-18
429             seconds => 72000, # 20:00 UTC
430             nanoseconds => 0,
431             tz_name => 'America/Los_Angeles',
432             });
433 2         13 is $dt->days, $p->{days}, 'zoned datetime: days';
434 2         1112 is $dt->epoch, 566856000, 'zoned datetime: epoch';
435 2         1082 is $dt->nanoseconds, $p->{nanoseconds}, 'zoned datetime: nanoseconds';
436 2         1351 is $dt->seconds, $p->{seconds}, 'zoned datetime: seconds';
437 2         1024 is $dt->type, $type, 'zoned datetime: type';
438 2         1059 is $dt->tz_name, $p->{tz_name}, 'zoned datetime: tz_name';
439 2         1053 is $dt->tz_offset, $p->{tz_offset}, 'zoned datetime: no tz_offset';
440            
441 2         1245 $dt = $new->($datetime_class, $p = {
442             days => 0,
443             seconds => 0,
444             tz_offset => 0, # GMT
445             });
446 2         10 is $dt->days, 0, 'zoned datetime (zero offset): days';
447 2         1176 is $dt->epoch, 0, 'zoned datetime (zero offset): epoch';
448 2         1164 is $dt->nanoseconds, 0, 'zoned datetime (zero offset): nanoseconds';
449 2         1110 is $dt->seconds, 0, 'zoned datetime (zero offset): seconds';
450 2         1093 is $dt->type, $type, 'zoned datetime (zero offset): type';
451 2         2328 like $dt->tz_name, qr<^Etc/GMT(?:[-+]0)?$>, 'zoned datetime (zero offset): tz_name';
452 2         1071 is $dt->tz_offset, 0, 'zoned datetime (zero offset): tz_offset';
453            
454 2         1177 $dt = $new->($datetime_class, $p = {
455             days => 0,
456             seconds => 0,
457             tz_offset => 86400, # Zone Etc/GMT-24 doesn't exist
458             });
459 2         16 is $dt->days, 0, 'zoned datetime (too high offset): days';
460 2         1065 is $dt->epoch, 0, 'zoned datetime (too high offset): epoch';
461 2         1041 is $dt->nanoseconds, 0, 'zoned datetime (too high offset): nanoseconds';
462 2         1513 is $dt->seconds, 0, 'zoned datetime (too high offset): seconds';
463 2         1227 is $dt->type, $type, 'zoned datetime (too high offset): type';
464 2         1043 is $dt->tz_name, undef, 'zoned datetime (too high offset): no tz_name';
465 2         1784 is $dt->tz_offset, 86400, 'zoned datetime (too high offset): tz_offset';
466            
467 2         1010 $dt = $new->($datetime_class, $p = {
468             days => 0,
469             nanoseconds => 0,
470             tz_offset => -72000, # Zone Etc/GMT+20 doesn't exist
471             });
472 2         11 is $dt->days, 0, 'zoned datetime (too low offset): days';
473 2         1088 is $dt->epoch, 0, 'zoned datetime (too low offset): epoch';
474 2         1053 is $dt->nanoseconds, 0, 'zoned datetime (too low offset): nanoseconds';
475 2         1178 is $dt->seconds, 0, 'zoned datetime (too low offset): seconds';
476 2         853 is $dt->type, $type, 'zoned datetime (too low offset): type';
477 2         1367 is $dt->tz_name, undef, 'zoned datetime (too low offset): no tz_name';
478 2         1626 is $dt->tz_offset, -72000, 'zoned datetime (too low offset): tz_offset';
479            
480 2         1177 isa_ok $dt, 'Neo4j::Types::DateTime';
481             }
482              
483              
484             sub neo4j_datetime_ok {
485 3     3 1 13594 my ($class, $new, $name) = @_;
486 3 100       13 $name = "neo4j_datetime_ok '$class'" unless defined $name;
487             _load_module_ok($name, $class) and
488 3 100   2   15 subtest $name, sub { _datetime_test($class, $new) };
  2         2347  
489             }
490              
491              
492             sub _duration_test {
493 1     1   5 my ($duration_class, $new) = @_;
494            
495 1         10 plan tests => 2 * 4 + 1;
496            
497 1         1257 my $d;
498            
499             # Whether ISO 8601 allows negative quantities isn't entirely clear.
500             # But it does seem to make sense to allow them.
501             # However, the Neo4j server may have bugs related to this;
502             # e. g. in Neo4j 5.6, duration({months: 1, days: -1}) yields P29D,
503             # which is definitely wrong: A month must not be assumed to have a
504             # length of any particular number of days, therefore subtracting
505             # one day from a duration never changes the months count.
506            
507 1         13 $d = $new->($duration_class, {
508             months => 18,
509             seconds => -172800,
510             });
511 1         7 is $d->months, 18, 'months';
512 1         652 is $d->days, 0, 'no days yields zero';
513 1         590 is $d->seconds, -172800, 'seconds';
514 1         580 is $d->nanoseconds, 0, 'no nanoseconds yields zero';
515            
516 1         676 $d = $new->($duration_class, {
517             days => -42,
518             nanoseconds => 2000,
519             });
520 1         8 is $d->months, 0, 'no months yields zero';
521 1         615 is $d->days, -42, 'days';
522 1         574 is $d->seconds, 0, 'no seconds yields zero';
523 1         558 is $d->nanoseconds, 2000, 'nanoseconds';
524            
525 1         544 isa_ok $d, 'Neo4j::Types::Duration';
526             }
527              
528              
529             sub neo4j_duration_ok {
530 2     2 1 9890 my ($class, $new, $name) = @_;
531 2 100       10 $name = "neo4j_duration_ok '$class'" unless defined $name;
532             _load_module_ok($name, $class) and
533 2 100   1   10 subtest $name, sub { _duration_test($class, $new) };
  1         1792  
534             }
535              
536              
537             sub _bytearray_test {
538 1     1   4 my ($bytearray_class, $new) = @_;
539            
540 1         5 plan tests => 1 + 2 + 1;
541            
542 1         1100 my $b;
543            
544 1         8 $b = $new->($bytearray_class, {
545             as_string => 'foo',
546             });
547 1         5 is $b->as_string, 'foo', 'bytes "foo"';
548            
549 1         646 $b = $new->($bytearray_class, {
550             as_string => "\x{100}",
551             });
552 1         9 ok ! utf8::is_utf8($b->as_string), 'wide char bytearray: UTF8 off';
553 1         527 ok length($b->as_string) > 1, 'wide char bytearray: multiple bytes';
554            
555 1         509 isa_ok $b, 'Neo4j::Types::ByteArray';
556             }
557              
558              
559             sub neo4j_bytearray_ok {
560 2     2 1 281749 my ($class, $new, $name) = @_;
561 2 100       8 $name = "neo4j_bytearray_ok '$class'" unless defined $name;
562             _load_module_ok($name, $class) and
563 2 100   1   9 subtest $name, sub { _bytearray_test($class, $new) };
  1         1570  
564             }
565              
566              
567             package Test::Neo4j::Types::PathNode;
568             $Test::Neo4j::Types::PathNode::VERSION = '2.00';
569 4     4   51 use parent 'Neo4j::Types::Node';
  4         8  
  4         33  
570              
571              
572             package Test::Neo4j::Types::PathRel;
573             $Test::Neo4j::Types::PathRel::VERSION = '2.00';
574 4     4   346 use parent 'Neo4j::Types::Relationship';
  4         6  
  4         20  
575              
576              
577             1;