@@ -389,13 +389,47 @@ def test_render_text_grouping(grouping: str, capsys: pytest.CaptureFixture) -> N
389389 assert "issue" in captured
390390
391391
392- @pytest .mark .ai_generated
393- def test_validate_grouping_severity_cli (simple2_nwb : Path ) -> None :
394- """Test --grouping=severity via CLI."""
395- r = CliRunner ().invoke (validate , ["--grouping=severity" , str (simple2_nwb )])
392+ def _grouping_opts (* groupings : str ) -> list [str ]:
393+ """Build CLI args for one or more -g options."""
394+ opts : list [str ] = []
395+ for g in groupings :
396+ opts .extend (["-g" , g ])
397+ return opts
398+
399+
400+ # simple2_nwb always produces at least this ERROR (not inside a dandiset)
401+ _SIMPLE2_EXPECTED_ID = "DANDI.NO_DANDISET_FOUND"
402+
403+ # Single and composite grouping specs for parametrized tests.
404+ # Each entry is a tuple of grouping levels.
405+ _GROUPING_SPECS : list [tuple [str , ...]] = [
406+ ("severity" ,),
407+ ("id" ,),
408+ ("validator" ,),
409+ ("standard" ,),
410+ ("dandiset" ,),
411+ ("severity" , "id" ),
412+ ("validator" , "severity" ),
413+ ("id" , "validator" ),
414+ ]
415+
416+
417+ @pytest .mark .ai_generated
418+ @pytest .mark .parametrize ("grouping" , _GROUPING_SPECS , ids = lambda g : "+" .join (g ))
419+ def test_validate_grouping_text_cli (
420+ grouping : tuple [str , ...], simple2_nwb : Path
421+ ) -> None :
422+ """Each grouping spec (single or composite) produces section headers and known issues."""
423+ r = CliRunner ().invoke (validate , [* _grouping_opts (* grouping ), str (simple2_nwb )])
396424 assert r .exit_code != 0
397425 assert "===" in r .output
398- assert "ERROR" in r .output
426+ # Both known issues must appear somewhere in output
427+ assert _SIMPLE2_EXPECTED_ID in r .output
428+ # Composite groupings must produce nested (indented) headers
429+ if len (grouping ) > 1 :
430+ lines = r .output .split ("\n " )
431+ inner = [ln for ln in lines if "===" in ln and ln .startswith (" " )]
432+ assert inner , "composite grouping should produce nested headers"
399433
400434
401435@pytest .mark .ai_generated
@@ -438,99 +472,125 @@ def test_render_text_multilevel_grouping(capsys: pytest.CaptureFixture) -> None:
438472 _render_text (issues , grouping = ("severity" , "id" ))
439473 captured = capsys .readouterr ().out
440474
441- # Should have nested headers: severity then id
442475 assert "=== WARNING" in captured
443476 assert "=== ERROR" in captured
444477 assert "=== NWBI.check_data_orientation" in captured
445478 assert "=== NWBI.check_missing_unit" in captured
446- # Nested headers should be indented
447479 lines = captured .split ("\n " )
448- # Find inner headers — they should have leading spaces
449480 inner_headers = [ln for ln in lines if "===" in ln and ln .startswith (" " )]
450- assert len (inner_headers ) >= 2 # at least 2 inner group headers
481+ assert len (inner_headers ) >= 2
451482
452483
453484@pytest .mark .ai_generated
454- def test_validate_multilevel_grouping_text_cli (simple2_nwb : Path ) -> None :
455- """Test -g severity -g id via CLI produces nested headers."""
456- r = CliRunner ().invoke (validate , ["-g" , "severity" , "-g" , "id" , str (simple2_nwb )])
457- assert r .exit_code != 0
458- assert "===" in r .output
459- # Should have nested structure
460- lines = r .output .split ("\n " )
461- inner_headers = [ln for ln in lines if "===" in ln and ln .startswith (" " )]
462- assert len (inner_headers ) >= 1
463-
464-
465- @pytest .mark .ai_generated
466- def test_validate_multilevel_grouping_json_cli (simple2_nwb : Path ) -> None :
467- """Test -g severity -f json_pp via CLI produces nested JSON dict."""
485+ @pytest .mark .parametrize (
486+ "grouping" ,
487+ [
488+ ("path" ,),
489+ ("severity" ,),
490+ ("id" ,),
491+ ("validator" ,),
492+ ("standard" ,),
493+ ("dandiset" ,),
494+ ("severity" , "id" ),
495+ ("validator" , "severity" ),
496+ ],
497+ ids = lambda g : "+" .join (g ),
498+ )
499+ def test_validate_grouping_json_cli (
500+ grouping : tuple [str , ...], simple2_nwb : Path
501+ ) -> None :
502+ """Each grouping spec produces a (nested) dict in JSON output with known issues."""
468503 r = CliRunner ().invoke (
469- validate , ["-g" , "severity" , "-f" , "json_pp" , str (simple2_nwb )]
504+ validate , [* _grouping_opts ( * grouping ) , "-f" , "json_pp" , str (simple2_nwb )]
470505 )
471506 assert r .exit_code == 1
472507 data = json .loads (r .output )
473- # With grouping, output should be a dict (not a list)
474508 assert isinstance (data , dict )
475- # Keys should be severity names
476- for key in data :
477- assert key in ( "CRITICAL" , "ERROR" , "WARNING" , "HINT" , "INFO" , "NONE" )
478- # Values should be lists of validation result dicts
479- for v in data . values ():
480- assert isinstance ( v , list )
481- for rec in v :
482- assert "id" in rec
509+ assert len ( data ) >= 1
510+ # For composite groupings, values are nested dicts
511+ if len ( grouping ) > 1 :
512+ for v in data . values ():
513+ assert isinstance ( v , dict )
514+ else :
515+ for v in data . values () :
516+ assert isinstance ( v , list )
483517
484518
485519@pytest .mark .ai_generated
486- def test_validate_multilevel_grouping_json_two_levels (simple2_nwb : Path ) -> None :
487- """Test -g severity -g id -f json_pp produces two-level nested JSON ."""
520+ def test_validate_grouping_yaml_cli (simple2_nwb : Path ) -> None :
521+ """Grouped YAML output is a dict keyed by grouping values ."""
488522 r = CliRunner ().invoke (
489- validate , ["-g" , " severity" , "-g" , "id" , "- f" , "json_pp " , str (simple2_nwb )]
523+ validate , [* _grouping_opts ( " severity") , "-f" , "yaml " , str (simple2_nwb )]
490524 )
491525 assert r .exit_code == 1
492- data = json .loads (r .output )
493- assert isinstance (data , dict )
494- # Each value should be a dict (second grouping level)
495- for severity_key , id_groups in data .items ():
496- assert isinstance (id_groups , dict )
497- for id_key , results in id_groups .items ():
498- assert isinstance (results , list )
499- for rec in results :
500- assert "id" in rec
501-
502-
503- @pytest .mark .ai_generated
504- def test_validate_grouping_yaml_cli (simple2_nwb : Path ) -> None :
505- """Test -g severity -f yaml produces grouped YAML output."""
506- r = CliRunner ().invoke (validate , ["-g" , "severity" , "-f" , "yaml" , str (simple2_nwb )])
507- assert r .exit_code == 1
508526 yaml = ruamel .yaml .YAML (typ = "safe" )
509527 data = yaml .load (r .output )
510528 assert isinstance (data , dict )
511- for key in data :
512- assert key in ("CRITICAL" , "ERROR" , "WARNING" , "HINT" , "INFO" , "NONE" )
529+ assert "ERROR" in data
513530
514531
515532@pytest .mark .ai_generated
516533def test_validate_grouping_jsonl_error (simple2_nwb : Path ) -> None :
517- """Test -g severity -f json_lines gives a UsageError ."""
534+ """Grouping is incompatible with json_lines format ."""
518535 r = CliRunner ().invoke (
519- validate , ["-g" , " severity" , "-f" , "json_lines" , str (simple2_nwb )]
536+ validate , [* _grouping_opts ( " severity") , "-f" , "json_lines" , str (simple2_nwb )]
520537 )
521538 assert r .exit_code != 0
522539 assert "incompatible" in r .output
523540
524541
525542@pytest .mark .ai_generated
526543def test_validate_grouping_none_explicit (simple2_nwb : Path ) -> None :
527- """Test -g none is treated as no grouping."""
528- r = CliRunner ().invoke (validate , ["-g" , " none" , str (simple2_nwb )])
544+ """-g none is treated as no grouping."""
545+ r = CliRunner ().invoke (validate , [* _grouping_opts ( " none") , str (simple2_nwb )])
529546 assert r .exit_code != 0
530- # Should NOT have section headers
531547 assert "===" not in r .output
532548
533549
550+ @pytest .mark .ai_generated
551+ @pytest .mark .parametrize (
552+ "grouping" ,
553+ [("severity" ,), ("id" , "validator" )],
554+ ids = lambda g : "+" .join (g ),
555+ )
556+ def test_validate_load_with_grouping (
557+ grouping : tuple [str , ...], simple2_nwb : Path , tmp_path : Path
558+ ) -> None :
559+ """--load combined with single or composite --grouping works."""
560+ outfile = tmp_path / "results.jsonl"
561+ CliRunner ().invoke (
562+ validate , ["-f" , "json_lines" , "-o" , str (outfile ), str (simple2_nwb )]
563+ )
564+ assert outfile .exists ()
565+
566+ r = CliRunner ().invoke (
567+ validate , ["--load" , str (outfile ), * _grouping_opts (* grouping )]
568+ )
569+ assert r .exit_code == 1
570+ assert "===" in r .output
571+ assert _SIMPLE2_EXPECTED_ID in r .output
572+
573+
574+ @pytest .mark .ai_generated
575+ @pytest .mark .parametrize (
576+ "grouping" ,
577+ [("severity" ,), ("validator" , "id" )],
578+ ids = lambda g : "+" .join (g ),
579+ )
580+ def test_validate_grouping_output_file (
581+ grouping : tuple [str , ...], simple2_nwb : Path , tmp_path : Path
582+ ) -> None :
583+ """--grouping with --output writes grouped JSON to file."""
584+ outfile = tmp_path / "grouped.json"
585+ r = CliRunner ().invoke (
586+ validate ,
587+ [* _grouping_opts (* grouping ), "-o" , str (outfile ), str (simple2_nwb )],
588+ )
589+ assert r .exit_code == 1
590+ data = json .loads (outfile .read_text ())
591+ assert isinstance (data , dict )
592+
593+
534594@pytest .mark .ai_generated
535595def test_group_results_unit () -> None :
536596 """Unit test for _group_results with multiple levels."""
0 commit comments