445 lines
9.2 KiB
Go
445 lines
9.2 KiB
Go
// Copyright 2025 eat-pray-ai & OpenWaygate
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
func TestGetFileName(t *testing.T) {
|
|
type args struct {
|
|
file string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want string
|
|
}{
|
|
{
|
|
name: "with extension",
|
|
args: args{file: "example.txt"},
|
|
want: "example",
|
|
},
|
|
{
|
|
name: "without extension",
|
|
args: args{file: "example"},
|
|
want: "example",
|
|
},
|
|
{
|
|
name: "with path",
|
|
args: args{file: "/foo/bar/example.txt"},
|
|
want: "example",
|
|
},
|
|
{
|
|
name: "double extension",
|
|
args: args{file: "archive.tar.gz"},
|
|
want: "archive.tar",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
if got := GetFileName(tt.args.file); got != tt.want {
|
|
t.Errorf("GetFileName() = %v, want %v", got, tt.want)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestIsJson(t *testing.T) {
|
|
type args struct {
|
|
s string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want bool
|
|
}{
|
|
{
|
|
name: "valid json",
|
|
args: args{s: `{"key": "value"}`},
|
|
want: true,
|
|
},
|
|
{
|
|
name: "invalid json",
|
|
args: args{s: `{"key": "value"`},
|
|
want: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
if got := IsJson(tt.args.s); got != tt.want {
|
|
t.Errorf("IsJson() = %v, want %v", got, tt.want)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestIsInteractive(t *testing.T) {
|
|
devNull, _ := os.Open(os.DevNull)
|
|
defer func() { _ = devNull.Close() }()
|
|
|
|
tests := []struct {
|
|
name string
|
|
writer io.Writer
|
|
ci string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "bytes.Buffer is non-interactive",
|
|
writer: &bytes.Buffer{},
|
|
want: false,
|
|
},
|
|
{
|
|
name: "os.File without TTY is non-interactive",
|
|
writer: devNull,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "CI env forces non-interactive",
|
|
writer: os.Stdout,
|
|
ci: "true",
|
|
want: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
if tt.ci != "" {
|
|
t.Setenv("CI", tt.ci)
|
|
}
|
|
if got := IsInteractive(tt.writer); got != tt.want {
|
|
t.Errorf("IsInteractive() = %v, want %v", got, tt.want)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestPrintJSON(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data any
|
|
interactive bool
|
|
wantWriter string
|
|
}{
|
|
{
|
|
name: "interactive indented json",
|
|
data: map[string]string{"key": "value"},
|
|
interactive: true,
|
|
wantWriter: "{\n \"key\": \"value\"\n}\n",
|
|
},
|
|
{
|
|
name: "non-interactive compact json",
|
|
data: map[string]string{"key": "value"},
|
|
interactive: false,
|
|
wantWriter: "{\"key\":\"value\"}\n",
|
|
},
|
|
{
|
|
name: "nil data interactive",
|
|
data: nil,
|
|
interactive: true,
|
|
wantWriter: "null\n",
|
|
},
|
|
{
|
|
name: "nil data non-interactive",
|
|
data: nil,
|
|
interactive: false,
|
|
wantWriter: "null\n",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
orig := IsInteractive
|
|
IsInteractive = func(any) bool { return tt.interactive }
|
|
defer func() { IsInteractive = orig }()
|
|
|
|
writer := &bytes.Buffer{}
|
|
PrintJSON(tt.data, writer)
|
|
if gotWriter := writer.String(); gotWriter != tt.wantWriter {
|
|
t.Errorf("PrintJSON() = %v, want %v", gotWriter, tt.wantWriter)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestPrintYAML(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data any
|
|
wantWriter string
|
|
}{
|
|
{
|
|
name: "simple yaml",
|
|
data: map[string]string{"key": "value"},
|
|
wantWriter: "key: value\n\n",
|
|
},
|
|
{
|
|
name: "nil data",
|
|
data: nil,
|
|
wantWriter: "null\n\n",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
writer := &bytes.Buffer{}
|
|
PrintYAML(tt.data, writer)
|
|
if gotWriter := writer.String(); gotWriter != tt.wantWriter {
|
|
t.Errorf("PrintYAML() = %v, want %v", gotWriter, tt.wantWriter)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestResetFlags(t *testing.T) {
|
|
flags := pflag.NewFlagSet("reset", pflag.ContinueOnError)
|
|
omittedBool, explicitBool, absentBool := new(true), new(true), new(true)
|
|
flags.BoolVar(omittedBool, "omitted-bool", true, "")
|
|
flags.BoolVar(explicitBool, "explicit-bool", true, "")
|
|
var omittedTags, explicitTags []string
|
|
absentTags := []string{"keep"}
|
|
flags.StringSliceVar(&omittedTags, "omitted-tags", []string{"default"}, "")
|
|
flags.StringSliceVar(&explicitTags, "explicit-tags", []string{"default"}, "")
|
|
if err := flags.Parse([]string{"--explicit-bool=false", "--explicit-tags="}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ResetFlags(map[string]**bool{
|
|
"omitted-bool": &omittedBool,
|
|
"explicit-bool": &explicitBool,
|
|
"absent-bool": &absentBool,
|
|
}, flags)
|
|
ResetFlags(map[string]*[]string{
|
|
"omitted-tags": &omittedTags,
|
|
"explicit-tags": &explicitTags,
|
|
"absent-tags": &absentTags,
|
|
}, flags)
|
|
|
|
if omittedBool != nil || omittedTags != nil {
|
|
t.Errorf("omitted flags must reset to nil: bool=%v tags=%v", omittedBool, omittedTags)
|
|
}
|
|
if explicitBool == nil || *explicitBool {
|
|
t.Errorf("explicit false must remain non-nil and false: %v", explicitBool)
|
|
}
|
|
if explicitTags == nil || len(explicitTags) != 0 {
|
|
t.Errorf("explicit empty tags must remain non-nil and empty: %#v", explicitTags)
|
|
}
|
|
if absentBool == nil || !*absentBool || !reflect.DeepEqual(absentTags, []string{"keep"}) {
|
|
t.Errorf("unregistered flags must remain unchanged: bool=%v tags=%v", absentBool, absentTags)
|
|
}
|
|
}
|
|
|
|
func TestExtractHl(t *testing.T) {
|
|
type args struct {
|
|
uri string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want string
|
|
}{
|
|
{
|
|
name: "valid language uri with hl",
|
|
args: args{uri: "i18n://language/zh-CN"},
|
|
want: "zh-CN",
|
|
},
|
|
{
|
|
name: "valid region uri with hl",
|
|
args: args{uri: "i18n://region/zh-CN"},
|
|
want: "zh-CN",
|
|
},
|
|
{
|
|
name: "valid language uri without hl",
|
|
args: args{uri: "i18n://language/"},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "valid region uri without hl",
|
|
args: args{uri: "i18n://region/"},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "invalid uri",
|
|
args: args{uri: "i18n://invalid/zh-CN"},
|
|
want: "",
|
|
},
|
|
{
|
|
name: "empty uri",
|
|
args: args{uri: ""},
|
|
want: "",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
if got := ExtractHl(tt.args.uri); got != tt.want {
|
|
t.Errorf("ExtractHl() = %v, want %v", got, tt.want)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestRandomStage(t *testing.T) {
|
|
s1 := RandomStage()
|
|
s2 := RandomStage()
|
|
if s1 == "" {
|
|
t.Error("RandomStage() returned empty string")
|
|
}
|
|
if s1 == s2 {
|
|
t.Error("RandomStage() returned same value twice")
|
|
}
|
|
}
|
|
|
|
func TestConfirmPreRun(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
yes bool
|
|
input string
|
|
interactive bool
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "yes flag bypasses prompt",
|
|
yes: true,
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "non-interactive without yes flag",
|
|
yes: false,
|
|
interactive: false,
|
|
wantErr: ErrNotConfirmed,
|
|
},
|
|
{
|
|
name: "interactive confirm with y",
|
|
yes: false,
|
|
interactive: true,
|
|
input: "y\n",
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "interactive confirm with Y",
|
|
yes: false,
|
|
interactive: true,
|
|
input: "Y\n",
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "interactive deny with n",
|
|
yes: false,
|
|
interactive: true,
|
|
input: "n\n",
|
|
wantErr: ErrNotConfirmed,
|
|
},
|
|
{
|
|
name: "interactive deny with empty",
|
|
yes: false,
|
|
interactive: true,
|
|
input: "\n",
|
|
wantErr: ErrNotConfirmed,
|
|
},
|
|
{
|
|
name: "interactive deny with EOF",
|
|
yes: false,
|
|
interactive: true,
|
|
input: "",
|
|
wantErr: ErrNotConfirmed,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
orig := IsInteractive
|
|
IsInteractive = func(any) bool { return tt.interactive }
|
|
defer func() { IsInteractive = orig }()
|
|
|
|
cmd := &cobra.Command{Use: "test"}
|
|
cmd.Flags().Bool("yes", false, "")
|
|
if tt.yes {
|
|
_ = cmd.Flags().Set("yes", "true")
|
|
}
|
|
|
|
var errBuf bytes.Buffer
|
|
cmd.SetErr(&errBuf)
|
|
cmd.SetIn(strings.NewReader(tt.input))
|
|
|
|
err := ConfirmPreRun(cmd, "Would do something")
|
|
if !errors.Is(err, tt.wantErr) {
|
|
t.Errorf("ConfirmPreRun() error = %v, want %v", err, tt.wantErr)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestHandleCmdError(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input error
|
|
wantOut string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "with error",
|
|
input: fmt.Errorf("some error"),
|
|
wantOut: "help called",
|
|
wantErr: "Error: some error\n",
|
|
},
|
|
{
|
|
name: "without error",
|
|
input: nil,
|
|
wantOut: "",
|
|
wantErr: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(
|
|
tt.name, func(t *testing.T) {
|
|
cmd := &cobra.Command{Use: "test"}
|
|
var outBuf, errBuf bytes.Buffer
|
|
cmd.SetOut(&outBuf)
|
|
cmd.SetErr(&errBuf)
|
|
cmd.SetHelpFunc(
|
|
func(c *cobra.Command, _ []string) {
|
|
_, _ = fmt.Fprint(c.OutOrStdout(), "help called")
|
|
},
|
|
)
|
|
|
|
HandleCmdError(tt.input, cmd)
|
|
|
|
if gotOut := outBuf.String(); gotOut != tt.wantOut {
|
|
t.Fatalf(
|
|
"unexpected stdout output, got %q, want %q", gotOut, tt.wantOut,
|
|
)
|
|
}
|
|
|
|
if gotErr := errBuf.String(); gotErr != tt.wantErr {
|
|
t.Fatalf(
|
|
"unexpected stderr output, got %q, want %q", gotErr, tt.wantErr,
|
|
)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|