Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
charm.land/lipgloss/v2 v2.0.0
github.com/mergestat/timediff v0.0.4
github.com/shopspring/decimal v1.4.0
github.com/stretchr/testify v1.11.1
github.com/sumup/sumup-go v0.15.0
github.com/urfave/cli/v3 v3.6.2
golang.org/x/term v0.40.0
Expand All @@ -23,12 +24,15 @@ require (
github.com/charmbracelet/x/windows v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mattn/go-runewidth v0.0.20 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,7 @@ golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
30 changes: 30 additions & 0 deletions internal/app/context_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package app

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNormalizeLocale(t *testing.T) {
t.Parallel()

tests := []struct {
name string
input string
want string
}{
{name: "returns empty string for empty input", input: "", want: ""},
{name: "trims whitespace and replaces underscore", input: " en_US.UTF-8 ", want: "en-US"},
{name: "strips locale modifier", input: "sr_RS@latin", want: "sr-RS"},
{name: "returns plain locale unchanged", input: "de-DE", want: "de-DE"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

assert.Equal(t, tt.want, normalizeLocale(tt.input))
})
}
}
145 changes: 45 additions & 100 deletions internal/app/context_test.go
Original file line number Diff line number Diff line change
@@ -1,123 +1,68 @@
package app
package app_test

import (
"context"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v3"

"github.com/sumup/sumup-cli/internal/app"
"github.com/sumup/sumup-cli/internal/config"
)

func TestNormalizeLocale(t *testing.T) {
t.Parallel()

tests := []struct {
name string
input string
want string
}{
{name: "empty", input: "", want: ""},
{name: "trim and replace underscore", input: " en_US.UTF-8 ", want: "en-US"},
{name: "strip modifier", input: "sr_RS@latin", want: "sr-RS"},
{name: "plain locale", input: "de-DE", want: "de-DE"},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

if got := normalizeLocale(tc.input); got != tc.want {
t.Fatalf("normalizeLocale(%q) = %q, want %q", tc.input, got, tc.want)
}
})
}
}

func TestGetMerchantCodePrefersFlag(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)
if err := config.SetCurrentMerchantCode("MCONFIG"); err != nil {
t.Fatalf("set current merchant code: %v", err)
}

cmd := &cli.Command{
Name: "sumup",
Flags: []cli.Flag{
&cli.StringFlag{Name: "merchant-code"},
},
Action: func(_ context.Context, cmd *cli.Command) error {
got, err := GetMerchantCode(cmd, "merchant-code")
if err != nil {
t.Fatalf("GetMerchantCode() unexpected error: %v", err)
}
if got != "MFLAG" {
t.Fatalf("GetMerchantCode() = %q, want %q", got, "MFLAG")
}
return nil
},
}

if err := cmd.Run(context.Background(), []string{"sumup", "--merchant-code", "MFLAG"}); err != nil {
t.Fatalf("run command: %v", err)
}
func TestGetMerchantCode(t *testing.T) {
t.Run("prefers flag value over stored config", func(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)
require.NoError(t, config.SetCurrentMerchantCode("MCONFIG"))

got, err := runGetMerchantCode(t, []string{"sumup", "--merchant-code", "MFLAG"})
require.NoError(t, err)
assert.Equal(t, "MFLAG", got)
})

t.Run("falls back to stored config", func(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)
require.NoError(t, config.SetCurrentMerchantCode("MCONFIG"))

got, err := runGetMerchantCode(t, []string{"sumup"})
require.NoError(t, err)
assert.Equal(t, "MCONFIG", got)
})

t.Run("returns helpful error when no merchant code is available", func(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)

got, err := runGetMerchantCode(t, []string{"sumup"})
require.Error(t, err)
assert.Empty(t, got)
assert.ErrorContains(t, err, "merchant code is required")
})
}

func TestGetMerchantCodeFallsBackToConfig(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)
if err := config.SetCurrentMerchantCode("MCONFIG"); err != nil {
t.Fatalf("set current merchant code: %v", err)
}
func runGetMerchantCode(t *testing.T, args []string) (string, error) {
t.Helper()

var got string
cmd := &cli.Command{
Name: "sumup",
Flags: []cli.Flag{
&cli.StringFlag{Name: "merchant-code"},
},
Action: func(_ context.Context, cmd *cli.Command) error {
got, err := GetMerchantCode(cmd, "merchant-code")
if err != nil {
t.Fatalf("GetMerchantCode() unexpected error: %v", err)
}
if got != "MCONFIG" {
t.Fatalf("GetMerchantCode() = %q, want %q", got, "MCONFIG")
}
return nil
var err error
got, err = app.GetMerchantCode(cmd, "merchant-code")
return err
},
}

if err := cmd.Run(context.Background(), []string{"sumup"}); err != nil {
t.Fatalf("run command: %v", err)
}
}

func TestGetMerchantCodeErrorsWhenUnset(t *testing.T) {
tempDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tempDir)
t.Setenv("HOME", tempDir)

cmd := &cli.Command{
Name: "sumup",
Flags: []cli.Flag{
&cli.StringFlag{Name: "merchant-code"},
},
Action: func(_ context.Context, cmd *cli.Command) error {
_, err := GetMerchantCode(cmd, "merchant-code")
if err == nil {
t.Fatal("GetMerchantCode() error = nil, want non-nil")
}
if !strings.Contains(err.Error(), "merchant code is required") {
t.Fatalf("GetMerchantCode() error = %q, want merchant code hint", err)
}
return nil
},
}

if err := cmd.Run(context.Background(), []string{"sumup"}); err != nil {
t.Fatalf("run command: %v", err)
}
err := cmd.Run(context.Background(), args)
return got, err
}
Loading
Loading