added more tests
Go Build and Test / build-and-test (push) Successful in 25s

This commit is contained in:
wt
2025-08-12 16:55:55 +07:00
parent bca550374f
commit f854cbace5
8 changed files with 343 additions and 25 deletions
-23
View File
@@ -178,26 +178,3 @@ func TestTargetNaming(t *testing.T) {
}
}
}
func TestFormatBytes(t *testing.T) {
tests := []struct {
size int64
expected string
}{
{500, "500 B"},
{1024, "1.0 KiB"},
{1500, "1.5 KiB"},
{1048576, "1.0 MiB"},
{1572864, "1.5 MiB"},
{1073741824, "1.0 GiB"},
{1610612736, "1.5 GiB"},
{1099511627776, "1.0 TiB"},
}
for _, test := range tests {
result := formatBytes(test.size)
if result != test.expected {
t.Errorf("For %d bytes expected '%s', got '%s'", test.size, test.expected, result)
}
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ import (
"github.com/spf13/cobra"
)
var osExit = os.Exit
var rootCmd = &cobra.Command{
Use: "backup",
Short: "Backup your data",
@@ -14,6 +16,6 @@ var rootCmd = &cobra.Command{
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
osExit(1)
}
}
+67
View File
@@ -0,0 +1,67 @@
package cmd
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
)
// Вспомогательная структура для перехвата вызовов osExit
type exitCapture struct {
code int
called bool
}
func (e *exitCapture) capture(code int) {
e.code = code
e.called = true
}
func TestExecute_Success(t *testing.T) {
// Сохраняем оригинальный osExit
originalExit := osExit
defer func() { osExit = originalExit }()
// Настраиваем перехват
capture := &exitCapture{}
osExit = capture.capture
// Запускаем выполнение
Execute()
// Проверяем, что выход не вызывался
if capture.called {
t.Errorf("osExit called with code %d, expected no call", capture.code)
}
}
func TestExecute_Error(t *testing.T) {
originalExit := osExit
defer func() { osExit = originalExit }()
capture := &exitCapture{}
osExit = capture.capture
// Подменяем rootCmd на команду, возвращающую ошибку
originalCmd := rootCmd
defer func() { rootCmd = originalCmd }()
rootCmd = &cobra.Command{
Use: "error-cmd",
RunE: func(cmd *cobra.Command, args []string) error {
return assert.AnError // Имитация ошибки
},
}
// Вызываем Execute
Execute()
// Проверяем, что osExit вызван с кодом 1
if !capture.called {
t.Fatal("osExit not called")
}
if capture.code != 1 {
t.Errorf("expected exit code 1, got %d", capture.code)
}
}
+10
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"net"
"strconv"
"strings"
)
@@ -47,6 +48,15 @@ func parseServerTarget(target string) (*ServerTargetInfo, bool) {
return nil, false // Invalid format
}
addressport := strings.SplitN(parts[0], ":", 2)
if len(addressport) != 2 {
return nil, false // Invalid format
}
if _, err := strconv.Atoi(addressport[1]); err != nil {
return nil, false // Invalid port
}
address := parts[0]
filename := parts[1]
+229
View File
@@ -0,0 +1,229 @@
package cmd
import (
"bytes"
"io"
"testing"
"github.com/stretchr/testify/require"
)
func TestIsNetworkAddress(t *testing.T) {
tests := []struct {
name string
input string
want bool
}{
{"Valid IPv4 with port", "192.168.1.1:8080", true},
{"Valid hostname with port", "example.com:443", true},
{"IPv6 with port", "[::1]:8080", true},
{"Missing port", "127.0.0.1", false},
{"Port only", ":8080", true}, // технически валидный формат
{"Empty string", "", false},
{"Non-address string", "hello/world", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := isNetworkAddress(tt.input)
require.Equal(t, tt.want, got)
})
}
}
func TestFormatBytes(t *testing.T) {
tests := []struct {
size int64
expected string
}{
{500, "500 B"},
{1024, "1.0 KiB"},
{1500, "1.5 KiB"},
{1048576, "1.0 MiB"},
{1572864, "1.5 MiB"},
{1073741824, "1.0 GiB"},
{1610612736, "1.5 GiB"},
{1099511627776, "1.0 TiB"},
}
for _, test := range tests {
result := formatBytes(test.size)
if result != test.expected {
t.Errorf("For %d bytes expected '%s', got '%s'", test.size, test.expected, result)
}
}
}
func TestParseServerTarget(t *testing.T) {
tests := []struct {
name string
input string
wantInfo *ServerTargetInfo
wantSuccess bool
}{
{
name: "Valid address and filename",
input: "localhost:8080/backup.tar.gz",
wantInfo: &ServerTargetInfo{
URL: "http://localhost:8080/backup/backup.tar.gz",
Address: "localhost:8080",
Filename: "backup.tar.gz",
},
wantSuccess: true,
},
{
name: "Missing filename",
input: "127.0.0.1:8000/",
wantInfo: nil,
wantSuccess: false,
},
{
name: "Missing port",
input: "example.com/backup.gz",
wantInfo: nil,
wantSuccess: false,
},
{
name: "Invalid host:port",
input: "invalid_host:port/backup",
wantInfo: nil,
wantSuccess: false,
},
{
name: "Empty string",
input: "",
wantInfo: nil,
wantSuccess: false,
},
{
name: "No slash separator",
input: "localhost:8080",
wantInfo: nil,
wantSuccess: false,
},
{
name: "Multiple slashes",
input: "host:1234/path/to/backup.gz",
wantInfo: &ServerTargetInfo{
URL: "http://host:1234/backup/path/to/backup.gz",
Address: "host:1234",
Filename: "path/to/backup.gz",
},
wantSuccess: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotInfo, gotSuccess := parseServerTarget(tt.input)
require.Equal(t, tt.wantSuccess, gotSuccess)
if tt.wantSuccess {
require.Equal(t, tt.wantInfo, gotInfo)
} else {
require.Nil(t, gotInfo)
}
})
}
}
func TestProgressTracker(t *testing.T) {
t.Run("Write sends delta", func(t *testing.T) {
var buf bytes.Buffer
ch := make(chan int64, 10)
data := []byte("test data")
pt := &progressTracker{
Writer: &buf,
progressCh: ch,
}
n, err := pt.Write(data)
require.NoError(t, err)
require.Equal(t, len(data), n)
require.Equal(t, data, buf.Bytes())
require.Len(t, ch, 1)
require.Equal(t, int64(len(data)), <-ch)
})
t.Run("Read sends cumulative total", func(t *testing.T) {
data := []byte("sample data")
src := bytes.NewReader(data)
ch := make(chan int64, 10)
pt := &progressTracker{
Reader: src,
progressCh: ch,
}
// First read
buf1 := make([]byte, 6)
n1, err1 := pt.Read(buf1)
require.NoError(t, err1)
require.Equal(t, 6, n1)
// Second read
buf2 := make([]byte, 10)
n2, err2 := pt.Read(buf2)
require.NoError(t, err2)
require.Equal(t, 5, n2)
require.Len(t, ch, 2)
require.Equal(t, int64(6), <-ch)
require.Equal(t, int64(11), <-ch)
})
t.Run("Read handles EOF correctly", func(t *testing.T) {
src := bytes.NewReader([]byte{1, 2, 3})
ch := make(chan int64, 3)
pt := &progressTracker{
Reader: src,
progressCh: ch,
}
buf := make([]byte, 5)
// First read (3 bytes + no error)
n, err := pt.Read(buf)
require.NoError(t, err)
require.Equal(t, 3, n)
require.Equal(t, []byte{1, 2, 3}, buf[:n])
// Second read (0 bytes + EOF)
n, err = pt.Read(buf)
require.Equal(t, io.EOF, err)
require.Equal(t, 0, n)
// Only first read sends progress
require.Len(t, ch, 1)
require.Equal(t, int64(3), <-ch)
})
t.Run("Read with immediate EOF", func(t *testing.T) {
// Custom reader that returns 0 and EOF immediately
r := &eofReader{}
ch := make(chan int64, 1)
pt := &progressTracker{
Reader: r,
progressCh: ch,
}
buf := make([]byte, 5)
n, err := pt.Read(buf)
require.Equal(t, io.EOF, err)
require.Equal(t, 0, n)
// No progress should be sent
require.Len(t, ch, 0)
})
}
// eofReader всегда возвращает EOF
type eofReader struct{}
func (r *eofReader) Read(p []byte) (int, error) {
return 0, io.EOF
}
+4
View File
@@ -8,6 +8,7 @@ require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/gorilla/mux v1.8.1
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
golang.org/x/sys v0.30.0
)
@@ -18,6 +19,7 @@ require (
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
@@ -27,9 +29,11 @@ require (
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.3.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+3 -1
View File
@@ -2,6 +2,8 @@ package main
import "backup/cmd"
var execute = cmd.Execute
func main() {
cmd.Execute()
execute()
}
+27
View File
@@ -0,0 +1,27 @@
package main
import (
"testing"
)
func TestMainFunction(t *testing.T) {
// Сохраняем оригинальную функцию
originalExecute := execute
defer func() {
execute = originalExecute // Восстанавливаем после теста
}()
// Флаг для проверки вызова
called := false
execute = func() {
called = true // Подменяем функцию
}
// Запускаем main
main()
// Проверяем, что функция была вызвана
if !called {
t.Fatal("Expected cmd.Execute to be called, but it wasn't")
}
}