@@ -335,6 +335,37 @@ def test_columns_modal_renders_built_state():
335335 assert m .profile_columns == {}
336336
337337
338+ def test_columns_modal_dedupes_duplicate_column_names ():
339+ """Survey slugs (osmi-* series) and a few others ship parquet schemas
340+ with repeated top-level column names. The modal must suffix repeats
341+ so the DataTable row key is unique and stats_by_name doesn't silently
342+ collapse duplicate-named entries onto the last one."""
343+ pytest .importorskip ("textual" )
344+ from scripts .pipeline .browse import ColumnsModal
345+
346+ stats = [
347+ {"name" : "Q1" , "type" : "string" , "length" : 100 ,
348+ "null_count" : 0 , "min" : None , "max" : None },
349+ {"name" : "Why or why not?" , "type" : "string" , "length" : 80 ,
350+ "null_count" : 0 , "min" : None , "max" : None },
351+ {"name" : "Q2" , "type" : "string" , "length" : 90 ,
352+ "null_count" : 0 , "min" : None , "max" : None },
353+ {"name" : "Why or why not?" , "type" : "string" , "length" : 75 ,
354+ "null_count" : 0 , "min" : None , "max" : None },
355+ {"name" : "Why or why not?" , "type" : "string" , "length" : 60 ,
356+ "null_count" : 0 , "min" : None , "max" : None },
357+ ]
358+ m = ColumnsModal ("x" , {"slug" : "x" }, stats )
359+ assert [s ["name" ] for s in m .stats ] == [
360+ "Q1" , "Why or why not?" , "Q2" ,
361+ "Why or why not? (2)" , "Why or why not? (3)" ,
362+ ]
363+ # stats_by_name keeps a distinct entry for each occurrence.
364+ assert m .stats_by_name ["Why or why not?" ]["length" ] == 80
365+ assert m .stats_by_name ["Why or why not? (2)" ]["length" ] == 75
366+ assert m .stats_by_name ["Why or why not? (3)" ]["length" ] == 60
367+
368+
338369def test_render_column_detail_dtype_shapes ():
339370 """`_render_column_detail` produces shape-appropriate multi-line markup."""
340371 pytest .importorskip ("textual" )
0 commit comments