mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
tests: fixed several tests
This commit is contained in:
parent
98777205a5
commit
d4acd99fa7
10 changed files with 137 additions and 97 deletions
|
@ -11,14 +11,12 @@ import (
|
||||||
|
|
||||||
var ep = NewEntrypoint()
|
var ep = NewEntrypoint()
|
||||||
|
|
||||||
func addRoute(alias string) *route.ReveseProxyRoute {
|
func addRoute(alias string) {
|
||||||
r := &route.ReveseProxyRoute{
|
routes.HTTP.Add(&route.ReveseProxyRoute{
|
||||||
Route: &route.Route{
|
Route: &route.Route{
|
||||||
Alias: alias,
|
Alias: alias,
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
routes.HTTP.Add(r)
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(t *testing.T, match []string, noMatch []string) {
|
func run(t *testing.T, match []string, noMatch []string) {
|
||||||
|
@ -28,10 +26,9 @@ func run(t *testing.T, match []string, noMatch []string) {
|
||||||
|
|
||||||
for _, test := range match {
|
for _, test := range match {
|
||||||
t.Run(test, func(t *testing.T) {
|
t.Run(test, func(t *testing.T) {
|
||||||
r := addRoute(test)
|
|
||||||
found, err := ep.findRouteFunc(test)
|
found, err := ep.findRouteFunc(test)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.True(t, found == r)
|
expect.NotNil(t, found)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +109,7 @@ func TestFindRouteByDomainsExactMatch(t *testing.T) {
|
||||||
".sub.domain.com",
|
".sub.domain.com",
|
||||||
})
|
})
|
||||||
|
|
||||||
addRoute("app1")
|
addRoute("app1.foo.bar")
|
||||||
|
|
||||||
tests := []string{
|
tests := []string{
|
||||||
"app1.foo.bar", // exact match
|
"app1.foo.bar", // exact match
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package systeminfo
|
package systeminfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -295,48 +294,46 @@ func (s *SystemInfo) collectSensorsInfo(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicitly implement MarshalJSON to avoid reflection
|
// explicitly implement MarshalJSON to avoid reflection
|
||||||
func (s *SystemInfo) MarshalJSONTo(buf []byte) []byte {
|
func (s *SystemInfo) MarshalJSONTo(b []byte) []byte {
|
||||||
b := bytes.NewBuffer(buf)
|
b = append(b, '{')
|
||||||
|
|
||||||
b.WriteRune('{')
|
|
||||||
|
|
||||||
// timestamp
|
// timestamp
|
||||||
b.WriteString(`"timestamp":`)
|
b = append(b, `"timestamp":`...)
|
||||||
b.WriteString(strconv.FormatInt(s.Timestamp, 10))
|
b = strconv.AppendInt(b, s.Timestamp, 10)
|
||||||
|
|
||||||
// cpu_average
|
// cpu_average
|
||||||
b.WriteString(`,"cpu_average":`)
|
b = append(b, `,"cpu_average":`...)
|
||||||
if s.CPUAverage != nil {
|
if s.CPUAverage != nil {
|
||||||
b.WriteString(strconv.FormatFloat(*s.CPUAverage, 'f', 2, 64))
|
b = strconv.AppendFloat(b, *s.CPUAverage, 'f', 2, 64)
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// memory
|
// memory
|
||||||
b.WriteString(`,"memory":`)
|
b = append(b, `,"memory":`...)
|
||||||
if s.Memory != nil {
|
if s.Memory != nil {
|
||||||
b.Write(fmt.Appendf(nil,
|
b = fmt.Appendf(b,
|
||||||
`{"total":%d,"available":%d,"used":%d,"used_percent":%s}`,
|
`{"total":%d,"available":%d,"used":%d,"used_percent":%s}`,
|
||||||
s.Memory.Total,
|
s.Memory.Total,
|
||||||
s.Memory.Available,
|
s.Memory.Available,
|
||||||
s.Memory.Used,
|
s.Memory.Used,
|
||||||
strconv.FormatFloat(s.Memory.UsedPercent, 'f', 2, 64),
|
strconv.FormatFloat(s.Memory.UsedPercent, 'f', 2, 64),
|
||||||
))
|
)
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// disk
|
// disk
|
||||||
b.WriteString(`,"disks":`)
|
b = append(b, `,"disks":`...)
|
||||||
if len(s.Disks) > 0 {
|
if len(s.Disks) > 0 {
|
||||||
b.WriteRune('{')
|
b = append(b, '{')
|
||||||
first := true
|
first := true
|
||||||
for device, disk := range s.Disks {
|
for device, disk := range s.Disks {
|
||||||
if !first {
|
if !first {
|
||||||
b.WriteRune(',')
|
b = append(b, ',')
|
||||||
}
|
}
|
||||||
b.Write(fmt.Appendf(nil,
|
b = fmt.Appendf(b,
|
||||||
`"%s":{"device":%q,"path":%q,"fstype":%q,"total":%d,"free":%d,"used":%d,"used_percent":%s}`,
|
`"%s":{"device":%q,"path":%q,"fstype":%q,"total":%d,"free":%d,"used":%d,"used_percent":%.2f}`,
|
||||||
device,
|
device,
|
||||||
device,
|
device,
|
||||||
disk.Path,
|
disk.Path,
|
||||||
|
@ -344,81 +341,81 @@ func (s *SystemInfo) MarshalJSONTo(buf []byte) []byte {
|
||||||
disk.Total,
|
disk.Total,
|
||||||
disk.Free,
|
disk.Free,
|
||||||
disk.Used,
|
disk.Used,
|
||||||
strconv.FormatFloat(float64(disk.UsedPercent), 'f', 2, 32),
|
disk.UsedPercent,
|
||||||
))
|
)
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
b.WriteRune('}')
|
b = append(b, '}')
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// disks_io
|
// disks_io
|
||||||
b.WriteString(`,"disks_io":`)
|
b = append(b, `,"disks_io":`...)
|
||||||
if len(s.DisksIO) > 0 {
|
if len(s.DisksIO) > 0 {
|
||||||
b.WriteString("{")
|
b = append(b, '{')
|
||||||
first := true
|
first := true
|
||||||
for name, usage := range s.DisksIO {
|
for name, usage := range s.DisksIO {
|
||||||
if !first {
|
if !first {
|
||||||
b.WriteRune(',')
|
b = append(b, ',')
|
||||||
}
|
}
|
||||||
b.Write(fmt.Appendf(nil,
|
b = fmt.Appendf(b,
|
||||||
`"%s":{"name":%q,"read_bytes":%d,"write_bytes":%d,"read_speed":%s,"write_speed":%s,"iops":%d}`,
|
`"%s":{"name":%q,"read_bytes":%d,"write_bytes":%d,"read_speed":%.2f,"write_speed":%.2f,"iops":%d}`,
|
||||||
name,
|
name,
|
||||||
name,
|
name,
|
||||||
usage.ReadBytes,
|
usage.ReadBytes,
|
||||||
usage.WriteBytes,
|
usage.WriteBytes,
|
||||||
strconv.FormatFloat(usage.ReadSpeed, 'f', 2, 64),
|
usage.ReadSpeed,
|
||||||
strconv.FormatFloat(usage.WriteSpeed, 'f', 2, 64),
|
usage.WriteSpeed,
|
||||||
usage.Iops,
|
usage.Iops,
|
||||||
))
|
)
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
b.WriteRune('}')
|
b = append(b, '}')
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// network
|
// network
|
||||||
b.WriteString(`,"network":`)
|
b = append(b, `,"network":`...)
|
||||||
if s.Network != nil {
|
if s.Network != nil {
|
||||||
b.Write(fmt.Appendf(nil,
|
b = fmt.Appendf(b,
|
||||||
`{"bytes_sent":%d,"bytes_recv":%d,"upload_speed":%s,"download_speed":%s}`,
|
`{"bytes_sent":%d,"bytes_recv":%d,"upload_speed":%.2f,"download_speed":%.2f}`,
|
||||||
s.Network.BytesSent,
|
s.Network.BytesSent,
|
||||||
s.Network.BytesRecv,
|
s.Network.BytesRecv,
|
||||||
strconv.FormatFloat(s.Network.UploadSpeed, 'f', 2, 64),
|
s.Network.UploadSpeed,
|
||||||
strconv.FormatFloat(s.Network.DownloadSpeed, 'f', 2, 64),
|
s.Network.DownloadSpeed,
|
||||||
))
|
)
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sensors
|
// sensors
|
||||||
b.WriteString(`,"sensors":`)
|
b = append(b, `,"sensors":`...)
|
||||||
if len(s.Sensors) > 0 {
|
if len(s.Sensors) > 0 {
|
||||||
b.WriteRune('{')
|
b = append(b, '{')
|
||||||
first := true
|
first := true
|
||||||
for _, sensor := range s.Sensors {
|
for _, sensor := range s.Sensors {
|
||||||
if !first {
|
if !first {
|
||||||
b.WriteRune(',')
|
b = append(b, ',')
|
||||||
}
|
}
|
||||||
b.Write(fmt.Appendf(nil,
|
b = fmt.Appendf(b,
|
||||||
`%q:{"name":%q,"temperature":%s,"high":%s,"critical":%s}`,
|
`"%s":{"name":%q,"temperature":%.2f,"high":%.2f,"critical":%.2f}`,
|
||||||
sensor.SensorKey,
|
sensor.SensorKey,
|
||||||
sensor.SensorKey,
|
sensor.SensorKey,
|
||||||
strconv.FormatFloat(float64(sensor.Temperature), 'f', 2, 32),
|
sensor.Temperature,
|
||||||
strconv.FormatFloat(float64(sensor.High), 'f', 2, 32),
|
sensor.High,
|
||||||
strconv.FormatFloat(float64(sensor.Critical), 'f', 2, 32),
|
sensor.Critical,
|
||||||
))
|
)
|
||||||
first = false
|
first = false
|
||||||
}
|
}
|
||||||
b.WriteRune('}')
|
b = append(b, '}')
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("null")
|
b = append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteRune('}')
|
b = append(b, '}')
|
||||||
return b.Bytes()
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sensors) UnmarshalJSON(data []byte) error {
|
func (s *Sensors) UnmarshalJSON(data []byte) error {
|
||||||
|
|
|
@ -49,6 +49,9 @@ func (p *DockerProvider) ShortName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DockerProvider) IsExplicitOnly() bool {
|
func (p *DockerProvider) IsExplicitOnly() bool {
|
||||||
|
if p.name == "" { // tests
|
||||||
|
return false
|
||||||
|
}
|
||||||
return p.name[len(p.name)-1] == '!'
|
return p.name[len(p.name)-1] == '!'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,10 @@ import (
|
||||||
var testDockerLabelsYAML []byte
|
var testDockerLabelsYAML []byte
|
||||||
|
|
||||||
func TestParseDockerLabels(t *testing.T) {
|
func TestParseDockerLabels(t *testing.T) {
|
||||||
var provider DockerProvider
|
provider := &DockerProvider{
|
||||||
|
name: "test",
|
||||||
|
dockerHost: "unix:///var/run/docker.sock",
|
||||||
|
}
|
||||||
|
|
||||||
labels := make(map[string]string)
|
labels := make(map[string]string)
|
||||||
expect.NoError(t, yaml.Unmarshal(testDockerLabelsYAML, &labels))
|
expect.NoError(t, yaml.Unmarshal(testDockerLabelsYAML, &labels))
|
||||||
|
|
|
@ -22,6 +22,7 @@ const (
|
||||||
|
|
||||||
func makeRoutes(cont *container.Summary, dockerHostIP ...string) route.Routes {
|
func makeRoutes(cont *container.Summary, dockerHostIP ...string) route.Routes {
|
||||||
var p DockerProvider
|
var p DockerProvider
|
||||||
|
|
||||||
var host string
|
var host string
|
||||||
if len(dockerHostIP) > 0 {
|
if len(dockerHostIP) > 0 {
|
||||||
host = "tcp://" + dockerHostIP[0] + ":2375"
|
host = "tcp://" + dockerHostIP[0] + ":2375"
|
||||||
|
@ -30,6 +31,7 @@ func makeRoutes(cont *container.Summary, dockerHostIP ...string) route.Routes {
|
||||||
}
|
}
|
||||||
cont.ID = "test"
|
cont.ID = "test"
|
||||||
p.name = "test"
|
p.name = "test"
|
||||||
|
p.dockerHost = host
|
||||||
routes := expect.Must(p.routesFromContainerLabels(D.FromDocker(cont, host)))
|
routes := expect.Must(p.routesFromContainerLabels(D.FromDocker(cont, host)))
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
r.Finalize()
|
r.Finalize()
|
||||||
|
@ -67,7 +69,7 @@ func TestApplyLabel(t *testing.T) {
|
||||||
Names: dummyNames,
|
Names: dummyNames,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
D.LabelAliases: "a,b",
|
D.LabelAliases: "a,b",
|
||||||
D.LabelIdleTimeout: "",
|
D.LabelIdleTimeout: "10s",
|
||||||
D.LabelStopMethod: "stop",
|
D.LabelStopMethod: "stop",
|
||||||
D.LabelStopSignal: "SIGTERM",
|
D.LabelStopSignal: "SIGTERM",
|
||||||
D.LabelStopTimeout: "1h",
|
D.LabelStopTimeout: "1h",
|
||||||
|
@ -109,8 +111,13 @@ func TestApplyLabel(t *testing.T) {
|
||||||
expect.Equal(t, a.Middlewares, middlewaresExpect)
|
expect.Equal(t, a.Middlewares, middlewaresExpect)
|
||||||
expect.Equal(t, len(b.Middlewares), 0)
|
expect.Equal(t, len(b.Middlewares), 0)
|
||||||
|
|
||||||
expect.Equal(t, a.Container.IdlewatcherConfig.IdleTimeout, 0)
|
expect.NotNil(t, a.Container)
|
||||||
expect.Equal(t, b.Container.IdlewatcherConfig.IdleTimeout, 0)
|
expect.NotNil(t, b.Container)
|
||||||
|
expect.NotNil(t, a.Container.IdlewatcherConfig)
|
||||||
|
expect.NotNil(t, b.Container.IdlewatcherConfig)
|
||||||
|
|
||||||
|
expect.Equal(t, a.Container.IdlewatcherConfig.IdleTimeout, 10*time.Second)
|
||||||
|
expect.Equal(t, b.Container.IdlewatcherConfig.IdleTimeout, 10*time.Second)
|
||||||
expect.Equal(t, a.Container.IdlewatcherConfig.StopTimeout, time.Hour)
|
expect.Equal(t, a.Container.IdlewatcherConfig.StopTimeout, time.Hour)
|
||||||
expect.Equal(t, b.Container.IdlewatcherConfig.StopTimeout, time.Hour)
|
expect.Equal(t, b.Container.IdlewatcherConfig.StopTimeout, time.Hour)
|
||||||
expect.Equal(t, a.Container.IdlewatcherConfig.StopMethod, "stop")
|
expect.Equal(t, a.Container.IdlewatcherConfig.StopMethod, "stop")
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
expect "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseCommands(t *testing.T) {
|
func TestParseCommands(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input string
|
||||||
|
@ -42,7 +44,7 @@ func TestParseCommands(t *testing.T) {
|
||||||
// serve tests
|
// serve tests
|
||||||
{
|
{
|
||||||
name: "serve_valid",
|
name: "serve_valid",
|
||||||
input: "serve /var/www",
|
input: "serve " + tmpDir,
|
||||||
wantErr: nil,
|
wantErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -55,6 +57,11 @@ func TestParseCommands(t *testing.T) {
|
||||||
input: "serve / / /",
|
input: "serve / / /",
|
||||||
wantErr: ErrInvalidArguments,
|
wantErr: ErrInvalidArguments,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "serve_non_existent_path",
|
||||||
|
input: "serve " + tmpDir + "/non-existent",
|
||||||
|
wantErr: os.ErrNotExist,
|
||||||
|
},
|
||||||
// redirect tests
|
// redirect tests
|
||||||
{
|
{
|
||||||
name: "redirect_valid",
|
name: "redirect_valid",
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
var s2 S
|
var s2 S
|
||||||
err := MapUnmarshalValidate(testStructSerialized, &s2)
|
err := MapUnmarshalValidate(testStructSerialized, &s2)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, s2, testStruct)
|
expect.Equal(t, s2, testStruct)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,15 +65,15 @@ func TestUnmarshalAnonymousField(t *testing.T) {
|
||||||
// t.Fatalf("anon %v, all %v", anon, all)
|
// t.Fatalf("anon %v, all %v", anon, all)
|
||||||
err := MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s)
|
err := MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, s.A, 1)
|
expect.Equal(t, s.A, 1)
|
||||||
expect.Values(t, s.B, 2)
|
expect.Equal(t, s.B, 2)
|
||||||
expect.Values(t, s.C, 3)
|
expect.Equal(t, s.C, 3)
|
||||||
|
|
||||||
err = MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s2)
|
err = MapUnmarshalValidate(map[string]any{"a": 1, "b": 2, "c": 3}, &s2)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, s2.A, 1)
|
expect.Equal(t, s2.A, 1)
|
||||||
expect.Values(t, s2.B, 2)
|
expect.Equal(t, s2.B, 2)
|
||||||
expect.Values(t, s2.C, 3)
|
expect.Equal(t, s2.C, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringIntConvert(t *testing.T) {
|
func TestStringIntConvert(t *testing.T) {
|
||||||
|
@ -95,11 +95,11 @@ func TestStringIntConvert(t *testing.T) {
|
||||||
ok, err := ConvertString("127", field)
|
ok, err := ConvertString("127", field)
|
||||||
expect.True(t, ok)
|
expect.True(t, ok)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, field.Interface(), 127)
|
expect.Equal(t, field.Interface(), 127)
|
||||||
|
|
||||||
err = Convert(reflect.ValueOf(uint8(64)), field)
|
err = Convert(reflect.ValueOf(uint8(64)), field)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, field.Interface(), 64)
|
expect.Equal(t, field.Interface(), 64)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,19 +125,19 @@ func TestConvertor(t *testing.T) {
|
||||||
m := new(testModel)
|
m := new(testModel)
|
||||||
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
||||||
|
|
||||||
expect.Values(t, m.Test.foo, 123)
|
expect.Equal(t, m.Test.foo, 123)
|
||||||
expect.Values(t, m.Test.bar, "123")
|
expect.Equal(t, m.Test.bar, "123")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("int_to_string", func(t *testing.T) {
|
t.Run("int_to_string", func(t *testing.T) {
|
||||||
m := new(testModel)
|
m := new(testModel)
|
||||||
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Test": "123"}, m))
|
||||||
|
|
||||||
expect.Values(t, m.Test.foo, 123)
|
expect.Equal(t, m.Test.foo, 123)
|
||||||
expect.Values(t, m.Test.bar, "123")
|
expect.Equal(t, m.Test.bar, "123")
|
||||||
|
|
||||||
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Baz": 456}, m))
|
expect.NoError(t, MapUnmarshalValidate(map[string]any{"Baz": 456}, m))
|
||||||
expect.Values(t, m.Baz, "456")
|
expect.Equal(t, m.Baz, "456")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid", func(t *testing.T) {
|
t.Run("invalid", func(t *testing.T) {
|
||||||
|
@ -152,21 +152,21 @@ func TestStringToSlice(t *testing.T) {
|
||||||
convertible, err := ConvertString("a,b,c", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("a,b,c", reflect.ValueOf(&dst))
|
||||||
expect.True(t, convertible)
|
expect.True(t, convertible)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, dst, []string{"a", "b", "c"})
|
expect.Equal(t, dst, []string{"a", "b", "c"})
|
||||||
})
|
})
|
||||||
t.Run("yaml-like", func(t *testing.T) {
|
t.Run("yaml-like", func(t *testing.T) {
|
||||||
dst := make([]string, 0)
|
dst := make([]string, 0)
|
||||||
convertible, err := ConvertString("- a\n- b\n- c", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("- a\n- b\n- c", reflect.ValueOf(&dst))
|
||||||
expect.True(t, convertible)
|
expect.True(t, convertible)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, dst, []string{"a", "b", "c"})
|
expect.Equal(t, dst, []string{"a", "b", "c"})
|
||||||
})
|
})
|
||||||
t.Run("single-line-yaml-like", func(t *testing.T) {
|
t.Run("single-line-yaml-like", func(t *testing.T) {
|
||||||
dst := make([]string, 0)
|
dst := make([]string, 0)
|
||||||
convertible, err := ConvertString("- a", reflect.ValueOf(&dst))
|
convertible, err := ConvertString("- a", reflect.ValueOf(&dst))
|
||||||
expect.True(t, convertible)
|
expect.True(t, convertible)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, dst, []string{"a"})
|
expect.Equal(t, dst, []string{"a"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ func TestStringToMap(t *testing.T) {
|
||||||
convertible, err := ConvertString(" a: b\n c: d", reflect.ValueOf(&dst))
|
convertible, err := ConvertString(" a: b\n c: d", reflect.ValueOf(&dst))
|
||||||
expect.True(t, convertible)
|
expect.True(t, convertible)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, dst, map[string]string{"a": "b", "c": "d"})
|
expect.Equal(t, dst, map[string]string{"a": "b", "c": "d"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +218,8 @@ func TestStringToStruct(t *testing.T) {
|
||||||
convertible, err := ConvertString(" A: a\n B: 123", reflect.ValueOf(&dst))
|
convertible, err := ConvertString(" A: a\n B: 123", reflect.ValueOf(&dst))
|
||||||
expect.True(t, convertible)
|
expect.True(t, convertible)
|
||||||
expect.NoError(t, err)
|
expect.NoError(t, err)
|
||||||
expect.Values(t, dst.A, "a")
|
expect.Equal(t, dst.A, "a")
|
||||||
expect.Values(t, dst.B, 123)
|
expect.Equal(t, dst.B, 123)
|
||||||
})
|
})
|
||||||
|
|
||||||
type T2 struct {
|
type T2 struct {
|
||||||
|
|
|
@ -9,6 +9,13 @@ import (
|
||||||
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AppendDuration appends a duration to a buffer with the following format:
|
||||||
|
// - 1 ns
|
||||||
|
// - 1 ms
|
||||||
|
// - 1 seconds
|
||||||
|
// - 1 minutes and 1 seconds
|
||||||
|
// - 1 hours, 1 minutes and 1 seconds
|
||||||
|
// - 1 days, 1 hours and 1 minutes (ignore seconds if days >= 1)
|
||||||
func AppendDuration(d time.Duration, buf []byte) []byte {
|
func AppendDuration(d time.Duration, buf []byte) []byte {
|
||||||
if d < 0 {
|
if d < 0 {
|
||||||
buf = append(buf, '-')
|
buf = append(buf, '-')
|
||||||
|
@ -39,23 +46,37 @@ func AppendDuration(d time.Duration, buf []byte) []byte {
|
||||||
minutes := (totalSeconds % 3600) / 60
|
minutes := (totalSeconds % 3600) / 60
|
||||||
seconds := totalSeconds % 60
|
seconds := totalSeconds % 60
|
||||||
|
|
||||||
|
idxPartBeg := 0
|
||||||
if days > 0 {
|
if days > 0 {
|
||||||
buf = strconv.AppendInt(buf, days, 10)
|
buf = strconv.AppendInt(buf, days, 10)
|
||||||
buf = fmt.Appendf(buf, "day%s, ", Pluralize(days))
|
buf = fmt.Appendf(buf, " day%s, ", Pluralize(days))
|
||||||
}
|
}
|
||||||
if hours > 0 {
|
if hours > 0 {
|
||||||
|
idxPartBeg = len(buf) - 2
|
||||||
buf = strconv.AppendInt(buf, hours, 10)
|
buf = strconv.AppendInt(buf, hours, 10)
|
||||||
buf = fmt.Appendf(buf, "hour%s, ", Pluralize(hours))
|
buf = fmt.Appendf(buf, " hour%s, ", Pluralize(hours))
|
||||||
}
|
}
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
|
idxPartBeg = len(buf) - 2
|
||||||
buf = strconv.AppendInt(buf, minutes, 10)
|
buf = strconv.AppendInt(buf, minutes, 10)
|
||||||
buf = fmt.Appendf(buf, "minute%s, ", Pluralize(minutes))
|
buf = fmt.Appendf(buf, " minute%s, ", Pluralize(minutes))
|
||||||
}
|
}
|
||||||
if seconds > 0 && totalSeconds < 3600 {
|
if seconds > 0 && totalSeconds < 3600 {
|
||||||
|
idxPartBeg = len(buf) - 2
|
||||||
buf = strconv.AppendInt(buf, seconds, 10)
|
buf = strconv.AppendInt(buf, seconds, 10)
|
||||||
buf = fmt.Appendf(buf, "second%s, ", Pluralize(seconds))
|
buf = fmt.Appendf(buf, " second%s, ", Pluralize(seconds))
|
||||||
}
|
}
|
||||||
return buf[:len(buf)-2]
|
// remove last comma and space
|
||||||
|
buf = buf[:len(buf)-2]
|
||||||
|
if idxPartBeg > 0 && idxPartBeg < len(buf) {
|
||||||
|
// replace last part ', ' with ' and ' in-place, alloc-free
|
||||||
|
// ', ' is 2 bytes, ' and ' is 5 bytes, so we need to make room for 3 more bytes
|
||||||
|
tailLen := len(buf) - (idxPartBeg + 2)
|
||||||
|
buf = append(buf, "000"...) // append 3 bytes for ' and '
|
||||||
|
copy(buf[idxPartBeg+5:], buf[idxPartBeg+2:idxPartBeg+2+tailLen]) // shift tail right by 3
|
||||||
|
copy(buf[idxPartBeg:], " and ") // overwrite ', ' with ' and '
|
||||||
|
}
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatDuration(d time.Duration) string {
|
func FormatDuration(d time.Duration) string {
|
||||||
|
|
|
@ -158,6 +158,16 @@ func TestFormatDuration(t *testing.T) {
|
||||||
duration: 1*24*time.Hour + 12*time.Hour,
|
duration: 1*24*time.Hour + 12*time.Hour,
|
||||||
expected: "1 day and 12 hours",
|
expected: "1 day and 12 hours",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "days and hours and minutes",
|
||||||
|
duration: 1*24*time.Hour + 12*time.Hour + 30*time.Minute,
|
||||||
|
expected: "1 day, 12 hours and 30 minutes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "days and hours and minutes and seconds (ignore seconds)",
|
||||||
|
duration: 1*24*time.Hour + 12*time.Hour + 30*time.Minute + 15*time.Second,
|
||||||
|
expected: "1 day, 12 hours and 30 minutes",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func ErrorT[T error](t *testing.T, err error, msgAndArgs ...any) {
|
||||||
|
|
||||||
func Equal[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
func Equal[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.Equal(t, want, got, msgAndArgs...)
|
require.EqualValues(t, want, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotEqual[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
func NotEqual[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
||||||
|
@ -53,11 +53,6 @@ func NotEqual[T any](t *testing.T, got T, want T, msgAndArgs ...any) {
|
||||||
require.NotEqual(t, want, got, msgAndArgs...)
|
require.NotEqual(t, want, got, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Values(t *testing.T, got any, want any, msgAndArgs ...any) {
|
|
||||||
t.Helper()
|
|
||||||
require.EqualValues(t, want, got, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Contains[T any](t *testing.T, got T, wants []T, msgAndArgs ...any) {
|
func Contains[T any](t *testing.T, got T, wants []T, msgAndArgs ...any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
require.Contains(t, wants, got, msgAndArgs...)
|
require.Contains(t, wants, got, msgAndArgs...)
|
||||||
|
|
Loading…
Add table
Reference in a new issue