detached bb8a06b

fix(cli): trim route status spacing

Remove the extra blank line between rendered route status sections and replace route test fixtures with neutral example names.
commit
bb8a06bd0c9ce7deea245bfa3f0481754517bb5a
author
brice · · 2026-04-15 14:55 UTC
parent
187ed78
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +69 -38
M internal/adapters/in/cli/routes.go +1 -1
@@ -636,7 +636,7 @@ func renderRoutesStatusSection(out io.Writer, section routeStatusSection) error
636636
637637 if len(section.Routes) > 0 {
638638 tree := buildRouteStatusTree(section.Routes)
639- if err := cliWriteLine(out, tree.Render()); err != nil {
639+ if err := cliWriteLine(out, strings.TrimSuffix(tree.Render(), "\n")); err != nil {
640640 return err
641641 }
642642 }
@@ -636,7 +636,7 @@ func renderRoutesStatusSection(out io.Writer, section routeStatusSection) error
636 636
637 if len(section.Routes) > 0 {637 if len(section.Routes) > 0 {
638 tree := buildRouteStatusTree(section.Routes)638 tree := buildRouteStatusTree(section.Routes)
639- if err := cliWriteLine(out, tree.Render()); err != nil {639+ if err := cliWriteLine(out, strings.TrimSuffix(tree.Render(), "\n")); err != nil {
640 return err640 return err
641 }641 }
642 }642 }
M internal/adapters/in/cli/routes_test.go +68 -37
@@ -434,12 +434,12 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
434434 },
435435 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
436436 return map[string]remote.RemoteEntry{
437- "igor": {URL: "https://gordon.supri.xyz"},
438- "hetzner-vps": {URL: "https://reg.bnema.dev"},
439- }, "igor", nil
437+ "remote-a": {URL: "https://remote-a.example.com"},
438+ "remote-b": {URL: "https://remote-b.example.com"},
439+ }, "remote-a", nil
440440 },
441441 loadRemote: func(_ context.Context, name string, entry remote.RemoteEntry) (routeListSection, error) {
442- if name == "hetzner-vps" {
442+ if name == "remote-b" {
443443 return routeListSection{
444444 Kind: "remote",
445445 Name: name,
@@ -452,8 +452,8 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
452452 Name: name,
453453 URL: entry.URL,
454454 Routes: []routeListItem{{
455- Domain: "grafana.supri.xyz",
456- Image: "grafana",
455+ Domain: "dashboard.example.com",
456+ Image: "dashboard",
457457 }},
458458 }, nil
459459 },
@@ -464,18 +464,18 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
464464 require.NoError(t, err)
465465 require.Len(t, sections, 3)
466466 assert.Equal(t, "local", sections[0].Name)
467- assert.Equal(t, "hetzner-vps", sections[1].Name)
468- assert.Equal(t, "igor", sections[2].Name)
469- assert.Equal(t, "remote unavailable", sections[1].Error)
470- assert.Equal(t, "grafana.supri.xyz", sections[2].Routes[0].Domain)
467+ assert.Equal(t, "remote-a", sections[1].Name)
468+ assert.Equal(t, "remote-b", sections[2].Name)
469+ assert.Equal(t, "dashboard.example.com", sections[1].Routes[0].Domain)
470+ assert.Equal(t, "remote unavailable", sections[2].Error)
471471 }
472472
473473 func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
474474 testsDeps := routesListDeps{
475475 explicitRemote: func() (*remote.ResolvedRemote, bool, error) {
476476 return &remote.ResolvedRemote{
477- Name: "igor",
478- URL: "https://gordon.supri.xyz",
477+ Name: "remote-a",
478+ URL: "https://remote-a.example.com",
479479 }, true, nil
480480 },
481481 loadLocal: func(context.Context, string) (routeListSection, error) {
@@ -492,8 +492,8 @@ func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
492492 Name: name,
493493 URL: entry.URL,
494494 Routes: []routeListItem{{
495- Domain: "test.supri.xyz",
496- Image: "hello-test",
495+ Domain: "test.example.com",
496+ Image: "hello-app",
497497 }},
498498 }, nil
499499 },
@@ -503,8 +503,8 @@ func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
503503
504504 require.NoError(t, err)
505505 require.Len(t, sections, 1)
506- assert.Equal(t, "igor", sections[0].Name)
507- assert.Equal(t, "test.supri.xyz", sections[0].Routes[0].Domain)
506+ assert.Equal(t, "remote-a", sections[0].Name)
507+ assert.Equal(t, "test.example.com", sections[0].Routes[0].Domain)
508508 }
509509
510510 func TestCollectRoutesSections_ExplicitRemoteResolutionErrorReturnsError(t *testing.T) {
@@ -581,11 +581,11 @@ func TestLoadRoutesListExplicitRemoteSection_PropagatesLoaderError(t *testing.T)
581581 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
582582 return routeListSection{}, errors.New("boom")
583583 },
584- }, &remote.ResolvedRemote{Name: "igor", URL: "https://gordon.supri.xyz"})
584+ }, &remote.ResolvedRemote{Name: "remote-a", URL: "https://remote-a.example.com"})
585585
586586 assert.Equal(t, "remote", section.Kind)
587- assert.Equal(t, "igor", section.Name)
588- assert.Equal(t, "https://gordon.supri.xyz", section.URL)
587+ assert.Equal(t, "remote-a", section.Name)
588+ assert.Equal(t, "https://remote-a.example.com", section.URL)
589589 assert.Equal(t, "boom", section.Error)
590590 }
591591
@@ -594,11 +594,11 @@ func TestLoadRoutesStatusExplicitRemoteSection_PropagatesLoaderError(t *testing.
594594 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
595595 return routeStatusSection{}, errors.New("boom")
596596 },
597- }, &remote.ResolvedRemote{Name: "igor", URL: "https://gordon.supri.xyz"})
597+ }, &remote.ResolvedRemote{Name: "remote-a", URL: "https://remote-a.example.com"})
598598
599599 assert.Equal(t, "remote", section.Kind)
600- assert.Equal(t, "igor", section.Name)
601- assert.Equal(t, "https://gordon.supri.xyz", section.URL)
600+ assert.Equal(t, "remote-a", section.Name)
601+ assert.Equal(t, "https://remote-a.example.com", section.URL)
602602 assert.Equal(t, "boom", section.Error)
603603 }
604604
@@ -612,7 +612,7 @@ func TestCollectRoutesListAggregateSections_EncodesLoaderErrors(t *testing.T) {
612612 return map[string]remote.RemoteEntry{}, "", nil
613613 },
614614 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
615- return routeListSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, nil
615+ return routeListSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, nil
616616 },
617617 })
618618
@@ -628,10 +628,10 @@ func TestCollectRoutesListAggregateSections_EncodesLoaderErrors(t *testing.T) {
628628 return routeListSection{Kind: "local", Name: "local"}, nil
629629 },
630630 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
631- return map[string]remote.RemoteEntry{"igor": {URL: "https://gordon.supri.xyz"}}, "", nil
631+ return map[string]remote.RemoteEntry{"remote-a": {URL: "https://remote-a.example.com"}}, "", nil
632632 },
633633 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
634- return routeListSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, errors.New("remote failed")
634+ return routeListSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, errors.New("remote failed")
635635 },
636636 })
637637
@@ -653,7 +653,7 @@ func TestCollectRoutesStatusAggregateSections_EncodesLoaderErrors(t *testing.T)
653653 return map[string]remote.RemoteEntry{}, "", nil
654654 },
655655 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
656- return routeStatusSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, nil
656+ return routeStatusSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, nil
657657 },
658658 })
659659
@@ -669,10 +669,10 @@ func TestCollectRoutesStatusAggregateSections_EncodesLoaderErrors(t *testing.T)
669669 return routeStatusSection{Kind: "local", Name: "local"}, nil
670670 },
671671 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
672- return map[string]remote.RemoteEntry{"igor": {URL: "https://gordon.supri.xyz"}}, "", nil
672+ return map[string]remote.RemoteEntry{"remote-a": {URL: "https://remote-a.example.com"}}, "", nil
673673 },
674674 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
675- return routeStatusSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, errors.New("remote failed")
675+ return routeStatusSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, errors.New("remote failed")
676676 },
677677 })
678678
@@ -760,8 +760,8 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
760760 },
761761 {
762762 Kind: "remote",
763- Name: "igor",
764- URL: "https://gordon.supri.xyz",
763+ Name: "remote-a",
764+ URL: "https://remote-a.example.com",
765765 Error: "dial tcp timeout",
766766 },
767767 }
@@ -791,7 +791,7 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
791791 routeIdx := lineIndex("app.local")
792792 require.NotEqual(t, -1, routeIdx)
793793
794- remoteIdx := lineIndex("Remote: igor")
794+ remoteIdx := lineIndex("Remote: remote-a")
795795 require.NotEqual(t, -1, remoteIdx)
796796
797797 errorIdx := lineIndex("dial tcp timeout")
@@ -803,10 +803,41 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
803803 assert.Less(t, remoteIdx, errorIdx)
804804 }
805805
806+func TestRenderRoutesStatusSections_DoesNotAddExtraBlankLineBetweenSections(t *testing.T) {
807+ sections := []routeStatusSection{
808+ {
809+ Kind: "remote",
810+ Name: "remote-b",
811+ Routes: []routeStatusItem{{
812+ Domain: "app.example.com",
813+ Image: "app:latest",
814+ ContainerStatus: "running",
815+ }},
816+ },
817+ {
818+ Kind: "remote",
819+ Name: "remote-a",
820+ Routes: []routeStatusItem{{
821+ Domain: "dashboard.example.com",
822+ Image: "dashboard",
823+ ContainerStatus: "running",
824+ }},
825+ },
826+ }
827+
828+ var out bytes.Buffer
829+ err := renderRoutesStatusSections(&out, sections)
830+
831+ require.NoError(t, err)
832+ rendered := stripANSI(out.String())
833+ assert.NotContains(t, rendered, "app:latest\n\n\nRemote: remote-a")
834+ assert.Contains(t, rendered, "app:latest\n\nRemote: remote-a")
835+}
836+
806837 func TestRenderRoutesListSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {
807838 sections := []routeListSection{
808839 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
809- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"},
840+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"},
810841 }
811842
812843 var out bytes.Buffer
@@ -816,14 +847,14 @@ func TestRenderRoutesListSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T
816847 rendered := stripANSI(out.String())
817848 assert.NotContains(t, rendered, "Local")
818849 assert.NotContains(t, rendered, "failed to initialize local control plane")
819- assert.Contains(t, rendered, "Remote: igor")
850+ assert.Contains(t, rendered, "Remote: remote-a")
820851 assert.Contains(t, rendered, "No routes configured")
821852 }
822853
823854 func TestRenderRoutesStatusSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {
824855 sections := []routeStatusSection{
825856 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
826- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"},
857+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"},
827858 }
828859
829860 var out bytes.Buffer
@@ -833,14 +864,14 @@ func TestRenderRoutesStatusSections_HidesLocalErrorWhenRemoteIsUsable(t *testing
833864 rendered := stripANSI(out.String())
834865 assert.NotContains(t, rendered, "Local")
835866 assert.NotContains(t, rendered, "failed to initialize local control plane")
836- assert.Contains(t, rendered, "Remote: igor")
867+ assert.Contains(t, rendered, "Remote: remote-a")
837868 assert.Contains(t, rendered, "No routes configured")
838869 }
839870
840871 func TestRenderRoutesStatusSections_KeepsLocalErrorWhenAllRemotesFail(t *testing.T) {
841872 sections := []routeStatusSection{
842873 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
843- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz", Error: "dial tcp timeout"},
874+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com", Error: "dial tcp timeout"},
844875 }
845876
846877 var out bytes.Buffer
@@ -850,7 +881,7 @@ func TestRenderRoutesStatusSections_KeepsLocalErrorWhenAllRemotesFail(t *testing
850881 rendered := stripANSI(out.String())
851882 assert.Contains(t, rendered, "Local")
852883 assert.Contains(t, rendered, "failed to initialize local control plane")
853- assert.Contains(t, rendered, "Remote: igor")
884+ assert.Contains(t, rendered, "Remote: remote-a")
854885 assert.Contains(t, rendered, "dial tcp timeout")
855886 }
856887
@@ -434,12 +434,12 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
434 },434 },
435 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {435 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
436 return map[string]remote.RemoteEntry{436 return map[string]remote.RemoteEntry{
437- "igor": {URL: "https://gordon.supri.xyz"},437+ "remote-a": {URL: "https://remote-a.example.com"},
438- "hetzner-vps": {URL: "https://reg.bnema.dev"},438+ "remote-b": {URL: "https://remote-b.example.com"},
439- }, "igor", nil439+ }, "remote-a", nil
440 },440 },
441 loadRemote: func(_ context.Context, name string, entry remote.RemoteEntry) (routeListSection, error) {441 loadRemote: func(_ context.Context, name string, entry remote.RemoteEntry) (routeListSection, error) {
442- if name == "hetzner-vps" {442+ if name == "remote-b" {
443 return routeListSection{443 return routeListSection{
444 Kind: "remote",444 Kind: "remote",
445 Name: name,445 Name: name,
@@ -452,8 +452,8 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
452 Name: name,452 Name: name,
453 URL: entry.URL,453 URL: entry.URL,
454 Routes: []routeListItem{{454 Routes: []routeListItem{{
455- Domain: "grafana.supri.xyz",455+ Domain: "dashboard.example.com",
456- Image: "grafana",456+ Image: "dashboard",
457 }},457 }},
458 }, nil458 }, nil
459 },459 },
@@ -464,18 +464,18 @@ func TestCollectRoutesListSections_DefaultModeIncludesLocalThenSortedRemotes(t *
464 require.NoError(t, err)464 require.NoError(t, err)
465 require.Len(t, sections, 3)465 require.Len(t, sections, 3)
466 assert.Equal(t, "local", sections[0].Name)466 assert.Equal(t, "local", sections[0].Name)
467- assert.Equal(t, "hetzner-vps", sections[1].Name)467+ assert.Equal(t, "remote-a", sections[1].Name)
468- assert.Equal(t, "igor", sections[2].Name)468+ assert.Equal(t, "remote-b", sections[2].Name)
469- assert.Equal(t, "remote unavailable", sections[1].Error)469+ assert.Equal(t, "dashboard.example.com", sections[1].Routes[0].Domain)
470- assert.Equal(t, "grafana.supri.xyz", sections[2].Routes[0].Domain)470+ assert.Equal(t, "remote unavailable", sections[2].Error)
471 }471 }
472 472
473 func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {473 func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
474 testsDeps := routesListDeps{474 testsDeps := routesListDeps{
475 explicitRemote: func() (*remote.ResolvedRemote, bool, error) {475 explicitRemote: func() (*remote.ResolvedRemote, bool, error) {
476 return &remote.ResolvedRemote{476 return &remote.ResolvedRemote{
477- Name: "igor",477+ Name: "remote-a",
478- URL: "https://gordon.supri.xyz",478+ URL: "https://remote-a.example.com",
479 }, true, nil479 }, true, nil
480 },480 },
481 loadLocal: func(context.Context, string) (routeListSection, error) {481 loadLocal: func(context.Context, string) (routeListSection, error) {
@@ -492,8 +492,8 @@ func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
492 Name: name,492 Name: name,
493 URL: entry.URL,493 URL: entry.URL,
494 Routes: []routeListItem{{494 Routes: []routeListItem{{
495- Domain: "test.supri.xyz",495+ Domain: "test.example.com",
496- Image: "hello-test",496+ Image: "hello-app",
497 }},497 }},
498 }, nil498 }, nil
499 },499 },
@@ -503,8 +503,8 @@ func TestCollectRoutesListSections_ExplicitRemoteSkipsAggregate(t *testing.T) {
503 503
504 require.NoError(t, err)504 require.NoError(t, err)
505 require.Len(t, sections, 1)505 require.Len(t, sections, 1)
506- assert.Equal(t, "igor", sections[0].Name)506+ assert.Equal(t, "remote-a", sections[0].Name)
507- assert.Equal(t, "test.supri.xyz", sections[0].Routes[0].Domain)507+ assert.Equal(t, "test.example.com", sections[0].Routes[0].Domain)
508 }508 }
509 509
510 func TestCollectRoutesSections_ExplicitRemoteResolutionErrorReturnsError(t *testing.T) {510 func TestCollectRoutesSections_ExplicitRemoteResolutionErrorReturnsError(t *testing.T) {
@@ -581,11 +581,11 @@ func TestLoadRoutesListExplicitRemoteSection_PropagatesLoaderError(t *testing.T)
581 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {581 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
582 return routeListSection{}, errors.New("boom")582 return routeListSection{}, errors.New("boom")
583 },583 },
584- }, &remote.ResolvedRemote{Name: "igor", URL: "https://gordon.supri.xyz"})584+ }, &remote.ResolvedRemote{Name: "remote-a", URL: "https://remote-a.example.com"})
585 585
586 assert.Equal(t, "remote", section.Kind)586 assert.Equal(t, "remote", section.Kind)
587- assert.Equal(t, "igor", section.Name)587+ assert.Equal(t, "remote-a", section.Name)
588- assert.Equal(t, "https://gordon.supri.xyz", section.URL)588+ assert.Equal(t, "https://remote-a.example.com", section.URL)
589 assert.Equal(t, "boom", section.Error)589 assert.Equal(t, "boom", section.Error)
590 }590 }
591 591
@@ -594,11 +594,11 @@ func TestLoadRoutesStatusExplicitRemoteSection_PropagatesLoaderError(t *testing.
594 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {594 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
595 return routeStatusSection{}, errors.New("boom")595 return routeStatusSection{}, errors.New("boom")
596 },596 },
597- }, &remote.ResolvedRemote{Name: "igor", URL: "https://gordon.supri.xyz"})597+ }, &remote.ResolvedRemote{Name: "remote-a", URL: "https://remote-a.example.com"})
598 598
599 assert.Equal(t, "remote", section.Kind)599 assert.Equal(t, "remote", section.Kind)
600- assert.Equal(t, "igor", section.Name)600+ assert.Equal(t, "remote-a", section.Name)
601- assert.Equal(t, "https://gordon.supri.xyz", section.URL)601+ assert.Equal(t, "https://remote-a.example.com", section.URL)
602 assert.Equal(t, "boom", section.Error)602 assert.Equal(t, "boom", section.Error)
603 }603 }
604 604
@@ -612,7 +612,7 @@ func TestCollectRoutesListAggregateSections_EncodesLoaderErrors(t *testing.T) {
612 return map[string]remote.RemoteEntry{}, "", nil612 return map[string]remote.RemoteEntry{}, "", nil
613 },613 },
614 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {614 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
615- return routeListSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, nil615+ return routeListSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, nil
616 },616 },
617 })617 })
618 618
@@ -628,10 +628,10 @@ func TestCollectRoutesListAggregateSections_EncodesLoaderErrors(t *testing.T) {
628 return routeListSection{Kind: "local", Name: "local"}, nil628 return routeListSection{Kind: "local", Name: "local"}, nil
629 },629 },
630 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {630 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
631- return map[string]remote.RemoteEntry{"igor": {URL: "https://gordon.supri.xyz"}}, "", nil631+ return map[string]remote.RemoteEntry{"remote-a": {URL: "https://remote-a.example.com"}}, "", nil
632 },632 },
633 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {633 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeListSection, error) {
634- return routeListSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, errors.New("remote failed")634+ return routeListSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, errors.New("remote failed")
635 },635 },
636 })636 })
637 637
@@ -653,7 +653,7 @@ func TestCollectRoutesStatusAggregateSections_EncodesLoaderErrors(t *testing.T)
653 return map[string]remote.RemoteEntry{}, "", nil653 return map[string]remote.RemoteEntry{}, "", nil
654 },654 },
655 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {655 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
656- return routeStatusSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, nil656+ return routeStatusSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, nil
657 },657 },
658 })658 })
659 659
@@ -669,10 +669,10 @@ func TestCollectRoutesStatusAggregateSections_EncodesLoaderErrors(t *testing.T)
669 return routeStatusSection{Kind: "local", Name: "local"}, nil669 return routeStatusSection{Kind: "local", Name: "local"}, nil
670 },670 },
671 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {671 listRemotes: func() (map[string]remote.RemoteEntry, string, error) {
672- return map[string]remote.RemoteEntry{"igor": {URL: "https://gordon.supri.xyz"}}, "", nil672+ return map[string]remote.RemoteEntry{"remote-a": {URL: "https://remote-a.example.com"}}, "", nil
673 },673 },
674 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {674 loadRemote: func(context.Context, string, remote.RemoteEntry) (routeStatusSection, error) {
675- return routeStatusSection{Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"}, errors.New("remote failed")675+ return routeStatusSection{Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"}, errors.New("remote failed")
676 },676 },
677 })677 })
678 678
@@ -760,8 +760,8 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
760 },760 },
761 {761 {
762 Kind: "remote",762 Kind: "remote",
763- Name: "igor",763+ Name: "remote-a",
764- URL: "https://gordon.supri.xyz",764+ URL: "https://remote-a.example.com",
765 Error: "dial tcp timeout",765 Error: "dial tcp timeout",
766 },766 },
767 }767 }
@@ -791,7 +791,7 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
791 routeIdx := lineIndex("app.local")791 routeIdx := lineIndex("app.local")
792 require.NotEqual(t, -1, routeIdx)792 require.NotEqual(t, -1, routeIdx)
793 793
794- remoteIdx := lineIndex("Remote: igor")794+ remoteIdx := lineIndex("Remote: remote-a")
795 require.NotEqual(t, -1, remoteIdx)795 require.NotEqual(t, -1, remoteIdx)
796 796
797 errorIdx := lineIndex("dial tcp timeout")797 errorIdx := lineIndex("dial tcp timeout")
@@ -803,10 +803,41 @@ func TestRenderRoutesStatusSections_IncludesSectionHeadingsAndErrors(t *testing.
803 assert.Less(t, remoteIdx, errorIdx)803 assert.Less(t, remoteIdx, errorIdx)
804 }804 }
805 805
806+func TestRenderRoutesStatusSections_DoesNotAddExtraBlankLineBetweenSections(t *testing.T) {
807+ sections := []routeStatusSection{
808+ {
809+ Kind: "remote",
810+ Name: "remote-b",
811+ Routes: []routeStatusItem{{
812+ Domain: "app.example.com",
813+ Image: "app:latest",
814+ ContainerStatus: "running",
815+ }},
816+ },
817+ {
818+ Kind: "remote",
819+ Name: "remote-a",
820+ Routes: []routeStatusItem{{
821+ Domain: "dashboard.example.com",
822+ Image: "dashboard",
823+ ContainerStatus: "running",
824+ }},
825+ },
826+ }
827+
828+ var out bytes.Buffer
829+ err := renderRoutesStatusSections(&out, sections)
830+
831+ require.NoError(t, err)
832+ rendered := stripANSI(out.String())
833+ assert.NotContains(t, rendered, "app:latest\n\n\nRemote: remote-a")
834+ assert.Contains(t, rendered, "app:latest\n\nRemote: remote-a")
835+}
836+
806 func TestRenderRoutesListSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {837 func TestRenderRoutesListSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {
807 sections := []routeListSection{838 sections := []routeListSection{
808 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},839 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
809- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"},840+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"},
810 }841 }
811 842
812 var out bytes.Buffer843 var out bytes.Buffer
@@ -816,14 +847,14 @@ func TestRenderRoutesListSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T
816 rendered := stripANSI(out.String())847 rendered := stripANSI(out.String())
817 assert.NotContains(t, rendered, "Local")848 assert.NotContains(t, rendered, "Local")
818 assert.NotContains(t, rendered, "failed to initialize local control plane")849 assert.NotContains(t, rendered, "failed to initialize local control plane")
819- assert.Contains(t, rendered, "Remote: igor")850+ assert.Contains(t, rendered, "Remote: remote-a")
820 assert.Contains(t, rendered, "No routes configured")851 assert.Contains(t, rendered, "No routes configured")
821 }852 }
822 853
823 func TestRenderRoutesStatusSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {854 func TestRenderRoutesStatusSections_HidesLocalErrorWhenRemoteIsUsable(t *testing.T) {
824 sections := []routeStatusSection{855 sections := []routeStatusSection{
825 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},856 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
826- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz"},857+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com"},
827 }858 }
828 859
829 var out bytes.Buffer860 var out bytes.Buffer
@@ -833,14 +864,14 @@ func TestRenderRoutesStatusSections_HidesLocalErrorWhenRemoteIsUsable(t *testing
833 rendered := stripANSI(out.String())864 rendered := stripANSI(out.String())
834 assert.NotContains(t, rendered, "Local")865 assert.NotContains(t, rendered, "Local")
835 assert.NotContains(t, rendered, "failed to initialize local control plane")866 assert.NotContains(t, rendered, "failed to initialize local control plane")
836- assert.Contains(t, rendered, "Remote: igor")867+ assert.Contains(t, rendered, "Remote: remote-a")
837 assert.Contains(t, rendered, "No routes configured")868 assert.Contains(t, rendered, "No routes configured")
838 }869 }
839 870
840 func TestRenderRoutesStatusSections_KeepsLocalErrorWhenAllRemotesFail(t *testing.T) {871 func TestRenderRoutesStatusSections_KeepsLocalErrorWhenAllRemotesFail(t *testing.T) {
841 sections := []routeStatusSection{872 sections := []routeStatusSection{
842 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},873 {Kind: "local", Name: "local", Error: "failed to initialize local control plane"},
843- {Kind: "remote", Name: "igor", URL: "https://gordon.supri.xyz", Error: "dial tcp timeout"},874+ {Kind: "remote", Name: "remote-a", URL: "https://remote-a.example.com", Error: "dial tcp timeout"},
844 }875 }
845 876
846 var out bytes.Buffer877 var out bytes.Buffer
@@ -850,7 +881,7 @@ func TestRenderRoutesStatusSections_KeepsLocalErrorWhenAllRemotesFail(t *testing
850 rendered := stripANSI(out.String())881 rendered := stripANSI(out.String())
851 assert.Contains(t, rendered, "Local")882 assert.Contains(t, rendered, "Local")
852 assert.Contains(t, rendered, "failed to initialize local control plane")883 assert.Contains(t, rendered, "failed to initialize local control plane")
853- assert.Contains(t, rendered, "Remote: igor")884+ assert.Contains(t, rendered, "Remote: remote-a")
854 assert.Contains(t, rendered, "dial tcp timeout")885 assert.Contains(t, rendered, "dial tcp timeout")
855 }886 }
856 887

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close