Merge branch 'master' of https://github.com/kivitendo/kivitendo-erp
[kivitendo-erp.git] / t / helper / csv.t
1 use Test::More tests => 71;
2
3 use lib 't';
4 use utf8;
5
6 use Data::Dumper;
7 use Support::TestSetup;
8
9 use_ok 'SL::Helper::Csv';
10
11 Support::TestSetup::login();
12
13 my $csv = SL::Helper::Csv->new(
14   file    => \"Kaffee\n",       # " # make emacs happy
15   header  => [ 'description' ],
16   profile => [{ class  => 'SL::DB::Part', }],
17 );
18
19 isa_ok $csv->_csv, 'Text::CSV_XS';
20 isa_ok $csv->_io, 'IO::File';
21 isa_ok $csv->parse, 'SL::Helper::Csv', 'parsing returns self';
22 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'simple case works';
23
24 is $csv->get_objects->[0]->description, 'Kaffee', 'get_object works';
25 ####
26
27 $::myconfig{numberformat} = '1.000,00';
28 $::myconfig{dateformat} = 'dd.mm.yyyy';
29
30 $csv = SL::Helper::Csv->new(
31   file    => \"Kaffee;0.12;12,2;1,5234\n",            # " # make emacs happy
32   header  => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
33   profile => [{profile => { listprice => 'listprice_as_number' },
34                class   => 'SL::DB::Part',}],
35 );
36 $csv->parse;
37
38 is $csv->get_objects->[0]->sellprice, 0.12, 'numeric attr works';
39 is $csv->get_objects->[0]->lastcost, 12.2, 'attr helper works';
40 is $csv->get_objects->[0]->listprice, 1.5234, 'dispatch works';
41
42 #####
43
44
45 $csv = SL::Helper::Csv->new(
46   file   => \<<EOL,
47 description,sellprice,lastcost_as_number,listprice,
48 Kaffee,0.12,'12,2','1,5234'
49 EOL
50   sep_char => ',',
51   quote_char => "'",
52   profile => [{profile => { listprice => 'listprice_as_number' },
53                class   => 'SL::DB::Part',}]
54 );
55 $csv->parse;
56 is scalar @{ $csv->get_objects }, 1, 'auto header works';
57 is $csv->get_objects->[0]->description, 'Kaffee', 'get_object works on auto header';
58
59 #####
60
61
62 $csv = SL::Helper::Csv->new(
63   file   => \<<EOL,
64 ;;description;sellprice;lastcost_as_number;
65 #####;Puppy;Kaffee;0.12;12,2;1,5234
66 EOL
67   profile => [{class  => 'SL::DB::Part'}],
68 );
69 $csv->parse;
70 is scalar @{ $csv->get_objects }, 1, 'bozo header doesn\'t blow things up';
71
72 #####
73
74 $csv = SL::Helper::Csv->new(
75   file   => \<<EOL,
76 description;partnumber;sellprice;lastcost_as_number;
77 Kaffee;;0.12;12,2;1,5234
78 Beer;1123245;0.12;12,2;1,5234
79 EOL
80   profile => [{class  => 'SL::DB::Part'}],
81 );
82 $csv->parse;
83 is scalar @{ $csv->get_objects }, 2, 'multiple objects work';
84 is $csv->get_objects->[0]->description, 'Kaffee', 'first object';
85 is $csv->get_objects->[1]->partnumber, '1123245', 'second object';
86
87 ####
88
89 $csv = SL::Helper::Csv->new(
90   file   => \<<EOL,
91 description;partnumber;sellprice;lastcost_as_number;
92 Kaffee;;0.12;1,221.52
93 Beer;1123245;0.12;1.5234
94 EOL
95   numberformat => '1,000.00',
96   profile => [{class  => 'SL::DB::Part'}],
97 );
98 $csv->parse;
99 is $csv->get_objects->[0]->lastcost, '1221.52', 'formatnumber';
100
101 ######
102
103 $csv = SL::Helper::Csv->new(
104   file   => \<<EOL,
105 "description;partnumber;sellprice;lastcost_as_number;
106 Kaffee;;0.12;1,221.52
107 Beer;1123245;0.12;1.5234
108 EOL
109 # " # make emacs happy
110   numberformat => '1,000.00',
111   profile => [{class  => 'SL::DB::Part'}],
112 );
113 is $csv->parse, undef, 'broken csv header won\'t get parsed';
114
115 ######
116
117 $csv = SL::Helper::Csv->new(
118   file   => \<<EOL,
119 description;partnumber;sellprice;lastcost_as_number;
120 "Kaf"fee";;0.12;1,221.52
121 Beer;1123245;0.12;1.5234
122 EOL
123 # " # make emacs happy
124   numberformat => '1,000.00',
125   profile => [{class  => 'SL::DB::Part'}],
126 );
127 is $csv->parse, undef, 'broken csv content won\'t get parsed';
128 is_deeply $csv->errors, [ '"Kaf"fee";;0.12;1,221.52'."\n", 2023, 'EIQ - QUO character not allowed', 5, 2 ], 'error';
129 isa_ok( ($csv->errors)[0], 'SL::Helper::Csv::Error', 'Errors get objectified');
130
131 ####
132
133 $csv = SL::Helper::Csv->new(
134   file   => \<<EOL,
135 description;partnumber;sellprice;lastcost_as_number;wiener;
136 Kaffee;;0.12;1,221.52;ja wiener
137 Beer;1123245;0.12;1.5234;nein kein wieder
138 EOL
139   numberformat => '1,000.00',
140   ignore_unknown_columns => 1,
141   profile => [{class  => 'SL::DB::Part'}],
142 );
143 $csv->parse;
144 is $csv->get_objects->[0]->lastcost, '1221.52', 'ignore_unkown_columns works';
145
146 #####
147
148 $csv = SL::Helper::Csv->new(
149   file   => \<<EOL,
150 description;partnumber;sellprice;lastcost_as_number;buchungsgruppe;
151 Kaffee;;0.12;1,221.52;Standard 7%
152 Beer;1123245;0.12;1.5234;16 %
153 EOL
154   numberformat => '1,000.00',
155   profile => [{
156     profile => {buchungsgruppe => "buchungsgruppen.description"},
157     class  => 'SL::DB::Part',
158   }]
159 );
160 $csv->parse;
161 isa_ok $csv->get_objects->[0]->buchungsgruppe, 'SL::DB::Buchungsgruppe', 'deep dispatch auto vivify works';
162 is $csv->get_objects->[0]->buchungsgruppe->description, 'Standard 7%', '...and gets set correctly';
163
164
165 #####
166
167 $csv = SL::Helper::Csv->new(
168   file   => \<<EOL,
169 description;partnumber;sellprice;lastcost_as_number;make_1;model_1;
170   Kaffee;;0.12;1,221.52;213;Chair 0815
171 Beer;1123245;0.12;1.5234;
172 EOL
173   numberformat => '1,000.00',
174   profile => [{
175     profile => {
176       make_1 => "makemodels.0.make",
177       model_1 => "makemodels.0.model",
178     },
179     class  => 'SL::DB::Part',
180   }],
181 );
182 $csv->parse;
183 my @mm = $csv->get_objects->[0]->makemodel;
184 is scalar @mm,  1, 'one-to-many dispatch';
185 is $csv->get_objects->[0]->makemodels->[0]->model, 'Chair 0815', '... and works';
186
187 #####
188
189
190 $csv = SL::Helper::Csv->new(
191   file   => \<<EOL,
192 description;partnumber;sellprice;lastcost_as_number;make_1;model_1;make_2;model_2;
193  Kaffee;;0.12;1,221.52;213;Chair 0815;523;Table 15
194 EOL
195   numberformat => '1,000.00',
196   profile => [{
197     profile => {
198       make_1 => "makemodels.0.make",
199       model_1 => "makemodels.0.model",
200       make_2 => "makemodels.1.make",
201       model_2 => "makemodels.1.model",
202     },
203     class  => 'SL::DB::Part',
204   }]
205 );
206 $csv->parse;
207
208 print Dumper($csv->errors);
209
210 @mm = $csv->get_objects->[0]->makemodel;
211 is scalar @mm,  1, 'multiple one-to-many dispatch';
212 is $csv->get_objects->[0]->makemodels->[0]->model, 'Chair 0815', '...check 1';
213 is $csv->get_objects->[0]->makemodels->[0]->make, '213', '...check 2';
214 is $csv->get_objects->[0]->makemodels->[1]->model, 'Table 15', '...check 3';
215 is $csv->get_objects->[0]->makemodels->[1]->make, '523', '...check 4';
216
217 ######
218
219 $csv = SL::Helper::Csv->new(
220   file   => \<<EOL,
221 description;partnumber;sellprice;lastcost_as_number;buchungsgruppe;
222 EOL
223   numberformat => '1,000.00',
224   profile => [{
225     profile => {buchungsgruppe => "buchungsgruppen.1.description"},
226     class  => 'SL::DB::Part',
227   }]
228 );
229 is $csv->parse, undef, 'wrong profile gets rejected';
230 is_deeply $csv->errors, [ 'buchungsgruppen.1.description', undef, "Profile path error. Indexed relationship is not OneToMany around here: 'buchungsgruppen.1'", undef ,0 ], 'error indicates wrong header';
231 isa_ok( ($csv->errors)[0], 'SL::Helper::Csv::Error', 'Errors get objectified');
232
233 ####
234
235 $csv = SL::Helper::Csv->new(
236   file   => \<<EOL,
237 description;partnumber;sellprice;lastcost;wiener;
238 Kaffee;;0.12;1,221.52;ja wiener
239 Beer;1123245;0.12;1.5234;nein kein wieder
240 EOL
241   numberformat => '1,000.00',
242   ignore_unknown_columns => 1,
243   strict_profile => 1,
244   profile => [{
245     profile => {lastcost => 'lastcost_as_number'},
246     class  => 'SL::DB::Part',
247   }]
248 );
249 $csv->parse;
250 is $csv->get_objects->[0]->lastcost, '1221.52', 'strict_profile with ignore';
251 is $csv->get_objects->[0]->sellprice, undef,  'strict profile with ignore 2';
252
253 ####
254
255 $csv = SL::Helper::Csv->new(
256   file   => \<<EOL,
257 description;partnumber;sellprice;lastcost;wiener;
258 Kaffee;;0.12;1,221.52;ja wiener
259 Beer;1123245;0.12;1.5234;nein kein wieder
260 EOL
261   numberformat => '1,000.00',
262   strict_profile => 1,
263   profile => [{
264     profile => {lastcost => 'lastcost_as_number'},
265     class  => 'SL::DB::Part',
266   }]
267 );
268 $csv->parse;
269
270 is_deeply( ($csv->errors)[0], [ 'description', undef, 'header field \'description\' is not recognized', undef, 0 ], 'strict_profile without ignore_columns throws error');
271
272 #####
273
274 $csv = SL::Helper::Csv->new(
275   file   => \"Kaffee",       # " # make emacs happy
276   header =>  [ 'description' ],
277   profile => [{class  => 'SL::DB::Part'}],
278 );
279 $csv->parse;
280 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'eol bug at the end of files';
281
282 #####
283
284 $csv = SL::Helper::Csv->new(
285   file   => \"Description\nKaffee",        # " # make emacs happy
286   case_insensitive_header => 1,
287   profile => [{
288     profile => { description => 'description' },
289     class  => 'SL::DB::Part'
290   }],
291 );
292 $csv->parse;
293 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header from csv works';
294
295 #####
296
297 $csv = SL::Helper::Csv->new(
298   file   => \"Kaffee",          # " # make emacs happy
299   header =>  [ 'Description' ],
300   case_insensitive_header => 1,
301   profile => [{
302     profile => { description => 'description' },
303     class  => 'SL::DB::Part'
304   }],
305 );
306 $csv->parse;
307 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header as param works';
308
309 #####
310
311 $csv = SL::Helper::Csv->new(
312   file   => \"\x{EF}\x{BB}\x{BF}description\nKaffee",           # " # make emacs happy
313   profile => [{class  => 'SL::DB::Part'}],
314   encoding => 'utf8',
315 );
316 $csv->parse;
317 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'utf8 BOM works (bug 1872)';
318
319 #####
320
321 $csv = SL::Helper::Csv->new(
322   file   => \"Kaffee",            # " # make emacs happy
323   header => [ 'Description' ],
324   profile => [{class  => 'SL::DB::Part'}],
325 );
326 $csv->parse;
327 is_deeply $csv->get_data, undef, 'case insensitive header without flag ignores';
328
329 #####
330
331 $csv = SL::Helper::Csv->new(
332   file   => \"Kaffee",            # " # make emacs happy
333   header => [ 'foo' ],
334   profile => [{
335     profile => { foo => '' },
336     class  => 'SL::DB::Part',
337   }],
338 );
339 $csv->parse;
340
341 is_deeply $csv->get_data, [ { foo => 'Kaffee' } ], 'empty path still gets parsed into data';
342 ok $csv->get_objects->[0], 'empty path gets ignored in object creation';
343
344 #####
345
346 $csv = SL::Helper::Csv->new(
347   file   => \"Kaffee",            # " # make emacs happy
348   header => [ 'foo' ],
349   strict_profile => 1,
350   profile => [{
351     profile => { foo => '' },
352     class  => 'SL::DB::Part',
353   }],
354 );
355 $csv->parse;
356
357 is_deeply $csv->get_data, [ { foo => 'Kaffee' } ], 'empty path still gets parsed into data (strict profile)';
358 ok $csv->get_objects->[0], 'empty path gets ignored in object creation (strict profile)';
359
360 $csv = SL::Helper::Csv->new(
361   file   => \"Phil",            # " # make emacs happy
362   header => [ 'CVAR_grOUnDHog' ],
363   strict_profile => 1,
364   case_insensitive_header => 1,
365   profile => [{
366     profile => { cvar_Groundhog => '' },
367     class  => 'SL::DB::Part',
368   }],
369
370 );
371 $csv->parse;
372
373 is_deeply $csv->get_data, [ { cvar_Groundhog => 'Phil' } ], 'using empty path to get cvars working';
374 ok $csv->get_objects->[0], '...and not destorying the objects';
375
376 #####
377
378 $csv = SL::Helper::Csv->new(
379   file   => \"description\nKaffee",            # " # make emacs happy
380 );
381 $csv->parse;
382 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'without profile and class works';
383
384 #####
385 $csv = SL::Helper::Csv->new(
386   file    => \"Kaffee;1,50\nSchoke;0,89\n",
387   header  => [
388     [ 'datatype', 'description', 'sellprice' ],
389   ],
390   profile => [
391     { profile   => { sellprice => 'sellprice_as_number' },
392       class     => 'SL::DB::Part',}
393   ],
394 );
395
396 ok $csv->_check_multiplexed, 'multiplex check works on not-multiplexed data';
397 ok !$csv->is_multiplexed, 'not-multiplexed data is recognized';
398
399 #####
400 $csv = SL::Helper::Csv->new(
401   file    => \"P;Kaffee;1,50\nC;Meier\n",
402   header  => [
403     [ 'datatype', 'description', 'listprice' ],
404     [ 'datatype', 'name' ],
405   ],
406   profile => [
407     { profile   => { listprice => 'listprice_as_number' },
408       class     => 'SL::DB::Part',
409       row_ident => 'P' },
410     { class  => 'SL::DB::Customer',
411       row_ident => 'C' }
412   ],
413 );
414
415 ok $csv->_check_multiplexed, 'multiplex check works on multiplexed data';
416 ok $csv->is_multiplexed, 'multiplexed data is recognized';
417
418 #####
419 $csv = SL::Helper::Csv->new(
420   file    => \"P;Kaffee;1,50\nC;Meier\n",
421   header  => [
422     [ 'datatype', 'description', 'listprice' ],
423     [ 'datatype', 'name' ],
424   ],
425   profile => [
426     { profile   => { listprice => 'listprice_as_number' },
427       class     => 'SL::DB::Part', },
428     { class  => 'SL::DB::Customer',
429       row_ident => 'C' }
430   ],
431 );
432
433 ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing row_ident';
434
435 #####
436 $csv = SL::Helper::Csv->new(
437   file    => \"P;Kaffee;1,50\nC;Meier\n",
438   header  => [
439     [ 'datatype', 'description', 'listprice' ],
440     [ 'datatype', 'name' ],
441   ],
442   profile => [
443     { profile   => { listprice => 'listprice_as_number' },
444       row_ident => 'P' },
445     { class  => 'SL::DB::Customer',
446       row_ident => 'C' }
447   ],
448 );
449
450 ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing class';
451
452 #####
453 $csv = SL::Helper::Csv->new(
454   file    => \"P;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
455   header  => [
456     [ 'datatype', 'description', 'listprice' ],
457   ],
458   profile => [
459     { profile   => { listprice => 'listprice_as_number' },
460       class     => 'SL::DB::Part',
461       row_ident => 'P' },
462     { class  => 'SL::DB::Customer',
463       row_ident => 'C' }
464   ],
465 );
466
467 ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing header';
468
469 #####
470
471 $csv = SL::Helper::Csv->new(
472   file    => \"P;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
473   header  => [
474     [ 'datatype', 'description', 'listprice' ],
475     [ 'datatype', 'name' ],
476   ],
477   profile => [
478     { profile   => { listprice => 'listprice_as_number' },
479       class     => 'SL::DB::Part',
480       row_ident => 'P' },
481     { class  => 'SL::DB::Customer',
482       row_ident => 'C' }
483   ],
484   ignore_unknown_columns => 1,
485 );
486
487 $csv->parse;
488 is_deeply $csv->get_data,
489     [ { datatype => 'P', description => 'Kaffee', listprice => '1,50' }, { datatype => 'C', name => 'Meier' } ],
490     'multiplex: simple case works';
491 is scalar @{ $csv->get_objects }, 2, 'multiplex: multiple objects work';
492 is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: first object';
493 is $csv->get_objects->[1]->name,        'Meier',  'multiplex: second object';
494
495 #####
496
497 $csv = SL::Helper::Csv->new(
498   file    => \"datatype;description;listprice\ndatatype;name\nP;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
499   profile => [
500     { profile   => { listprice => 'listprice_as_number' },
501       class     => 'SL::DB::Part',
502       row_ident => 'P' },
503     { class  => 'SL::DB::Customer',
504       row_ident => 'C' }
505   ],
506   ignore_unknown_columns => 1,
507 );
508
509 $csv->parse;
510 is scalar @{ $csv->get_objects }, 2, 'multiplex: auto header works';
511 is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: auto header first object';
512 is $csv->get_objects->[1]->name,        'Meier',  'multiplex: auto header second object';
513
514 ######
515
516 $csv = SL::Helper::Csv->new(
517   file   => \<<EOL,
518 datatype;description
519 "datatype;name
520 P;Kaffee
521 C;Meier
522 P;Beer
523 EOL
524 # " # make emacs happy
525   profile => [
526               {class  => 'SL::DB::Part',     row_ident => 'P'},
527               {class  => 'SL::DB::Customer', row_ident => 'C'},
528              ],
529   ignore_unknown_columns => 1,
530 );
531 is $csv->parse, undef, 'multiplex: broken csv header won\'t get parsed';
532
533 ######
534
535 $csv = SL::Helper::Csv->new(
536   file   => \<<EOL,
537 datatype;description
538 P;Kaffee
539 C;Meier
540 P;Beer
541 EOL
542 # " # make emacs happy
543   profile => [
544               {class  => 'SL::DB::Part',     row_ident => 'P'},
545               {class  => 'SL::DB::Customer', row_ident => 'C'},
546              ],
547   header  => [ [], ['name'] ],
548   ignore_unknown_columns => 1,
549 );
550 ok !$csv->_check_multiplexed, 'multiplex check detects empty header';
551
552 #####
553
554 $csv = SL::Helper::Csv->new(
555   file   => \"Datatype;Description\nDatatype;Name\nP;Kaffee\nC;Meier",        # " # make emacs happy
556   case_insensitive_header => 1,
557   ignore_unknown_columns => 1,
558   profile => [
559     {
560       profile   => { datatype => 'datatype', description => 'description' },
561       class     => 'SL::DB::Part',
562       row_ident => 'P'
563     },
564     {
565       profile   => { datatype => 'datatype', name => 'name' },
566       class     => 'SL::DB::Customer',
567       row_ident => 'C'
568     }
569   ],
570 );
571 $csv->parse;
572 is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
573                             { datatype => 'C', name => 'Meier'} ],
574                           'multiplex: case insensitive header from csv works';
575
576 #####
577
578 $csv = SL::Helper::Csv->new(
579   file   => \"P;Kaffee\nC;Meier",          # " # make emacs happy
580   header =>  [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
581   case_insensitive_header => 1,
582   ignore_unknown_columns => 1,
583   profile => [
584     {
585       profile   => { datatype => 'datatype', description => 'description' },
586       class     => 'SL::DB::Part',
587       row_ident => 'P'
588     },
589     {
590       profile => { datatype => 'datatype', name => 'name' },
591       class  => 'SL::DB::Customer',
592       row_ident => 'C'
593     }
594   ],
595 );
596 $csv->parse;
597 is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
598                             { datatype => 'C', name => 'Meier' } ],
599                           'multiplex: case insensitive header as param works';
600
601
602 #####
603
604 $csv = SL::Helper::Csv->new(
605   file   => \"P;Kaffee\nC;Meier",          # " # make emacs happy
606   header =>  [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
607   profile => [
608     {
609       profile   => { datatype => 'datatype', description => 'description' },
610       class     => 'SL::DB::Part',
611       row_ident => 'P'
612     },
613     {
614       profile => { datatype => 'datatype', name => 'name' },
615       class  => 'SL::DB::Customer',
616       row_ident => 'C'
617     }
618   ],
619 );
620 $csv->parse;
621 is_deeply $csv->get_data, undef, 'multiplex: case insensitive header without flag ignores';
622
623 #####
624
625 $csv = SL::Helper::Csv->new(
626   file   => \<<EOL,
627 P;Kaffee;lecker
628 C;Meier;froh
629 EOL
630 # " # make emacs happy
631   header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
632   profile => [{
633     profile   => { datatype => '', Afoo => '', Abar => '' },
634     class     => 'SL::DB::Part',
635     row_ident => 'P'
636   },
637   {
638     profile   => { datatype => '', Bfoo => '', Bbar => '' },
639     class     => 'SL::DB::Customer',
640     row_ident => 'C'
641   }],
642 );
643 $csv->parse;
644
645 is_deeply $csv->get_data,
646     [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
647     'multiplex: empty path still gets parsed into data';
648 ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation';
649
650 #####
651
652 $csv = SL::Helper::Csv->new(
653   file   => \<<EOL,
654 P;Kaffee;lecker
655 C;Meier;froh
656 EOL
657 # " # make emacs happy
658   header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
659   strict_profile => 1,
660   profile => [{
661     profile   => { datatype => '', Afoo => '', Abar => '' },
662     class     => 'SL::DB::Part',
663     row_ident => 'P'
664   },
665   {
666     profile   => { datatype => '', Bfoo => '', Bbar => '' },
667     class     => 'SL::DB::Customer',
668     row_ident => 'C'
669   }],
670 );
671 $csv->parse;
672
673 is_deeply $csv->get_data,
674     [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
675     'multiplex: empty path still gets parsed into data (strict profile)';
676 ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation (strict profile)';
677
678 #####
679
680
681 # vim: ft=perl
682 # set emacs to perl mode
683 # Local Variables:
684 # mode: perl
685 # End:
686