diff --git a/.github/workflows/commit-lint.yml b/.github/workflows/commit-lint.yml index 87f41bf..44b9e8c 100644 --- a/.github/workflows/commit-lint.yml +++ b/.github/workflows/commit-lint.yml @@ -16,6 +16,8 @@ on: - develop - feature/** - release/** + - test/** + - bugfix/** jobs: lint-commits: diff --git a/.gitignore b/.gitignore index 26e46a7..fa0d738 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.idea +/vendor +/coverage.txt # Binaries for programs and plugins *.exe *.exe~ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca4a860 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: install tests + +install: + go mod tidy && go mod vendor + +tests: + go test -v ./tests \ No newline at end of file diff --git a/array.go b/array.go index 0ecd76c..cbc9b84 100644 --- a/array.go +++ b/array.go @@ -14,6 +14,10 @@ func InArray[T comparable](needle T, haystack []T) bool { } func Explode(separator string, stringStr string) []string { + if separator == "" { + return []string{stringStr} + } + return strings.Split(stringStr, separator) } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..6fe5e65 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + target: 90% + patch: + default: + target: 90% \ No newline at end of file diff --git a/tests/array_test.go b/tests/array_test.go new file mode 100644 index 0000000..c6dc971 --- /dev/null +++ b/tests/array_test.go @@ -0,0 +1,111 @@ +package tests + +import ( + "fmt" + "github.com/gouef/utils" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestInArray(t *testing.T) { + tests := []struct { + needle string + haystack []string + expected bool + }{ + {"apple", []string{"apple", "banana", "cherry"}, true}, + {"orange", []string{"apple", "banana", "cherry"}, false}, + {"", []string{"apple", "banana", "cherry"}, false}, + } + + for _, tt := range tests { + t.Run(tt.needle, func(t *testing.T) { + result := utils.InArray(tt.needle, tt.haystack) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestExplode(t *testing.T) { + tests := []struct { + separator string + input string + expected []string + }{ + {",", "apple,banana,cherry", []string{"apple", "banana", "cherry"}}, + {" ", "apple banana cherry", []string{"apple", "banana", "cherry"}}, + {";", "apple;banana;cherry", []string{"apple", "banana", "cherry"}}, + {"", "apple", []string{"apple"}}, + } + + for _, tt := range tests { + t.Run(tt.separator, func(t *testing.T) { + result := utils.Explode(tt.separator, tt.input) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestImplode(t *testing.T) { + tests := []struct { + separator string + elements []string + expected string + }{ + {",", []string{"apple", "banana", "cherry"}, "apple,banana,cherry"}, + {" ", []string{"apple", "banana", "cherry"}, "apple banana cherry"}, + {";", []string{"apple", "banana", "cherry"}, "apple;banana;cherry"}, + {"", []string{"apple"}, "apple"}, + } + + for _, tt := range tests { + t.Run(tt.separator, func(t *testing.T) { + result := utils.Implode(tt.separator, tt.elements) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestSlice(t *testing.T) { + tests := []struct { + arr []int + start int + length int + expected []int + }{ + {[]int{1, 2, 3, 4, 5}, 1, 3, []int{2, 3, 4}}, + {[]int{1, 2, 3, 4, 5}, -2, 3, []int{4, 5}}, + {[]int{1, 2, 3, 4, 5}, 0, 2, []int{1, 2}}, + {[]int{1, 2, 3, 4, 5}, 2, 5, []int{3, 4, 5}}, + {[]int{1, 2, 3, 4, 5}, -6, 2, nil}, // out of range + {[]int{1, 2, 3, 4, 5}, 3, 10, []int{4, 5}}, // length beyond array length + {[]int{}, 0, 1, []int{}}, // empty array + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("start:%d length:%d", tt.start, tt.length), func(t *testing.T) { + result := utils.Slice(tt.arr, tt.start, tt.length) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestIsset(t *testing.T) { + tests := []struct { + array map[interface{}]interface{} + key interface{} + expected bool + }{ + {map[interface{}]interface{}{"a": 1, "b": 2}, "a", true}, + {map[interface{}]interface{}{"a": 1, "b": 2}, "c", false}, + {map[interface{}]interface{}{"a": 1, "b": 2}, 1, false}, + {map[interface{}]interface{}{"a": 1, "b": 2}, "b", true}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("key:%v", tt.key), func(t *testing.T) { + result := utils.Isset(tt.array, tt.key) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/tests/ip_test.go b/tests/ip_test.go new file mode 100644 index 0000000..85920a5 --- /dev/null +++ b/tests/ip_test.go @@ -0,0 +1,53 @@ +package tests + +import ( + "fmt" + "github.com/gouef/utils" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestIp2long(t *testing.T) { + tests := []struct { + ip string + expected uint32 + err error + }{ + {"192.168.1.1", 3232235777, nil}, // Standardní IPv4 adresa + {"255.255.255.255", 4294967295, nil}, // Maximalní IPv4 adresa + {"0.0.0.0", 0, nil}, // Minimální IPv4 adresa + {"invalid_ip", 0, fmt.Errorf("invalid IP address: invalid_ip")}, // Neplatná adresa + {"256.256.256.256", 0, fmt.Errorf("invalid IP address: 256.256.256.256")}, // Neplatná adresa + {"fe80::1", 0, fmt.Errorf("not an IPv4 address: fe80::1")}, // IPv6 adresa + } + + for _, tt := range tests { + t.Run(tt.ip, func(t *testing.T) { + result, err := utils.Ip2long(tt.ip) + if tt.err != nil { + assert.EqualError(t, err, tt.err.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expected, result) + } + }) + } +} + +func TestLong2ip(t *testing.T) { + tests := []struct { + ipLong uint32 + expected string + }{ + {3232235777, "192.168.1.1"}, + {4294967295, "255.255.255.255"}, + {0, "0.0.0.0"}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("%d", tt.ipLong), func(t *testing.T) { + result := utils.Long2ip(tt.ipLong) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/math_test.go b/tests/math_test.go similarity index 72% rename from math_test.go rename to tests/math_test.go index b3f70fa..6ad56f0 100644 --- a/math_test.go +++ b/tests/math_test.go @@ -1,23 +1,24 @@ -package utils +package tests import ( + "github.com/gouef/utils" "github.com/stretchr/testify/assert" "testing" ) func TestRound(t *testing.T) { - result := RoundTo(1234, 1) + result := utils.RoundTo(1234, 1) assert.Equal(t, float64(1230), result, "Expected 1230") - result1 := RoundTo(1234, 2) + result1 := utils.RoundTo(1234, 2) assert.Equal(t, float64(1200), result1, "Expected 1200") - result2 := RoundTo(1234, 3) + result2 := utils.RoundTo(1234, 3) assert.Equal(t, float64(1000), result2, "Expected 1000") } func TestRoundTens(t *testing.T) { - result := RoundTens(123) + result := utils.RoundTens(123) assert.Equal(t, float64(120), result, "Expected 120") } diff --git a/tests/strings_test.go b/tests/strings_test.go new file mode 100644 index 0000000..b53d03b --- /dev/null +++ b/tests/strings_test.go @@ -0,0 +1,90 @@ +package tests + +import ( + "testing" + + "github.com/gouef/utils" + "github.com/stretchr/testify/assert" +) + +func TestSubstr(t *testing.T) { + tests := []struct { + input string + start int + length *int + expected string + }{ + {"hello world", 0, nil, "hello world"}, // Celý řetězec + {"hello world", 0, ptr(5), "hello"}, // Začátek + délka + {"hello world", 6, ptr(5), "world"}, // Od určité pozice + {"hello world", -5, ptr(3), "wor"}, // Záporný start + {"hello world", -5, ptr(10), "world"}, // Záporný start přesahující délku + {"hello world", 11, ptr(5), ""}, // Start mimo délku řetězce + {"hello world", -12, ptr(5), ""}, // Záporný start mimo délku řetězce + {"hello world", 3, ptr(20), "lo world"}, // Délka přesahující konec + } + + for _, tt := range tests { + result := utils.Substr(tt.input, tt.start, tt.length) + assert.Equal(t, tt.expected, result, "Input: %s, Start: %d, Length: %v", tt.input, tt.start, tt.length) + + // Test, že Substring funguje stejně + resultSubstring := utils.Substring(tt.input, tt.start, tt.length) + assert.Equal(t, tt.expected, resultSubstring, "Substring failed: Input: %s, Start: %d, Length: %v", tt.input, tt.start, tt.length) + } +} + +// Pomocná funkce pro ukazatel na int +func ptr(i int) *int { + return &i +} + +func TestStrpos(t *testing.T) { + tests := []struct { + stringStr string + needle string + expected int + }{ + {"hello world", "world", 6}, // Výskyt podřetězce + {"hello world", "hello", 0}, // Výskyt na začátku + {"hello world hello", "hello", 12}, // Poslední výskyt + {"hello world", "x", -1}, // Žádný výskyt + {"hello", "", 5}, // Prázdný needle + {"", "hello", -1}, // Prázdný řetězec + } + + for _, tt := range tests { + result := utils.Strpos(tt.stringStr, tt.needle) + assert.Equal(t, tt.expected, result, "Strpos failed: Input: %s, Needle: %s", tt.stringStr, tt.needle) + + // Test, že StringPosition funguje stejně + resultPosition := utils.StringPosition(tt.stringStr, tt.needle) + assert.Equal(t, tt.expected, resultPosition, "StringPosition failed: Input: %s, Needle: %s", tt.stringStr, tt.needle) + } +} + +func TestStrncmp(t *testing.T) { + tests := []struct { + s1 string + s2 string + n int + expected int + }{ + {"hello", "hello", 5, 0}, // Stejné řetězce + {"hello", "hella", 5, 1}, // Rozdílné znaky na konci + {"hello", "hellz", 4, 0}, // Porovnání prvních 4 znaků + {"hello", "world", 3, -1}, // Rozdílné řetězce + {"hello", "hello world", 5, 0}, // Prvních 5 znaků je stejné + {"hello", "he", 5, 1}, // Jeden řetězec kratší + {"he", "hello", 5, -1}, // Opačný případ + } + + for _, tt := range tests { + result := utils.Strncmp(tt.s1, tt.s2, tt.n) + assert.Equal(t, tt.expected, result, "Strncmp failed: S1: %s, S2: %s, N: %d", tt.s1, tt.s2, tt.n) + + // Test, že StringCompare funguje stejně + resultCompare := utils.StringCompare(tt.s1, tt.s2, tt.n) + assert.Equal(t, tt.expected, resultCompare, "StringCompare failed: S1: %s, S2: %s, N: %d", tt.s1, tt.s2, tt.n) + } +} diff --git a/tests/translator_test.go b/tests/translator_test.go new file mode 100644 index 0000000..ca8701d --- /dev/null +++ b/tests/translator_test.go @@ -0,0 +1 @@ +package tests diff --git a/tests/types_test.go b/tests/types_test.go new file mode 100644 index 0000000..930cf3b --- /dev/null +++ b/tests/types_test.go @@ -0,0 +1,50 @@ +package tests + +import ( + "testing" + + "github.com/gouef/utils" + "github.com/stretchr/testify/assert" +) + +func TestDetectType(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"123", int32(123)}, + {"2147483647", int32(2147483647)}, // Max int32 + {"9223372036854775807", int64(9223372036854775807)}, // Max int64 + {"3.14", float32(3.14)}, + {"2.718", float32(2.718)}, + {"1.7976931348623157e+308", float64(1.7976931348623157e+308)}, // Max float64 + {"true", true}, + {"false", false}, + {"hello", "hello"}, + } + + for _, tt := range tests { + result := utils.DetectType(tt.input) + assert.Equal(t, tt.expected, result, "Input: %s", tt.input) + } +} + +func TestIsInt(t *testing.T) { + tests := []struct { + input string + expectedOk bool + expected int + }{ + {"123", true, 123}, + {"2147483647", true, 2147483647}, // Max int32 + {"9223372036854775807", false, 0}, // Max int64 (bude vráceno false) + {"hello", false, 0}, + {"3.14", false, 0}, + } + + for _, tt := range tests { + ok, result := utils.IsInt(tt.input) + assert.Equal(t, tt.expectedOk, ok, "Input: %s", tt.input) + assert.Equal(t, tt.expected, result, "Input: %s", tt.input) + } +} diff --git a/tests/utils_test.go b/tests/utils_test.go new file mode 100644 index 0000000..ca8701d --- /dev/null +++ b/tests/utils_test.go @@ -0,0 +1 @@ +package tests diff --git a/translator_test.go b/translator_test.go deleted file mode 100644 index d4b585b..0000000 --- a/translator_test.go +++ /dev/null @@ -1 +0,0 @@ -package utils diff --git a/types.go b/types.go index 69ea7dd..f7fb505 100644 --- a/types.go +++ b/types.go @@ -1,28 +1,25 @@ package utils -import "strconv" +import ( + "math" + "strconv" +) func DetectType(input string) interface{} { - if i, err := strconv.Atoi(input); err == nil { - return i - } - - if i, err := strconv.ParseInt(input, 10, 32); err == nil { - return int32(i) - } - if i, err := strconv.ParseInt(input, 10, 64); err == nil { + if i >= math.MinInt32 && i <= math.MaxInt32 { + return int32(i) + } return int64(i) } if f, err := strconv.ParseFloat(input, 64); err == nil { + if f >= -math.MaxFloat32 && f <= math.MaxFloat32 { + return float32(f) + } return f } - if f, err := strconv.ParseFloat(input, 32); err == nil { - return float32(f) - } - if b, err := strconv.ParseBool(input); err == nil { return b } @@ -32,5 +29,8 @@ func DetectType(input string) interface{} { func IsInt(input string) (bool, int) { i, err := strconv.Atoi(input) - return err == nil, i + if err != nil || i < math.MinInt32 || i > math.MaxInt32 { + return false, 0 + } + return true, i } diff --git a/utils_test.go b/utils_test.go deleted file mode 100644 index d4b585b..0000000 --- a/utils_test.go +++ /dev/null @@ -1 +0,0 @@ -package utils