@@ -74,10 +74,12 @@ impl CliBackend {
7474 lines[ sep_idx + 1 ..]
7575 . iter ( )
7676 . filter ( |l| !l. trim ( ) . is_empty ( ) )
77- // Stop at footer lines like "123 upgrades available."
78- // These are short lines with a digit followed by a word — distinct from data rows
79- // which span the full table width
80- . take_while ( |l| l. len ( ) > 20 || !l. trim_start ( ) . starts_with ( |c : char | c. is_ascii_digit ( ) ) )
77+ // Stop at footer lines that start with a digit, such as:
78+ // - "2 upgrades available."
79+ // - "2 packages have pins that prevent upgrade..."
80+ // - "2 Pakete verfügen über Pins, die ein Upgrade verhindern..."
81+ // These lines indicate end of table data and start of informational messages.
82+ . take_while ( |l| !l. trim_start ( ) . starts_with ( |c : char | c. is_ascii_digit ( ) ) )
8183 . filter_map ( |line| self . parse_table_row ( line, & col_positions) )
8284 . collect ( )
8385 }
@@ -523,4 +525,72 @@ Google Chrome Google.Chrome 131.0.6 winget
523525 assert_eq ! ( packages[ 0 ] . source, "winget" ) ;
524526 assert ! ( packages[ 0 ] . available_version. is_empty( ) ) ;
525527 }
528+
529+ #[ test]
530+ fn parse_upgrade_table_with_german_pin_message ( ) {
531+ let backend = CliBackend :: new ( ) ;
532+ // Real output from winget upgrade with pinned packages (German locale)
533+ let output = "\
534+ Name ID Version Verfügbar Quelle
535+ -------------------------------------------------------------------------------------------------
536+ RamMap Microsoft.Sysinternals.R... 1.61 1.62 winget
537+ vc_clip vc_clip.vc_dir 2026.01.29 winget
538+ 2 Pakete verfügen über Pins, die ein Upgrade verhindern. Verwenden Sie den Befehl \" winget pin\" , um Pins anzuzeigen und zu bearbeiten. Wenn Sie das --include-pinned-Argument verwenden, werden möglicherweise weitere Ergebnisse angezeigt.
539+ " ;
540+ let packages = backend. parse_packages_from_table ( output) ;
541+ assert_eq ! ( packages. len( ) , 2 , "should parse only the package rows, not the pin message" ) ;
542+ assert_eq ! ( packages[ 0 ] . id, "Microsoft.Sysinternals.R..." ) ;
543+ assert_eq ! ( packages[ 0 ] . version, "1.61" ) ;
544+ assert_eq ! ( packages[ 0 ] . available_version, "1.62" ) ;
545+ assert_eq ! ( packages[ 1 ] . id, "vc_clip.vc_dir" ) ;
546+ }
547+
548+ #[ test]
549+ fn parse_upgrade_table_with_english_pin_message ( ) {
550+ let backend = CliBackend :: new ( ) ;
551+ // English version of pin message
552+ let output = "\
553+ Name Id Version Available Source
554+ -------------------------------------------------------------------------------------------------
555+ Google Chrome Google.Chrome 131.0.6778 132.0.6834 winget
556+ 2 packages have pins that prevent upgrade. Use the \" winget pin\" command to view and edit pins. If you use the --include-pinned argument, additional results may be displayed.
557+ " ;
558+ let packages = backend. parse_packages_from_table ( output) ;
559+ assert_eq ! ( packages. len( ) , 1 , "should parse only the package rows, not the pin message" ) ;
560+ assert_eq ! ( packages[ 0 ] . id, "Google.Chrome" ) ;
561+ assert_eq ! ( packages[ 0 ] . available_version, "132.0.6834" ) ;
562+ }
563+
564+ #[ test]
565+ fn parse_upgrade_table_with_upgrades_available_footer ( ) {
566+ let backend = CliBackend :: new ( ) ;
567+ // Footer message indicating number of upgrades
568+ let output = "\
569+ Name Id Version Available Source
570+ -------------------------------------------------------------------------------------------------
571+ Google Chrome Google.Chrome 131.0.6778 132.0.6834 winget
572+ Microsoft Visual Studio Code Microsoft.VisualStudioCode 1.95.3 1.96.0 winget
573+ 2 upgrades available.
574+ " ;
575+ let packages = backend. parse_packages_from_table ( output) ;
576+ assert_eq ! ( packages. len( ) , 2 , "should parse package rows, stopping at footer" ) ;
577+ assert_eq ! ( packages[ 0 ] . id, "Google.Chrome" ) ;
578+ assert_eq ! ( packages[ 1 ] . id, "Microsoft.VisualStudioCode" ) ;
579+ }
580+
581+ #[ test]
582+ fn parse_upgrade_table_with_multiple_footer_lines ( ) {
583+ let backend = CliBackend :: new ( ) ;
584+ // Multiple footer messages
585+ let output = "\
586+ Name Id Version Available Source
587+ -------------------------------------------------------------------------------------------------
588+ Google Chrome Google.Chrome 131.0.6778 132.0.6834 winget
589+ 1 upgrade available.
590+ 2 packages have pins that prevent upgrade. Use the \" winget pin\" command to view and edit pins.
591+ " ;
592+ let packages = backend. parse_packages_from_table ( output) ;
593+ assert_eq ! ( packages. len( ) , 1 , "should stop at first footer line" ) ;
594+ assert_eq ! ( packages[ 0 ] . id, "Google.Chrome" ) ;
595+ }
526596}
0 commit comments