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 }