mirror of
https://gitee.com/johng/gf
synced 2026-06-08 18:47:45 +08:00
Compare commits
31 Commits
v2.2.4
...
contrib/dr
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a8b33fa09 | |||
| 5884a0e05f | |||
| 31e44062a8 | |||
| 87cb1c9b8e | |||
| 0266d24d0a | |||
| 0876e00eb8 | |||
| 85c4794ceb | |||
| e007bf35b2 | |||
| 74e968e93b | |||
| 18507fb836 | |||
| 3b245837b9 | |||
| a853984f52 | |||
| 00c544ee99 | |||
| e7b9e41a5e | |||
| e254b4f3c0 | |||
| b0c9c68c9c | |||
| 1030434ce6 | |||
| 2f08c4b00f | |||
| 4553f90a83 | |||
| ef7fec7e24 | |||
| 0a76b9c61b | |||
| fbeb8f81ac | |||
| 62af4f1c2c | |||
| ed43efe4fb | |||
| 1cb42c32e3 | |||
| 628b454ebc | |||
| 38a858d7d3 | |||
| 83b92ddfa4 | |||
| 7cd415b1df | |||
| d2113b4d23 | |||
| d445987f95 |
3
.github/workflows/build_and_test.sh
vendored
3
.github/workflows/build_and_test.sh
vendored
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GOARCH=${{ matrix.goarch }}
|
||||
for file in `find . -name go.mod`; do
|
||||
dirpath=$(dirname $file)
|
||||
echo $dirpath
|
||||
@ -41,8 +40,10 @@ for file in `find . -name go.mod`; do
|
||||
go mod tidy
|
||||
go build ./...
|
||||
go test ./... -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1
|
||||
|
||||
if grep -q "/gogf/gf/.*/v2" go.mod; then
|
||||
sed -i "s/gogf\/gf\(\/.*\)\/v2/gogf\/gf\/v2\1/g" coverage.out
|
||||
fi
|
||||
|
||||
cd -
|
||||
done
|
||||
5
.github/workflows/gf.yml
vendored
5
.github/workflows/gf.yml
vendored
@ -127,6 +127,11 @@ jobs:
|
||||
ports:
|
||||
- 5236:5236
|
||||
|
||||
zookeeper:
|
||||
image: loads/zookeeper:3.8
|
||||
ports:
|
||||
- 2181:2181
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ "1.15", "1.16", "1.17", "1.18" ]
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,4 +17,5 @@ cbuild
|
||||
cmd/gf/main
|
||||
cmd/gf/gf
|
||||
go.work
|
||||
go.work.sum
|
||||
temp/
|
||||
|
||||
@ -5,6 +5,7 @@ go 1.18
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.1.0
|
||||
github.com/gogf/gf/v2 v2.2.2
|
||||
@ -35,6 +36,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.4.20 // indirect
|
||||
go.opentelemetry.io/otel v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.7.0 // indirect
|
||||
@ -53,6 +55,7 @@ require (
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 => ../../contrib/drivers/mssql/
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 => ../../contrib/drivers/mysql/
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 => ../../contrib/drivers/oracle/
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 => ../../contrib/drivers/pgsql/
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 => ../../contrib/drivers/sqlite/
|
||||
github.com/gogf/gf/v2 => ../../
|
||||
|
||||
@ -94,6 +94,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/sijms/go-ora/v2 v2.4.20 h1:9e3z7VLBQXRAHGiIda1GEFtRhfxata0LghyMZqvLKew=
|
||||
github.com/sijms/go-ora/v2 v2.4.20/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
|
||||
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -119,20 +119,13 @@ func (s serviceInstall) Run(ctx context.Context) (err error) {
|
||||
dstPath := paths[selectedID]
|
||||
|
||||
// Install the new binary.
|
||||
mlog.Debugf(`copy file from "%s" to "%s"`, gfile.SelfPath(), dstPath.filePath)
|
||||
err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
|
||||
if err != nil {
|
||||
mlog.Printf("install gf binary to '%s' failed: %v", dstPath.dirPath, err)
|
||||
mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.dirPath)
|
||||
} else {
|
||||
mlog.Printf("gf binary is successfully installed to: %s", dstPath.dirPath)
|
||||
}
|
||||
|
||||
// Uninstall the old binary.
|
||||
for _, path := range paths {
|
||||
// Do not delete myself.
|
||||
if path.filePath != "" && path.filePath != dstPath.filePath && gfile.SelfPath() != path.filePath {
|
||||
_ = gfile.Remove(path.filePath)
|
||||
}
|
||||
mlog.Printf("gf binary is successfully installed to: %s", dstPath.filePath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -59,10 +59,10 @@ func NewArrayRange(start, end, step int, safe ...bool) *Array {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]interface{}, (end-start+1)/step)
|
||||
slice := make([]interface{}, 0)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
slice = append(slice, i)
|
||||
index++
|
||||
}
|
||||
return NewArrayFrom(slice, safe...)
|
||||
|
||||
@ -51,10 +51,10 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]int, (end-start+1)/step)
|
||||
slice := make([]int, 0)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
slice = append(slice, i)
|
||||
index++
|
||||
}
|
||||
return NewIntArrayFrom(slice, safe...)
|
||||
|
||||
@ -61,10 +61,10 @@ func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{})
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]interface{}, (end-start+1)/step)
|
||||
slice := make([]interface{}, 0)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
slice = append(slice, i)
|
||||
index++
|
||||
}
|
||||
return NewSortedArrayFrom(slice, comparator, safe...)
|
||||
|
||||
@ -62,10 +62,10 @@ func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
}
|
||||
slice := make([]int, (end-start+1)/step)
|
||||
slice := make([]int, 0)
|
||||
index := 0
|
||||
for i := start; i <= end; i += step {
|
||||
slice[index] = i
|
||||
slice = append(slice, i)
|
||||
index++
|
||||
}
|
||||
return NewSortedIntArrayFrom(slice, safe...)
|
||||
|
||||
@ -59,7 +59,7 @@ func ExampleNewIntArrayRange() {
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5] 5 5
|
||||
// [1 2 3 4 5] 5 8
|
||||
}
|
||||
|
||||
func ExampleNewIntArrayFrom() {
|
||||
|
||||
@ -813,3 +813,14 @@ func TestIntArray_Walk(t *testing.T) {
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_NewIntArrayRange(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayRange(0, 128, 4)
|
||||
t.Assert(array.String(), `[0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124,128]`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayRange(1, 128, 4)
|
||||
t.Assert(array.String(), `[1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,93,97,101,105,109,113,117,121,125]`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ import (
|
||||
// Queue is a concurrent-safe queue built on doubly linked list and channel.
|
||||
type Queue struct {
|
||||
limit int // Limit for queue size.
|
||||
length *gtype.Int64 // Queue length.
|
||||
list *glist.List // Underlying list structure for data maintaining.
|
||||
closed *gtype.Bool // Whether queue is closed.
|
||||
events chan struct{} // Events for data writing.
|
||||
@ -44,6 +45,7 @@ const (
|
||||
func New(limit ...int) *Queue {
|
||||
q := &Queue{
|
||||
closed: gtype.NewBool(),
|
||||
length: gtype.NewInt64(),
|
||||
}
|
||||
if len(limit) > 0 && limit[0] > 0 {
|
||||
q.limit = limit[0]
|
||||
@ -57,6 +59,57 @@ func New(limit ...int) *Queue {
|
||||
return q
|
||||
}
|
||||
|
||||
// Push pushes the data `v` into the queue.
|
||||
// Note that it would panic if Push is called after the queue is closed.
|
||||
func (q *Queue) Push(v interface{}) {
|
||||
q.length.Add(1)
|
||||
if q.limit > 0 {
|
||||
q.C <- v
|
||||
} else {
|
||||
q.list.PushBack(v)
|
||||
if len(q.events) < defaultQueueSize {
|
||||
q.events <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop pops an item from the queue in FIFO way.
|
||||
// Note that it would return nil immediately if Pop is called after the queue is closed.
|
||||
func (q *Queue) Pop() interface{} {
|
||||
item := <-q.C
|
||||
q.length.Add(-1)
|
||||
return item
|
||||
}
|
||||
|
||||
// Close closes the queue.
|
||||
// Notice: It would notify all goroutines return immediately,
|
||||
// which are being blocked reading using Pop method.
|
||||
func (q *Queue) Close() {
|
||||
q.closed.Set(true)
|
||||
if q.events != nil {
|
||||
close(q.events)
|
||||
}
|
||||
if q.limit > 0 {
|
||||
close(q.C)
|
||||
} else {
|
||||
for i := 0; i < defaultBatchSize; i++ {
|
||||
q.Pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the length of the queue.
|
||||
// Note that the result might not be accurate as there's an
|
||||
// asynchronous channel reading the list constantly.
|
||||
func (q *Queue) Len() (length int64) {
|
||||
return q.length.Val()
|
||||
}
|
||||
|
||||
// Size is alias of Len.
|
||||
func (q *Queue) Size() int64 {
|
||||
return q.Len()
|
||||
}
|
||||
|
||||
// asyncLoopFromListToChannel starts an asynchronous goroutine,
|
||||
// which handles the data synchronization from list `q.list` to channel `q.C`.
|
||||
func (q *Queue) asyncLoopFromListToChannel() {
|
||||
@ -90,55 +143,3 @@ func (q *Queue) asyncLoopFromListToChannel() {
|
||||
// It's the sender's responsibility to close channel when it should be closed.
|
||||
close(q.C)
|
||||
}
|
||||
|
||||
// Push pushes the data `v` into the queue.
|
||||
// Note that it would panic if Push is called after the queue is closed.
|
||||
func (q *Queue) Push(v interface{}) {
|
||||
if q.limit > 0 {
|
||||
q.C <- v
|
||||
} else {
|
||||
q.list.PushBack(v)
|
||||
if len(q.events) < defaultQueueSize {
|
||||
q.events <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop pops an item from the queue in FIFO way.
|
||||
// Note that it would return nil immediately if Pop is called after the queue is closed.
|
||||
func (q *Queue) Pop() interface{} {
|
||||
return <-q.C
|
||||
}
|
||||
|
||||
// Close closes the queue.
|
||||
// Notice: It would notify all goroutines return immediately,
|
||||
// which are being blocked reading using Pop method.
|
||||
func (q *Queue) Close() {
|
||||
q.closed.Set(true)
|
||||
if q.events != nil {
|
||||
close(q.events)
|
||||
}
|
||||
if q.limit > 0 {
|
||||
close(q.C)
|
||||
} else {
|
||||
for i := 0; i < defaultBatchSize; i++ {
|
||||
q.Pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the length of the queue.
|
||||
// Note that the result might not be accurate as there's an
|
||||
// asynchronous channel reading the list constantly.
|
||||
func (q *Queue) Len() (length int) {
|
||||
if q.list != nil {
|
||||
length += q.list.Len()
|
||||
}
|
||||
length += len(q.C)
|
||||
return
|
||||
}
|
||||
|
||||
// Size is alias of Len.
|
||||
func (q *Queue) Size() int {
|
||||
return q.Len()
|
||||
}
|
||||
|
||||
@ -94,14 +94,14 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) {
|
||||
} else {
|
||||
if config.Pass != "" {
|
||||
source = fmt.Sprintf(
|
||||
"clickhouse://%s:%s@%s:%s/%s?charset=%s&debug=%t",
|
||||
"clickhouse://%s:%s@%s:%s/%s?debug=%t",
|
||||
config.User, url.PathEscape(config.Pass),
|
||||
config.Host, config.Port, config.Name, config.Charset, config.Debug,
|
||||
config.Host, config.Port, config.Name, config.Debug,
|
||||
)
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"clickhouse://%s@%s:%s/%s?charset=%s&debug=%t",
|
||||
config.User, config.Host, config.Port, config.Name, config.Charset, config.Debug,
|
||||
"clickhouse://%s@%s:%s/%s?debug=%t",
|
||||
config.User, config.Host, config.Port, config.Name, config.Debug,
|
||||
)
|
||||
}
|
||||
if config.Extra != "" {
|
||||
@ -173,8 +173,12 @@ func (d *Driver) TableFields(
|
||||
isNull = true
|
||||
fieldType = fieldsResult[1]
|
||||
}
|
||||
position := m["position"].Int()
|
||||
if result[0]["position"].Int() != 0 {
|
||||
position -= 1
|
||||
}
|
||||
fields[m["name"].String()] = &gdb.TableField{
|
||||
Index: m["position"].Int(),
|
||||
Index: position,
|
||||
Name: m["name"].String(),
|
||||
Default: m["default_expression"].Val(),
|
||||
Comment: m["comment"].String(),
|
||||
|
||||
@ -104,7 +104,7 @@ func Test_Model_Insert(t *testing.T) {
|
||||
}).Insert()
|
||||
t.AssertNil(err)
|
||||
|
||||
value, err := db.Model(table).Fields("passport").Where("id=3").Value() //model value
|
||||
value, err := db.Model(table).Fields("passport").Where("id=3").Value() // model value
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t3")
|
||||
|
||||
@ -122,7 +122,7 @@ func Test_Model_Insert(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t4")
|
||||
|
||||
_, err = db.Model(table).Where("id>?", 1).Delete() //model delete
|
||||
_, err = db.Model(table).Where("id>?", 1).Delete() // model delete
|
||||
t.AssertNil(err)
|
||||
|
||||
})
|
||||
@ -150,7 +150,7 @@ func Test_Model_One(t *testing.T) {
|
||||
_, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.Model(table).WherePri(1).One() //model one
|
||||
one, err := db.Model(table).WherePri(1).One() // model one
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data.Passport)
|
||||
t.Assert(one["create_time"], data.CreateTime)
|
||||
|
||||
@ -12,6 +12,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
@ -19,8 +22,6 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/google/uuid"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -532,18 +533,18 @@ func TestDriverClickhouse_TableFields(t *testing.T) {
|
||||
gtest.AssertNE(dataTypeTable, nil)
|
||||
|
||||
var result = map[string][]interface{}{
|
||||
"Col1": {1, "Col1", "UInt8", false, "", "", "", "列1"},
|
||||
"Col2": {2, "Col2", "String", true, "", "", "", "列2"},
|
||||
"Col3": {3, "Col3", "FixedString(3)", false, "", "", "", "列3"},
|
||||
"Col4": {4, "Col4", "String", false, "", "", "", "列4"},
|
||||
"Col5": {5, "Col5", "Map(String, UInt8)", false, "", "", "", "列5"},
|
||||
"Col6": {6, "Col6", "Array(String)", false, "", "", "", "列6"},
|
||||
"Col7": {7, "Col7", "Tuple(String, UInt8, Array(Map(String, String)))", false, "", "", "", "列7"},
|
||||
"Col8": {8, "Col8", "DateTime", false, "", "", "", "列8"},
|
||||
"Col9": {9, "Col9", "UUID", false, "", "", "", "列9"},
|
||||
"Col10": {10, "Col10", "DateTime", false, "", "", "", "列10"},
|
||||
"Col11": {11, "Col11", "Decimal(9, 2)", false, "", "", "", "列11"},
|
||||
"Col12": {12, "Col12", "Decimal(9, 2)", false, "", "", "", "列12"},
|
||||
"Col1": {0, "Col1", "UInt8", false, "", "", "", "列1"},
|
||||
"Col2": {1, "Col2", "String", true, "", "", "", "列2"},
|
||||
"Col3": {2, "Col3", "FixedString(3)", false, "", "", "", "列3"},
|
||||
"Col4": {3, "Col4", "String", false, "", "", "", "列4"},
|
||||
"Col5": {4, "Col5", "Map(String, UInt8)", false, "", "", "", "列5"},
|
||||
"Col6": {5, "Col6", "Array(String)", false, "", "", "", "列6"},
|
||||
"Col7": {6, "Col7", "Tuple(String, UInt8, Array(Map(String, String)))", false, "", "", "", "列7"},
|
||||
"Col8": {7, "Col8", "DateTime", false, "", "", "", "列8"},
|
||||
"Col9": {8, "Col9", "UUID", false, "", "", "", "列9"},
|
||||
"Col10": {9, "Col10", "DateTime", false, "", "", "", "列10"},
|
||||
"Col11": {10, "Col11", "Decimal(9, 2)", false, "", "", "", "列11"},
|
||||
"Col12": {11, "Col12", "Decimal(9, 2)", false, "", "", "", "列12"},
|
||||
}
|
||||
for k, v := range result {
|
||||
_, ok := dataTypeTable[k]
|
||||
@ -557,3 +558,13 @@ func TestDriverClickhouse_TableFields(t *testing.T) {
|
||||
gtest.AssertEQ(dataTypeTable[k].Comment, v[7])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_TableFields_HasField(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseExampleTable(connect))
|
||||
defer dropClickhouseExampleTable(connect)
|
||||
// 未修复前:panic: runtime error: index out of range [12] with length 12
|
||||
b, err := connect.GetCore().HasField(context.Background(), "data_type", "Col1")
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(b, true)
|
||||
}
|
||||
|
||||
@ -307,7 +307,7 @@ func Test_Model_Clone(t *testing.T) {
|
||||
result, err := md.Safe(true).Order("id ASC").All()
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
t.Assert(record["ID"].Int(), 3)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
@ -323,42 +323,42 @@ func Test_Model_Safe(t *testing.T) {
|
||||
md := db.Model(table).Safe(false).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe(true).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe().Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md1 := db.Model(table).Safe()
|
||||
md2 := md1.Where("id in (?)", g.Slice{1, 3})
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.All()
|
||||
t.AssertNil(err)
|
||||
@ -380,7 +380,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 1,3
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -395,7 +395,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 4,5,6
|
||||
count, err = md3.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
|
||||
all, err = md3.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -487,7 +487,7 @@ func Test_Model_Count(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Count with cache, check internal ctx data feature.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -498,24 +498,24 @@ func Test_Model_Count(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).FieldsEx("id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Fields("distinct id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
// COUNT...LIMIT...
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Page(1, 2).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
|
||||
}
|
||||
@ -1809,7 +1809,7 @@ func Test_Model_Distinct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id > 1").Distinct().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 9)
|
||||
t.Assert(count, int64(9))
|
||||
})
|
||||
}
|
||||
|
||||
@ -2310,7 +2310,7 @@ func Test_Model_Raw(t *testing.T) {
|
||||
OrderDesc("id").
|
||||
Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 6)
|
||||
t.Assert(count, int64(6))
|
||||
})
|
||||
}
|
||||
|
||||
@ -2398,38 +2398,38 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Where("nickname", "").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Slice where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{1, 2, 3}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Struct Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -2439,7 +2439,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Input struct {
|
||||
@ -2448,7 +2448,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Map Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -2457,7 +2457,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
"nickname": []string{},
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Input struct {
|
||||
@ -2467,7 +2467,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
"id": []int{},
|
||||
}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -581,7 +581,7 @@ func Test_DB_GetCount(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.GetCount(ctx, fmt.Sprintf("SELECT * FROM %s", table))
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -85,16 +85,16 @@ func Test_Master_Slave(t *testing.T) {
|
||||
// Auto slave.
|
||||
count, err = masterSlaveDB.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
|
||||
// slave.
|
||||
count, err = masterSlaveDB.Model(table).Slave().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
|
||||
// master.
|
||||
count, err = masterSlaveDB.Model(table).Master().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
@ -456,3 +457,22 @@ func Test_Issue2105(t *testing.T) {
|
||||
t.Assert(len(list[1].Json), 3)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/2231
|
||||
func Test_Issue2231(t *testing.T) {
|
||||
linkPattern := `(\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([^\?]*)\?{0,1}(.*)`
|
||||
link := `mysql:root:12345678@tcp(127.0.0.1:3306)/a正bc式?loc=Local&parseTime=true`
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
|
||||
match, err := gregex.MatchString(linkPattern, link)
|
||||
t.AssertNil(err)
|
||||
t.Assert(match[1], "mysql")
|
||||
t.Assert(match[2], "root")
|
||||
t.Assert(match[3], "12345678")
|
||||
t.Assert(match[4], "tcp")
|
||||
t.Assert(match[5], "127.0.0.1:3306")
|
||||
t.Assert(match[6], "a正bc式")
|
||||
t.Assert(match[7], "loc=Local&parseTime=true")
|
||||
})
|
||||
}
|
||||
|
||||
@ -462,7 +462,7 @@ func Test_Model_Clone(t *testing.T) {
|
||||
result, err := md.Safe(true).Order("id ASC").All()
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
t.Assert(record["id"].Int(), 3)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
@ -478,42 +478,42 @@ func Test_Model_Safe(t *testing.T) {
|
||||
md := db.Model(table).Safe(false).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe(true).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe().Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md1 := db.Model(table).Safe()
|
||||
md2 := md1.Where("id in (?)", g.Slice{1, 3})
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.All()
|
||||
t.AssertNil(err)
|
||||
@ -535,7 +535,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 1,3
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -550,7 +550,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 4,5,6
|
||||
count, err = md3.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
|
||||
all, err = md3.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -701,7 +701,7 @@ func Test_Model_Count(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Count with cache, check internal ctx data feature.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -712,24 +712,24 @@ func Test_Model_Count(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).FieldsEx("id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Fields("distinct id,nickname").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
// COUNT...LIMIT...
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Page(1, 2).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -776,7 +776,7 @@ func Test_Model_Count_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Data(g.MapStrAny{
|
||||
@ -795,7 +795,7 @@ func Test_Model_Count_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -809,7 +809,7 @@ func Test_Model_Count_All_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Data(g.MapStrAny{
|
||||
@ -828,7 +828,7 @@ func Test_Model_Count_All_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Data(g.MapStrAny{
|
||||
@ -847,7 +847,7 @@ func Test_Model_Count_All_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -861,7 +861,7 @@ func Test_Model_CountColumn_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).CountColumn("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Data(g.MapStrAny{
|
||||
@ -880,7 +880,7 @@ func Test_Model_CountColumn_WithCache(t *testing.T) {
|
||||
Force: false,
|
||||
}).CountColumn("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -1123,7 +1123,7 @@ func Test_Model_StructsWithOrmTag(t *testing.T) {
|
||||
dbInvalid.GetLogger().(*glog.Logger).SetWriter(buffer)
|
||||
defer dbInvalid.GetLogger().(*glog.Logger).SetWriter(os.Stdout)
|
||||
dbInvalid.Model(table).Order("id asc").Scan(&users)
|
||||
//fmt.Println(buffer.String())
|
||||
// fmt.Println(buffer.String())
|
||||
t.Assert(
|
||||
gstr.Contains(
|
||||
buffer.String(),
|
||||
@ -1986,7 +1986,7 @@ func Test_Model_Page(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 3)
|
||||
t.Assert(all[0]["id"], "7")
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -2890,7 +2890,7 @@ func Test_Model_Distinct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id > 1").Distinct().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 9)
|
||||
t.Assert(count, int64(9))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3636,7 +3636,7 @@ func Test_Model_Increment_Decrement(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 91).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3812,7 +3812,7 @@ func Test_Model_Raw(t *testing.T) {
|
||||
Limit(2).
|
||||
Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 6)
|
||||
t.Assert(count, int64(6))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3900,38 +3900,38 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Where("nickname", "").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Slice where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{1, 2, 3}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Struct Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -3941,7 +3941,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Input struct {
|
||||
@ -3950,7 +3950,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Map Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -3959,7 +3959,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
"nickname": []string{},
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Input struct {
|
||||
@ -3969,7 +3969,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
"id": []int{},
|
||||
}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -4649,12 +4649,12 @@ func Test_Builder_OmitEmptyWhere(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 1).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 0).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
builder := db.Model(table).OmitEmptyWhere().Builder()
|
||||
@ -4662,6 +4662,6 @@ func Test_Builder_OmitEmptyWhere(t *testing.T) {
|
||||
builder.Where("id", 0),
|
||||
).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ func Test_TX_Insert(t *testing.T) {
|
||||
if n, err := tx.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 2)
|
||||
t.Assert(n, int64(2))
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
@ -180,7 +180,7 @@ func Test_TX_BatchInsert(t *testing.T) {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 2)
|
||||
t.Assert(n, int64(2))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -218,7 +218,7 @@ func Test_TX_BatchReplace(t *testing.T) {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, TableSize)
|
||||
t.Assert(n, int64(TableSize))
|
||||
}
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -255,7 +255,7 @@ func Test_TX_BatchSave(t *testing.T) {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, TableSize)
|
||||
t.Assert(n, int64(TableSize))
|
||||
}
|
||||
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
@ -428,7 +428,7 @@ func Test_TX_GetCount(t *testing.T) {
|
||||
if count, err := tx.GetCount("SELECT * FROM " + table); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -674,7 +674,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
t.Assert(n, int64(0))
|
||||
}
|
||||
|
||||
t.Assert(tx.IsClosed(), true)
|
||||
@ -693,7 +693,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if n, err := tx.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
t.Assert(n, int64(0))
|
||||
}
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -701,8 +701,8 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, TableSize)
|
||||
t.AssertNE(n, 0)
|
||||
t.Assert(n, int64(TableSize))
|
||||
t.AssertNE(n, int64(0))
|
||||
}
|
||||
|
||||
t.Assert(tx.IsClosed(), true)
|
||||
@ -1136,6 +1136,6 @@ func Test_Transaction_Method(t *testing.T) {
|
||||
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,10 @@ package oracle_test
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -16,9 +20,6 @@ import (
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Model_InnerJoin(t *testing.T) {
|
||||
@ -162,7 +163,7 @@ func TestPage(t *testing.T) {
|
||||
func Test_Model_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
//db.SetDebug(true)
|
||||
// db.SetDebug(true)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Model(table)
|
||||
result, err := user.Data(g.Map{
|
||||
@ -406,7 +407,7 @@ func Test_Model_Count(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Count with cache, check internal ctx data feature.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -417,24 +418,24 @@ func Test_Model_Count(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).FieldsEx("ID").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Fields("distinct id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
// COUNT...LIMIT...
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Page(1, 2).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
|
||||
}
|
||||
@ -922,11 +923,11 @@ func Test_Model_Distinct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id > 1").Distinct().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 9)
|
||||
t.Assert(count, int64(9))
|
||||
})
|
||||
}
|
||||
|
||||
//not support
|
||||
// not support
|
||||
/*
|
||||
func Test_Model_Min_Max(t *testing.T) {
|
||||
table := createInitTable()
|
||||
@ -947,7 +948,7 @@ func Test_Model_Min_Max(t *testing.T) {
|
||||
func Test_Model_HasTable(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
//db.SetDebug(true)
|
||||
// db.SetDebug(true)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetCore().HasTable(strings.ToUpper(table))
|
||||
t.Assert(result, true)
|
||||
|
||||
@ -64,7 +64,7 @@ func Test_Model_Insert(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
value, err := db.Model(table).Fields("passport").Where("id=3").Value() //model value
|
||||
value, err := db.Model(table).Fields("passport").Where("id=3").Value() // model value
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t3")
|
||||
|
||||
@ -83,7 +83,7 @@ func Test_Model_Insert(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t4")
|
||||
|
||||
result, err = db.Model(table).Where("id>?", 1).Delete() //model delete
|
||||
result, err = db.Model(table).Where("id>?", 1).Delete() // model delete
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 3)
|
||||
@ -112,7 +112,7 @@ func Test_Model_One(t *testing.T) {
|
||||
_, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.Model(table).WherePri(1).One() //model one
|
||||
one, err := db.Model(table).WherePri(1).One() // model one
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data.Passport)
|
||||
t.Assert(one["create_time"], data.CreateTime)
|
||||
@ -214,12 +214,12 @@ func Test_Model_Count(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).FieldsEx("id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -94,6 +94,6 @@ func Test_Raw_Update(t *testing.T) {
|
||||
user := db.Model(table)
|
||||
n, err := user.Where("id", 101).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(n, 1)
|
||||
t.Assert(n, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -452,7 +452,7 @@ func Test_Model_Clone(t *testing.T) {
|
||||
result, err := md.Safe(true).Order("id ASC").All()
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
t.Assert(record["id"].Int(), 3)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
@ -468,42 +468,42 @@ func Test_Model_Safe(t *testing.T) {
|
||||
md := db.Model(table).Safe(false).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe(true).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md := db.Model(table).Safe().Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
md.Where("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
md1 := db.Model(table).Safe()
|
||||
md2 := md1.Where("id in (?)", g.Slice{1, 3})
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.All()
|
||||
t.AssertNil(err)
|
||||
@ -525,7 +525,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 1,3
|
||||
count, err := md2.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
|
||||
all, err := md2.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -540,7 +540,7 @@ func Test_Model_Safe(t *testing.T) {
|
||||
// 4,5,6
|
||||
count, err = md3.Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
|
||||
all, err = md3.Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
@ -692,7 +692,7 @@ func Test_Model_Count(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Count with cache, check internal ctx data feature.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -703,24 +703,24 @@ func Test_Model_Count(t *testing.T) {
|
||||
Force: false,
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).FieldsEx("id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Fields("distinct id").Where("id>8").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 2)
|
||||
t.Assert(count, int64(2))
|
||||
})
|
||||
// COUNT...LIMIT...
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Page(1, 2).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -1819,7 +1819,7 @@ func Test_Model_Page(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 3)
|
||||
t.Assert(all[0]["id"], "7")
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
@ -2369,7 +2369,7 @@ func Test_Model_Distinct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id > 1").Distinct().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 9)
|
||||
t.Assert(count, int64(9))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3171,7 +3171,7 @@ func Test_Model_Increment_Decrement(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 91).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
t.Assert(count, int64(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3202,7 +3202,7 @@ func Test_Model_Raw(t *testing.T) {
|
||||
Limit(2).
|
||||
Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 6)
|
||||
t.Assert(count, int64(6))
|
||||
})
|
||||
}
|
||||
|
||||
@ -3290,38 +3290,38 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", 0).Where("nickname", "").Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Slice where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{1, 2, 3}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 3)
|
||||
t.Assert(count, int64(3))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).OmitEmptyWhere().Where("id", g.Slice{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", g.Slice{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Struct Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -3331,7 +3331,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Input struct {
|
||||
@ -3340,7 +3340,7 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
}
|
||||
count, err := db.Model(table).Where(Input{}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
// Map Where.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -3349,14 +3349,14 @@ func Test_Model_OmitEmptyWhere(t *testing.T) {
|
||||
"nickname": []string{},
|
||||
}).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
t.Assert(count, int64(0))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where(g.Map{
|
||||
"id": []int{},
|
||||
}).OmitEmptyWhere().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
t.Assert(count, int64(TableSize))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
71
contrib/registry/zookeeper/README.MD
Normal file
71
contrib/registry/zookeeper/README.MD
Normal file
@ -0,0 +1,71 @@
|
||||
# GoFrame Etcd Registry
|
||||
|
||||
Use `zookeeper` as service registration and discovery management.
|
||||
|
||||
## Installation
|
||||
```
|
||||
go get -u -v github.com/gogf/gf/contrib/registry/zookeeper/v2
|
||||
```
|
||||
suggested using `go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf/contrib/registry/zookeeper/v2 latest
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/registry/zookeeper/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := zookeeper.New([]string{"127.0.0.1:2181"}, zookeeper.WithRootPath("/gogf"))
|
||||
gsvc.SetRegistry(r)
|
||||
|
||||
s := g.Server(`hello.svc`)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
g.Log().Info(r.Context(), `request received`)
|
||||
r.Response.Write(`Hello world`)
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/zookeeper/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(zookeeper.New([]string{"127.0.0.1:2181"},zookeeper.WithRootPath("/gogf")))
|
||||
gsel.SetBuilder(gsel.NewBuilderRoundRobin())
|
||||
|
||||
client := g.Client()
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Get(gctx.New(), `http://hello.svc/`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(res.ReadAllString())
|
||||
res.Close()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
`GoFrame zookeeper` is licensed under the [MIT License](../../../LICENSE), 100% free and open-source, forever.
|
||||
|
||||
11
contrib/registry/zookeeper/go.mod
Normal file
11
contrib/registry/zookeeper/go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module github.com/gogf/gf/contrib/registry/zookeeper/v2
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/go-zookeeper/zk v1.0.3
|
||||
github.com/gogf/gf/v2 v2.2.2
|
||||
golang.org/x/sync v0.1.0
|
||||
)
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
172
contrib/registry/zookeeper/go.sum
Normal file
172
contrib/registry/zookeeper/go.sum
Normal file
@ -0,0 +1,172 @@
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
|
||||
github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||
github.com/gogf/gf/v2 v2.2.2 h1:ew4k/VSr/gcPdMZcI8/HZYBYPjB0KOk6bQqA61M8bYE=
|
||||
github.com/gogf/gf/v2 v2.2.2/go.mod h1:thvkyb43RWUu/m05sRm4CbH9r7t7/FrW2M56L9Ystwk=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
|
||||
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
71
contrib/registry/zookeeper/zookeeper.go
Normal file
71
contrib/registry/zookeeper/zookeeper.go
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package zookeeper implements service Registry and Discovery using zookeeper.
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"github.com/go-zookeeper/zk"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ gsvc.Registry = &Registry{}
|
||||
|
||||
type Content struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Option is etcd registry option.
|
||||
type Option func(o *options)
|
||||
|
||||
type options struct {
|
||||
namespace string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
// WithRootPath with registry root path.
|
||||
func WithRootPath(path string) Option {
|
||||
return func(o *options) { o.namespace = path }
|
||||
}
|
||||
|
||||
// WithDigestACL with registry password.
|
||||
func WithDigestACL(user string, password string) Option {
|
||||
return func(o *options) {
|
||||
o.user = user
|
||||
o.password = password
|
||||
}
|
||||
}
|
||||
|
||||
// Registry is consul registry
|
||||
type Registry struct {
|
||||
opts *options
|
||||
conn *zk.Conn
|
||||
group singleflight.Group
|
||||
}
|
||||
|
||||
func New(address []string, opts ...Option) *Registry {
|
||||
conn, _, err := zk.Connect(address, time.Second*120)
|
||||
if err != nil {
|
||||
panic(gerror.Wrapf(err,
|
||||
"Error with connect to zookeeper"),
|
||||
)
|
||||
}
|
||||
options := &options{
|
||||
namespace: "/microservices",
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(options)
|
||||
}
|
||||
return &Registry{
|
||||
opts: options,
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
71
contrib/registry/zookeeper/zookeeper_discovery.go
Normal file
71
contrib/registry/zookeeper/zookeeper_discovery.go
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Search is the etcd discovery search function.
|
||||
func (r *Registry) Search(_ context.Context, in gsvc.SearchInput) ([]gsvc.Service, error) {
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(in.Prefix, "/", "-"), "-")
|
||||
instances, err, _ := r.group.Do(prefix, func() (interface{}, error) {
|
||||
serviceNamePath := path.Join(r.opts.namespace, prefix)
|
||||
servicesID, _, err := r.conn.Children(serviceNamePath)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with search the children node under %s",
|
||||
serviceNamePath,
|
||||
)
|
||||
}
|
||||
items := make([]gsvc.Service, 0, len(servicesID))
|
||||
for _, service := range servicesID {
|
||||
servicePath := path.Join(serviceNamePath, service)
|
||||
byteData, _, err := r.conn.Get(servicePath)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with node data which name is %s",
|
||||
servicePath,
|
||||
)
|
||||
}
|
||||
item, err := unmarshal(byteData)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with unmarshal node data to Content",
|
||||
)
|
||||
}
|
||||
svc, err := gsvc.NewServiceWithKV(item.Key, item.Value)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with new service with KV in Content",
|
||||
)
|
||||
}
|
||||
items = append(items, svc)
|
||||
}
|
||||
return items, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with group do",
|
||||
)
|
||||
}
|
||||
return instances.([]gsvc.Service), nil
|
||||
}
|
||||
|
||||
// Watch is the etcd discovery watch function.
|
||||
func (r *Registry) Watch(ctx context.Context, key string) (gsvc.Watcher, error) {
|
||||
return newWatcher(ctx, r.opts.namespace, key, r.conn)
|
||||
}
|
||||
20
contrib/registry/zookeeper/zookeeper_func.go
Normal file
20
contrib/registry/zookeeper/zookeeper_func.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func unmarshal(data []byte) (c *Content, err error) {
|
||||
err = json.Unmarshal(data, &c)
|
||||
return
|
||||
}
|
||||
|
||||
func marshal(c *Content) ([]byte, error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
139
contrib/registry/zookeeper/zookeeper_registrar.go
Normal file
139
contrib/registry/zookeeper/zookeeper_registrar.go
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-zookeeper/zk"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Register registers `service` to Registry.
|
||||
// Note that it returns a new Service if it changes the input Service with custom one.
|
||||
func (r *Registry) Register(_ context.Context, service gsvc.Service) (gsvc.Service, error) {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
if err = r.ensureName(r.opts.namespace, []byte(""), 0); err != nil {
|
||||
return service, gerror.Wrapf(
|
||||
err,
|
||||
"Error Creat node which name is %s",
|
||||
r.opts.namespace,
|
||||
)
|
||||
}
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(service.GetPrefix(), "/", "-"), "-")
|
||||
servicePrefixPath := path.Join(r.opts.namespace, prefix)
|
||||
if err = r.ensureName(servicePrefixPath, []byte(""), 0); err != nil {
|
||||
return service, gerror.Wrapf(
|
||||
err,
|
||||
"Error Creat node which name is %s",
|
||||
servicePrefixPath,
|
||||
)
|
||||
}
|
||||
|
||||
if data, err = marshal(&Content{
|
||||
Key: service.GetKey(),
|
||||
Value: service.GetValue(),
|
||||
}); err != nil {
|
||||
return service, gerror.Wrapf(
|
||||
err,
|
||||
"Error with marshal Content to Json string",
|
||||
)
|
||||
}
|
||||
servicePath := path.Join(servicePrefixPath, service.GetName())
|
||||
if err = r.ensureName(servicePath, data, zk.FlagEphemeral); err != nil {
|
||||
return service, gerror.Wrapf(
|
||||
err,
|
||||
"Error Creat node which name is %s",
|
||||
servicePath,
|
||||
)
|
||||
}
|
||||
go r.reRegister(servicePath, data)
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// Deregister off-lines and removes `service` from the Registry.
|
||||
func (r *Registry) Deregister(ctx context.Context, service gsvc.Service) error {
|
||||
ch := make(chan error, 1)
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(service.GetPrefix(), "/", "-"), "-")
|
||||
servicePath := path.Join(r.opts.namespace, prefix, service.GetName())
|
||||
go func() {
|
||||
err := r.conn.Delete(servicePath, -1)
|
||||
ch <- err
|
||||
}()
|
||||
var err error
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case err = <-ch:
|
||||
}
|
||||
return gerror.Wrapf(err,
|
||||
"Error with deregister service:%s",
|
||||
service.GetName(),
|
||||
)
|
||||
}
|
||||
|
||||
// ensureName ensure node exists, if not exist, create and set data
|
||||
func (r *Registry) ensureName(path string, data []byte, flags int32) error {
|
||||
exists, stat, err := r.conn.Exists(path)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err,
|
||||
"Error with check node exist which name is %s",
|
||||
path,
|
||||
)
|
||||
}
|
||||
// ephemeral nodes handling after restart
|
||||
// fixes a race condition if the server crashes without using CreateProtectedEphemeralSequential()
|
||||
if flags&zk.FlagEphemeral == zk.FlagEphemeral {
|
||||
err = r.conn.Delete(path, stat.Version)
|
||||
if err != nil && err != zk.ErrNoNode {
|
||||
return gerror.Wrapf(err,
|
||||
"Error with delete node which name is %s",
|
||||
path,
|
||||
)
|
||||
}
|
||||
exists = false
|
||||
}
|
||||
if !exists {
|
||||
if len(r.opts.user) > 0 && len(r.opts.password) > 0 {
|
||||
_, err = r.conn.Create(path, data, flags, zk.DigestACL(zk.PermAll, r.opts.user, r.opts.password))
|
||||
} else {
|
||||
_, err = r.conn.Create(path, data, flags, zk.WorldACL(zk.PermAll))
|
||||
}
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err,
|
||||
"Error with create node which name is %s",
|
||||
path,
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reRegister re-register data node info when bad connection recovered
|
||||
func (r *Registry) reRegister(path string, data []byte) {
|
||||
sessionID := r.conn.SessionID()
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
cur := r.conn.SessionID()
|
||||
// sessionID changed
|
||||
if cur > 0 && sessionID != cur {
|
||||
// re-ensureName
|
||||
if err := r.ensureName(path, data, zk.FlagEphemeral); err != nil {
|
||||
return
|
||||
}
|
||||
sessionID = cur
|
||||
}
|
||||
}
|
||||
}
|
||||
138
contrib/registry/zookeeper/zookeeper_watcher.go
Normal file
138
contrib/registry/zookeeper/zookeeper_watcher.go
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-zookeeper/zk"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ gsvc.Watcher = (*watcher)(nil)
|
||||
|
||||
var ErrWatcherStopped = errors.New("watcher stopped")
|
||||
|
||||
type watcher struct {
|
||||
ctx context.Context
|
||||
event chan zk.Event
|
||||
conn *zk.Conn
|
||||
cancel context.CancelFunc
|
||||
prefix string
|
||||
nameSpace string
|
||||
group singleflight.Group
|
||||
}
|
||||
|
||||
func newWatcher(ctx context.Context, nameSpace, prefix string, conn *zk.Conn) (*watcher, error) {
|
||||
w := &watcher{
|
||||
conn: conn,
|
||||
event: make(chan zk.Event, 1),
|
||||
nameSpace: nameSpace,
|
||||
prefix: prefix,
|
||||
}
|
||||
w.ctx, w.cancel = context.WithCancel(ctx)
|
||||
go w.watch(w.ctx)
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *watcher) Proceed() ([]gsvc.Service, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
return nil, w.ctx.Err()
|
||||
case e := <-w.event:
|
||||
if e.State == zk.StateDisconnected {
|
||||
return nil, gerror.Wrapf(
|
||||
ErrWatcherStopped,
|
||||
"watcher stopped",
|
||||
)
|
||||
}
|
||||
if e.Err != nil {
|
||||
return nil, e.Err
|
||||
}
|
||||
return w.getServicesByPrefix()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) getServicesByPrefix() ([]gsvc.Service, error) {
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(w.prefix, "/", "-"), "-")
|
||||
serviceNamePath := path.Join(w.nameSpace, prefix)
|
||||
instances, err, _ := w.group.Do(serviceNamePath, func() (interface{}, error) {
|
||||
servicesID, _, err := w.conn.Children(serviceNamePath)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with search the children node under %s",
|
||||
serviceNamePath,
|
||||
)
|
||||
}
|
||||
items := make([]gsvc.Service, 0, len(servicesID))
|
||||
for _, service := range servicesID {
|
||||
servicePath := path.Join(serviceNamePath, service)
|
||||
byteData, _, err := w.conn.Get(servicePath)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with node data which name is %s",
|
||||
servicePath,
|
||||
)
|
||||
}
|
||||
item, err := unmarshal(byteData)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with unmarshal node data to Content",
|
||||
)
|
||||
}
|
||||
svc, err := gsvc.NewServiceWithKV(item.Key, item.Value)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with new service with KV in Content",
|
||||
)
|
||||
}
|
||||
items = append(items, svc)
|
||||
}
|
||||
return items, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
"Error with group do",
|
||||
)
|
||||
}
|
||||
return instances.([]gsvc.Service), nil
|
||||
}
|
||||
|
||||
func (w *watcher) Close() error {
|
||||
w.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) watch(ctx context.Context) {
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(w.prefix, "/", "-"), "-")
|
||||
serviceNamePath := path.Join(w.nameSpace, prefix)
|
||||
for {
|
||||
|
||||
if w.conn.State() == zk.StateConnected || w.conn.State() == zk.StateHasSession {
|
||||
// each watch action is only valid once
|
||||
_, _, ch, err := w.conn.ChildrenW(serviceNamePath)
|
||||
if err != nil {
|
||||
w.event <- zk.Event{Err: err}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
w.event <- <-ch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
189
contrib/registry/zookeeper/zookeeper_z_test.go
Normal file
189
contrib/registry/zookeeper/zookeeper_z_test.go
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package zookeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
// TestRegistry TestRegistryManyService
|
||||
func TestRegistry(t *testing.T) {
|
||||
r := New([]string{"127.0.0.1:2181"}, WithRootPath("/gogf"))
|
||||
ctx := context.Background()
|
||||
|
||||
svc := &gsvc.LocalService{
|
||||
Name: "goframe-provider-0-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9000"),
|
||||
}
|
||||
|
||||
s, err := r.Register(ctx, svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = r.Deregister(ctx, s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRegistryMany TestRegistryManyService
|
||||
func TestRegistryMany(t *testing.T) {
|
||||
r := New([]string{"127.0.0.1:2181"}, WithRootPath("/gogf"))
|
||||
|
||||
svc := &gsvc.LocalService{
|
||||
Name: "goframe-provider-1-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9000"),
|
||||
}
|
||||
svc1 := &gsvc.LocalService{
|
||||
Name: "goframe-provider-2-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9001"),
|
||||
}
|
||||
svc2 := &gsvc.LocalService{
|
||||
Name: "goframe-provider-3-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9002"),
|
||||
}
|
||||
|
||||
s0, err := r.Register(context.Background(), svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s1, err := r.Register(context.Background(), svc1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s2, err := r.Register(context.Background(), svc2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = r.Deregister(context.Background(), s0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = r.Deregister(context.Background(), s1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = r.Deregister(context.Background(), s2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetService Test GetService
|
||||
func TestGetService(t *testing.T) {
|
||||
r := New([]string{"127.0.0.1:2181"}, WithRootPath("/gogf"))
|
||||
ctx := context.Background()
|
||||
|
||||
svc := &gsvc.LocalService{
|
||||
Name: "goframe-provider-4-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9000"),
|
||||
}
|
||||
|
||||
s, err := r.Register(ctx, svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
time.Sleep(time.Second * 1)
|
||||
serviceInstances, err := r.Search(ctx, gsvc.SearchInput{
|
||||
Prefix: s.GetPrefix(),
|
||||
Name: svc.Name,
|
||||
Version: svc.Version,
|
||||
Metadata: svc.Metadata,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, instance := range serviceInstances {
|
||||
g.Log().Info(ctx, instance)
|
||||
}
|
||||
|
||||
err = r.Deregister(ctx, s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWatch Test Watch
|
||||
func TestWatch(t *testing.T) {
|
||||
r := New([]string{"127.0.0.1:2181"}, WithRootPath("/gogf"))
|
||||
|
||||
ctx := gctx.New()
|
||||
|
||||
svc := &gsvc.LocalService{
|
||||
Name: "goframe-provider-4-tcp",
|
||||
Version: "test",
|
||||
Metadata: map[string]interface{}{"app": "goframe", gsvc.MDProtocol: "tcp"},
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9000"),
|
||||
}
|
||||
t.Log("watch")
|
||||
watch, err := r.Watch(context.Background(), svc.GetPrefix())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s1, err := r.Register(context.Background(), svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// watch svc
|
||||
// svc register, AddEvent
|
||||
next, err := watch.Proceed()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, instance := range next {
|
||||
// it will output one instance
|
||||
g.Log().Info(ctx, "Register Proceed service: ", instance)
|
||||
}
|
||||
|
||||
err = r.Deregister(context.Background(), s1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// svc deregister, DeleteEvent
|
||||
next, err = watch.Proceed()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, instance := range next {
|
||||
// it will output nothing
|
||||
g.Log().Info(ctx, "Deregister Proceed service: ", instance)
|
||||
}
|
||||
|
||||
err = watch.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = watch.Proceed()
|
||||
if err == nil {
|
||||
// if nil, stop failed
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
@ -118,7 +118,7 @@ type DB interface {
|
||||
GetOne(ctx context.Context, sql string, args ...interface{}) (Record, error) // See Core.GetOne.
|
||||
GetValue(ctx context.Context, sql string, args ...interface{}) (Value, error) // See Core.GetValue.
|
||||
GetArray(ctx context.Context, sql string, args ...interface{}) ([]Value, error) // See Core.GetArray.
|
||||
GetCount(ctx context.Context, sql string, args ...interface{}) (int64, error) // See Core.GetCount.
|
||||
GetCount(ctx context.Context, sql string, args ...interface{}) (int, error) // See Core.GetCount.
|
||||
GetScan(ctx context.Context, objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
|
||||
Union(unions ...*Model) *Model // See Core.Union.
|
||||
UnionAll(unions ...*Model) *Model // See Core.UnionAll.
|
||||
@ -316,7 +316,7 @@ const (
|
||||
ctxKeyInternalProducedSQL gctx.StrKey = `CtxKeyInternalProducedSQL`
|
||||
|
||||
// type:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
|
||||
linkPattern = `(\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([\w\-]*)\?{0,1}(.*)`
|
||||
linkPattern = `(\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([^\?]*)\?{0,1}(.*)`
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -230,7 +230,7 @@ func (c *Core) GetValue(ctx context.Context, sql string, args ...interface{}) (V
|
||||
}
|
||||
|
||||
// GetCount queries and returns the count from database.
|
||||
func (c *Core) GetCount(ctx context.Context, sql string, args ...interface{}) (int64, error) {
|
||||
func (c *Core) GetCount(ctx context.Context, sql string, args ...interface{}) (int, error) {
|
||||
// If the query fields do not contain function "COUNT",
|
||||
// it replaces the sql string and adds the "COUNT" function to the fields.
|
||||
if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) {
|
||||
@ -240,7 +240,7 @@ func (c *Core) GetCount(ctx context.Context, sql string, args ...interface{}) (i
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return value.Int64(), nil
|
||||
return value.Int(), nil
|
||||
}
|
||||
|
||||
// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement.
|
||||
|
||||
@ -86,7 +86,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
if config.DeletedAt != "" {
|
||||
return m.getSoftFieldName(tableName, []string{config.DeletedAt})
|
||||
}
|
||||
return m.getSoftFieldName(tableName, deletedFiledNames)
|
||||
|
||||
92
debug/gdebug/gdebug_z_unit_test.go
Normal file
92
debug/gdebug/gdebug_z_unit_test.go
Normal file
@ -0,0 +1,92 @@
|
||||
package gdebug_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_CallerPackage(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gdebug.CallerPackage(), "github.com/gogf/gf/v2/test/gtest")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CallerFunction(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gdebug.CallerFunction(), "C")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CallerFilePath(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.CallerFilePath(), "gtest_util.go"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CallerDirectory(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.CallerDirectory(), "gtest"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CallerFileLine(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.CallerFileLine(), "gtest_util.go:36"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CallerFileLineShort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.CallerFileLineShort(), "gtest_util.go:36"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_FuncPath(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gdebug.FuncPath(Test_FuncPath), "github.com/gogf/gf/v2/debug/gdebug_test.Test_FuncPath")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_FuncName(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gdebug.FuncName(Test_FuncName), "gdebug_test.Test_FuncName")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PrintStack(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gdebug.PrintStack()
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GoroutineId(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertGT(gdebug.GoroutineId(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Stack(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.Stack(), "gtest_util.go:36"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StackWithFilter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.Contains(gdebug.StackWithFilter([]string{"github.com"}), "gtest_util.go:36"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BinVersion(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertGT(len(gdebug.BinVersion()), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BinVersionMd5(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertGT(len(gdebug.BinVersionMd5()), 0)
|
||||
})
|
||||
}
|
||||
@ -208,5 +208,8 @@ func (j *Json) Dump() {
|
||||
}
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
if j.p == nil {
|
||||
return
|
||||
}
|
||||
gutil.Dump(*j.p)
|
||||
}
|
||||
|
||||
@ -50,3 +50,11 @@ func (j *Json) Interfaces() []interface{} {
|
||||
}
|
||||
return j.Array()
|
||||
}
|
||||
|
||||
// String returns current Json object as string.
|
||||
func (j *Json) String() string {
|
||||
if j.IsNil() {
|
||||
return ""
|
||||
}
|
||||
return j.MustToJsonString()
|
||||
}
|
||||
|
||||
@ -376,7 +376,7 @@ func Test_Convert2(t *testing.T) {
|
||||
j := gjson.New(`{"name":"gf","time":"2019-06-12"}`)
|
||||
t.Assert(j.Interface().(g.Map)["name"], "gf")
|
||||
t.Assert(j.Get("name1").Map(), nil)
|
||||
t.AssertNE(j.GetJson("name1"), nil)
|
||||
t.Assert(j.GetJson("name1"), nil)
|
||||
t.Assert(j.GetJsons("name1"), nil)
|
||||
t.Assert(j.GetJsonMap("name1"), nil)
|
||||
t.Assert(j.Contains("name1"), false)
|
||||
|
||||
@ -13,6 +13,13 @@ import (
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Case(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gcode.CodeNil.String(), "-1")
|
||||
t.Assert(gcode.CodeInternalError.String(), "50:Internal Error")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := gcode.New(1, "custom error", "detailed description")
|
||||
@ -21,3 +28,11 @@ func Test_Nil(t *testing.T) {
|
||||
t.Assert(c.Detail(), "detailed description")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WithCode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := gcode.WithCode(gcode.CodeInternalError, "CodeInternalError")
|
||||
t.Assert(c.Code(), gcode.CodeInternalError.Code())
|
||||
t.Assert(c.Detail(), "CodeInternalError")
|
||||
})
|
||||
}
|
||||
|
||||
@ -95,10 +95,14 @@ func Test_Wrapf(t *testing.T) {
|
||||
t.AssertNE(err, nil)
|
||||
t.Assert(err.Error(), "1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.Wrapf(nil, ""), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WrapSkip(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapSkip(1, nil, "2"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.WrapSkip(1, err, "2")
|
||||
err = gerror.WrapSkip(1, err, "3")
|
||||
@ -122,6 +126,7 @@ func Test_WrapSkip(t *testing.T) {
|
||||
|
||||
func Test_WrapSkipf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapSkipf(1, nil, "2"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.WrapSkipf(1, err, "2")
|
||||
err = gerror.WrapSkipf(1, err, "3")
|
||||
@ -145,6 +150,7 @@ func Test_WrapSkipf(t *testing.T) {
|
||||
|
||||
func Test_Cause(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.Cause(nil), nil)
|
||||
err := errors.New("1")
|
||||
t.Assert(gerror.Cause(err), err)
|
||||
})
|
||||
@ -167,6 +173,17 @@ func Test_Cause(t *testing.T) {
|
||||
err = gerror.Wrap(err, "3")
|
||||
t.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.Stack(nil), "")
|
||||
err := errors.New("1")
|
||||
t.Assert(gerror.Stack(err), err)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e *gerror.Error = nil
|
||||
t.Assert(e.Cause(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Format(t *testing.T) {
|
||||
@ -229,16 +246,22 @@ func Test_Stack(t *testing.T) {
|
||||
|
||||
func Test_Current(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.Current(nil), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
t.Assert(err.Error(), "3: 2: 1")
|
||||
t.Assert(gerror.Current(err).Error(), "3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e *gerror.Error = nil
|
||||
t.Assert(e.Current(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Unwrap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.Unwrap(nil), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
@ -253,6 +276,10 @@ func Test_Unwrap(t *testing.T) {
|
||||
err = gerror.Unwrap(err)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e *gerror.Error = nil
|
||||
t.Assert(e.Unwrap(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Code(t *testing.T) {
|
||||
@ -282,6 +309,7 @@ func Test_Code(t *testing.T) {
|
||||
t.Assert(err.Error(), "123")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapCode(gcode.New(1, "", nil), nil, "3"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.WrapCode(gcode.New(1, "", nil), err, "3")
|
||||
@ -289,6 +317,7 @@ func Test_Code(t *testing.T) {
|
||||
t.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapCodef(gcode.New(1, "", nil), nil, "%s", "3"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.WrapCodef(gcode.New(1, "", nil), err, "%s", "3")
|
||||
@ -296,6 +325,7 @@ func Test_Code(t *testing.T) {
|
||||
t.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapCodeSkip(gcode.New(1, "", nil), 100, nil, "3"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.WrapCodeSkip(gcode.New(1, "", nil), 100, err, "3")
|
||||
@ -303,6 +333,7 @@ func Test_Code(t *testing.T) {
|
||||
t.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.WrapCodeSkipf(gcode.New(1, "", nil), 100, nil, "%s", "3"), nil)
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.WrapCodeSkipf(gcode.New(1, "", nil), 100, err, "%s", "3")
|
||||
@ -311,6 +342,20 @@ func Test_Code(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestError_Error(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e *gerror.Error = nil
|
||||
t.Assert(e.Error(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestError_Code(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e *gerror.Error = nil
|
||||
t.Assert(e.Code(), gcode.CodeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetCode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := gerror.New("123")
|
||||
@ -321,6 +366,10 @@ func Test_SetCode(t *testing.T) {
|
||||
t.Assert(gerror.Code(err), gcode.CodeValidationFailed)
|
||||
t.Assert(err.Error(), "123")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err *gerror.Error = nil
|
||||
err.SetCode(gcode.CodeValidationFailed)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Json(t *testing.T) {
|
||||
@ -353,6 +402,10 @@ func Test_Equal(t *testing.T) {
|
||||
t.Assert(gerror.Equal(err3, err4), false)
|
||||
t.Assert(gerror.Equal(err1, err4), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var e = new(gerror.Error)
|
||||
t.Assert(e.Equal(e), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Is(t *testing.T) {
|
||||
@ -375,6 +428,7 @@ func Test_HashError(t *testing.T) {
|
||||
|
||||
func Test_HashCode(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gerror.HasCode(nil, gcode.CodeNotAuthorized), false)
|
||||
err1 := errors.New("1")
|
||||
err2 := gerror.WrapCode(gcode.CodeNotAuthorized, err1, "2")
|
||||
err3 := gerror.Wrap(err2, "3")
|
||||
@ -385,3 +439,14 @@ func Test_HashCode(t *testing.T) {
|
||||
t.Assert(gerror.HasCode(err4, gcode.CodeNotAuthorized), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NewOption(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(gerror.NewOption(gerror.Option{
|
||||
Error: errors.New("NewOptionError"),
|
||||
Stack: true,
|
||||
Text: "Text",
|
||||
Code: gcode.CodeNotAuthorized,
|
||||
}), gerror.New("NewOptionError"))
|
||||
})
|
||||
}
|
||||
|
||||
102
frame/g/g_z_unit_test.go
Normal file
102
frame/g/g_z_unit_test.go
Normal file
@ -0,0 +1,102 @@
|
||||
package g_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.TODO()
|
||||
)
|
||||
|
||||
func Test_NewVar(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).Int(), 1)
|
||||
t.Assert(g.NewVar(1, true).Int(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Dump(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.Dump("GoFrame")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DumpTo(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.DumpTo(os.Stdout, "GoFrame", gutil.DumpOption{})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DumpWithType(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.DumpWithType("GoFrame", 123)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DumpWithOption(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.DumpWithOption("GoFrame", gutil.DumpOption{})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Try(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.Try(ctx, func(ctx context.Context) {
|
||||
g.Dump("GoFrame")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TryCatch(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.TryCatch(ctx, func(ctx context.Context) {
|
||||
g.Dump("GoFrame")
|
||||
}, func(ctx context.Context, exception error) {
|
||||
g.Dump(exception)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.IsNil(nil), true)
|
||||
t.Assert(g.IsNil(0), false)
|
||||
t.Assert(g.IsNil("GoFrame"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.IsEmpty(nil), true)
|
||||
t.Assert(g.IsEmpty(0), true)
|
||||
t.Assert(g.IsEmpty("GoFrame"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetDebug(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
g.SetDebug(true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Object(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(g.Client(), nil)
|
||||
t.AssertNE(g.Server(), nil)
|
||||
t.AssertNE(g.TCPServer(), nil)
|
||||
t.AssertNE(g.UDPServer(), nil)
|
||||
t.AssertNE(g.View(), nil)
|
||||
t.AssertNE(g.Config(), nil)
|
||||
t.AssertNE(g.Cfg(), nil)
|
||||
t.AssertNE(g.Resource(), nil)
|
||||
t.AssertNE(g.I18n(), nil)
|
||||
t.AssertNE(g.Res(), nil)
|
||||
t.AssertNE(g.Log(), nil)
|
||||
t.AssertNE(g.Validator(), nil)
|
||||
})
|
||||
}
|
||||
@ -59,8 +59,12 @@ func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr strin
|
||||
encodedParamStr += "&"
|
||||
}
|
||||
s = gconv.String(v)
|
||||
if urlEncode && len(s) > 6 && strings.Compare(s[0:6], fileUploadingKey) != 0 {
|
||||
s = gurl.Encode(s)
|
||||
if urlEncode {
|
||||
if strings.HasPrefix(s, fileUploadingKey) && len(s) > len(fileUploadingKey) {
|
||||
// No url encoding if uploading file.
|
||||
} else {
|
||||
s = gurl.Encode(s)
|
||||
}
|
||||
}
|
||||
encodedParamStr += k + "=" + s
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func TestRwmutexIsSafe(t *testing.T) {
|
||||
func TestRWMutexIsSafe(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
lock := rwmutex.New()
|
||||
t.Assert(lock.IsSafe(), false)
|
||||
@ -37,103 +37,107 @@ func TestRwmutexIsSafe(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSafeRwmutex(t *testing.T) {
|
||||
func TestSafeRWMutex(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
safeLock := rwmutex.New(true)
|
||||
array := garray.New(true)
|
||||
var (
|
||||
localSafeLock = rwmutex.New(true)
|
||||
array = garray.New(true)
|
||||
)
|
||||
|
||||
go func() {
|
||||
safeLock.Lock()
|
||||
localSafeLock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
safeLock.Unlock()
|
||||
localSafeLock.Unlock()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
safeLock.Lock()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
localSafeLock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
safeLock.Unlock()
|
||||
localSafeLock.Unlock()
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
t.Assert(array.Len(), 1)
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
t.Assert(array.Len(), 3)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
t.Assert(array.Len(), 3)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
t.Assert(array.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSafeReaderRwmutex(t *testing.T) {
|
||||
func TestSafeReaderRWMutex(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
safeLock := rwmutex.New(true)
|
||||
array := garray.New(true)
|
||||
|
||||
var (
|
||||
localSafeLock = rwmutex.New(true)
|
||||
array = garray.New(true)
|
||||
)
|
||||
go func() {
|
||||
safeLock.RLock()
|
||||
localSafeLock.RLock()
|
||||
array.Append(1)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
localSafeLock.RUnlock()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
localSafeLock.RLock()
|
||||
array.Append(1)
|
||||
safeLock.RUnlock()
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
localSafeLock.RUnlock()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
safeLock.RLock()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
localSafeLock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
array.Append(1)
|
||||
safeLock.RUnlock()
|
||||
localSafeLock.Unlock()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
safeLock.Lock()
|
||||
array.Append(1)
|
||||
safeLock.Unlock()
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
t.Assert(array.Len(), 2)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
t.Assert(array.Len(), 3)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
t.Assert(array.Len(), 4)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
t.Assert(array.Len(), 6)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnsafeRwmutex(t *testing.T) {
|
||||
func TestUnsafeRWMutex(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
unsafeLock := rwmutex.New()
|
||||
array := garray.New(true)
|
||||
|
||||
var (
|
||||
localUnsafeLock = rwmutex.New()
|
||||
array = garray.New(true)
|
||||
)
|
||||
go func() {
|
||||
unsafeLock.Lock()
|
||||
localUnsafeLock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(2000 * time.Millisecond)
|
||||
array.Append(1)
|
||||
unsafeLock.Unlock()
|
||||
localUnsafeLock.Unlock()
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
unsafeLock.Lock()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
localUnsafeLock.Lock()
|
||||
array.Append(1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
array.Append(1)
|
||||
unsafeLock.Unlock()
|
||||
localUnsafeLock.Unlock()
|
||||
}()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
t.Assert(array.Len(), 2)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
t.Assert(array.Len(), 3)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
t.Assert(array.Len(), 3)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
t.Assert(array.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,8 +10,10 @@ package ghttp
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gtrace"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@ -142,6 +144,18 @@ func (r *Response) ClearBuffer() {
|
||||
r.buffer.Reset()
|
||||
}
|
||||
|
||||
// ServeContent replies to the request using the content in the
|
||||
// provided ReadSeeker. The main benefit of ServeContent over io.Copy
|
||||
// is that it handles Range requests properly, sets the MIME type, and
|
||||
// handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,
|
||||
// and If-Range requests.
|
||||
//
|
||||
// See http.ServeContent
|
||||
func (r *Response) ServeContent(name string, modTime time.Time, content io.ReadSeeker) {
|
||||
r.wroteHeader = true
|
||||
http.ServeContent(r.Writer.RawWriter(), r.Request.Request, name, modTime, content)
|
||||
}
|
||||
|
||||
// Flush outputs the buffer content to the client and clears the buffer.
|
||||
func (r *Response) Flush() {
|
||||
r.Header().Set(responseHeaderTraceID, gtrace.GetTraceID(r.Request.Context()))
|
||||
|
||||
@ -16,10 +16,11 @@ import (
|
||||
|
||||
// ResponseWriter is the custom writer for http response.
|
||||
type ResponseWriter struct {
|
||||
Status int // HTTP status.
|
||||
writer http.ResponseWriter // The underlying ResponseWriter.
|
||||
buffer *bytes.Buffer // The output buffer.
|
||||
hijacked bool // Mark this request is hijacked or not.
|
||||
Status int // HTTP status.
|
||||
writer http.ResponseWriter // The underlying ResponseWriter.
|
||||
buffer *bytes.Buffer // The output buffer.
|
||||
hijacked bool // Mark this request is hijacked or not.
|
||||
wroteHeader bool // Is header wrote or not, avoiding error: superfluous/multiple response.WriteHeader call.
|
||||
}
|
||||
|
||||
// RawWriter returns the underlying ResponseWriter.
|
||||
@ -54,7 +55,9 @@ func (w *ResponseWriter) Flush() {
|
||||
if w.hijacked {
|
||||
return
|
||||
}
|
||||
|
||||
if w.Status != 0 && !w.isHeaderWritten() {
|
||||
w.wroteHeader = true
|
||||
w.writer.WriteHeader(w.Status)
|
||||
}
|
||||
// Default status text output.
|
||||
@ -69,6 +72,9 @@ func (w *ResponseWriter) Flush() {
|
||||
|
||||
// isHeaderWrote checks and returns whether the header is written.
|
||||
func (w *ResponseWriter) isHeaderWritten() bool {
|
||||
if w.wroteHeader {
|
||||
return true
|
||||
}
|
||||
if _, ok := w.writer.Header()[responseHeaderContentLength]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -20,8 +20,8 @@ import (
|
||||
|
||||
// staticPathItem is the item struct for static path configuration.
|
||||
type staticPathItem struct {
|
||||
prefix string // The router URI.
|
||||
path string // The static path.
|
||||
Prefix string // The router URI.
|
||||
Path string // The static path.
|
||||
}
|
||||
|
||||
// SetIndexFiles sets the index files for server.
|
||||
@ -96,8 +96,8 @@ func (s *Server) AddStaticPath(prefix string, path string) {
|
||||
}
|
||||
}
|
||||
addItem := staticPathItem{
|
||||
prefix: prefix,
|
||||
path: realPath,
|
||||
Prefix: prefix,
|
||||
Path: realPath,
|
||||
}
|
||||
if len(s.config.StaticPaths) > 0 {
|
||||
s.config.StaticPaths = append(s.config.StaticPaths, addItem)
|
||||
@ -112,13 +112,13 @@ func (s *Server) AddStaticPath(prefix string, path string) {
|
||||
return r
|
||||
})
|
||||
for _, v := range s.config.StaticPaths {
|
||||
array.Add(v.prefix)
|
||||
array.Add(v.Prefix)
|
||||
}
|
||||
// Add the items to paths by previous sorted slice.
|
||||
paths := make([]staticPathItem, 0)
|
||||
for _, v := range array.Slice() {
|
||||
for _, item := range s.config.StaticPaths {
|
||||
if strings.EqualFold(gconv.String(v), item.prefix) {
|
||||
if strings.EqualFold(gconv.String(v), item.Prefix) {
|
||||
paths = append(paths, item)
|
||||
break
|
||||
}
|
||||
|
||||
@ -211,19 +211,19 @@ func (s *Server) searchStaticFile(uri string) *staticFile {
|
||||
// Firstly search the StaticPaths mapping.
|
||||
if len(s.config.StaticPaths) > 0 {
|
||||
for _, item := range s.config.StaticPaths {
|
||||
if len(uri) >= len(item.prefix) && strings.EqualFold(item.prefix, uri[0:len(item.prefix)]) {
|
||||
if len(uri) >= len(item.Prefix) && strings.EqualFold(item.Prefix, uri[0:len(item.Prefix)]) {
|
||||
// To avoid case like: /static/style -> /static/style.css
|
||||
if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
|
||||
if len(uri) > len(item.Prefix) && uri[len(item.Prefix)] != '/' {
|
||||
continue
|
||||
}
|
||||
file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
|
||||
file = gres.GetWithIndex(item.Path+uri[len(item.Prefix):], s.config.IndexFiles)
|
||||
if file != nil {
|
||||
return &staticFile{
|
||||
File: file,
|
||||
IsDir: file.FileInfo().IsDir(),
|
||||
}
|
||||
}
|
||||
path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
|
||||
path, dir = gspath.Search(item.Path, uri[len(item.Prefix):], s.config.IndexFiles...)
|
||||
if path != "" {
|
||||
return &staticFile{
|
||||
Path: path,
|
||||
@ -276,7 +276,7 @@ func (s *Server) serveFile(r *Request, f *staticFile, allowIndex ...bool) {
|
||||
}
|
||||
} else {
|
||||
info := f.File.FileInfo()
|
||||
http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), f.File)
|
||||
r.Response.ServeContent(info.Name(), info.ModTime(), f.File)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -300,7 +300,7 @@ func (s *Server) serveFile(r *Request, f *staticFile, allowIndex ...bool) {
|
||||
r.Response.WriteStatus(http.StatusForbidden)
|
||||
}
|
||||
} else {
|
||||
http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), file)
|
||||
r.Response.ServeContent(info.Name(), info.ModTime(), file)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -590,6 +590,17 @@ func Test_Middleware_CORSAndAuth(t *testing.T) {
|
||||
t.Assert(resp.Header["Access-Control-Max-Age"][0], "3628800")
|
||||
resp.Close()
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(client.SetHeader("Access-Control-Request-Headers", "GF,GoFrame").GetContent(ctx, "/"), "Not Found")
|
||||
t.Assert(client.SetHeader("Origin", "GoFrame").GetContent(ctx, "/"), "Not Found")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(client.SetHeader("Referer", "Referer").PostContent(ctx, "/"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
func MiddlewareScope1(r *ghttp.Request) {
|
||||
@ -697,3 +708,31 @@ func Test_Middleware_JsonBody(t *testing.T) {
|
||||
t.Assert(client.PutContent(ctx, "/", `{"name":}`), "the request body content should be JSON format")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MiddlewareHandlerResponse(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.GET("/403", func(r *ghttp.Request) {
|
||||
r.Response.WriteStatus(http.StatusForbidden, "")
|
||||
})
|
||||
group.GET("/default", func(r *ghttp.Request) {
|
||||
r.Response.WriteStatus(http.StatusInternalServerError, "")
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
rsp, err := client.Get(ctx, "/403")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
||||
rsp, err = client.Get(ctx, "/default")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusInternalServerError)
|
||||
})
|
||||
}
|
||||
|
||||
@ -141,3 +141,25 @@ func Test_Middleware_CORS2(t *testing.T) {
|
||||
resp.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Middleware_CORS3(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareCORS)
|
||||
group.GET("/user/list/{type}", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Get("type"))
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
client.SetHeader("Access-Control-Request-Method", "POST")
|
||||
resp, err := client.Get(ctx, "/api.v2/user/list/1")
|
||||
t.AssertNil(err)
|
||||
resp.Close()
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ package ghttp_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gbase64"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -18,6 +20,198 @@ import (
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func Test_Request_IsFileRequest(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.IsFileRequest())
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_IsAjaxRequest(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.IsAjaxRequest())
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetClientIp(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetClientIp())
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetHeader("X-Forwarded-For", "192.168.0.1")
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), "192.168.0.1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetUrl(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetUrl())
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetHeader("X-Forwarded-Proto", "https")
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), fmt.Sprintf("https://127.0.0.1:%d/", s.GetListenedPort()))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetReferer(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetReferer())
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetHeader("Referer", "Referer")
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), "Referer")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetServeHandler(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetServeHandler() != nil)
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
c := g.Client()
|
||||
c.SetHeader("Referer", "Referer")
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_BasicAuth(t *testing.T) {
|
||||
const (
|
||||
user = "root"
|
||||
pass = "123456"
|
||||
wrongPass = "12345"
|
||||
)
|
||||
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/auth1", func(r *ghttp.Request) {
|
||||
r.BasicAuth(user, pass, "tips")
|
||||
})
|
||||
group.ALL("/auth2", func(r *ghttp.Request) {
|
||||
r.BasicAuth(user, pass)
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
rsp, err := c.Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.Header.Get("WWW-Authenticate"), "Basic realm=\"tips\"")
|
||||
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
||||
|
||||
rsp, err = c.SetHeader("Authorization", user+pass).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
||||
|
||||
rsp, err = c.SetHeader("Authorization", "Test "+user+pass).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
||||
|
||||
rsp, err = c.SetHeader("Authorization", "Basic "+user+pass).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
||||
|
||||
rsp, err = c.SetHeader("Authorization", "Basic "+gbase64.EncodeString(user+pass)).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusForbidden)
|
||||
|
||||
rsp, err = c.SetHeader("Authorization", "Basic "+gbase64.EncodeString(user+":"+wrongPass)).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
||||
|
||||
rsp, err = c.BasicAuth(user, pass).Get(ctx, "/auth1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.StatusCode, http.StatusOK)
|
||||
|
||||
rsp, err = c.Get(ctx, "/auth2")
|
||||
t.AssertNil(err)
|
||||
t.Assert(rsp.Header.Get("WWW-Authenticate"), "Basic realm=\"Need Login\"")
|
||||
t.Assert(rsp.StatusCode, http.StatusUnauthorized)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_SetCtx(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
@ -42,3 +236,111 @@ func Test_Request_SetCtx(t *testing.T) {
|
||||
t.Assert(c.GetContent(ctx, "/"), "1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetCtx(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
ctx := context.WithValue(r.GetCtx(), "test", 1)
|
||||
r.SetCtx(ctx)
|
||||
r.Middleware.Next()
|
||||
})
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Context().Value("test"))
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(c.GetContent(ctx, "/"), "1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_GetCtxVar(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
})
|
||||
group.GET("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetCtxVar("key", "val"))
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), "val")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Request_Form(t *testing.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
type Default struct {
|
||||
D string
|
||||
}
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.SetForm("key", "val")
|
||||
r.Response.Write(r.GetForm("key"))
|
||||
})
|
||||
group.ALL("/useDef", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetForm("key", "defVal"))
|
||||
})
|
||||
group.ALL("/GetFormMap", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetFormMap(map[string]interface{}{"key": "val"}))
|
||||
})
|
||||
group.ALL("/GetFormMap1", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetFormMap(map[string]interface{}{"array": "val"}))
|
||||
})
|
||||
group.ALL("/GetFormMapStrVar", func(r *ghttp.Request) {
|
||||
if r.Get("a") != nil {
|
||||
r.Response.Write(r.GetFormMapStrVar()["a"])
|
||||
}
|
||||
})
|
||||
group.ALL("/GetFormStruct", func(r *ghttp.Request) {
|
||||
var user User
|
||||
if err := r.GetFormStruct(&user); err != nil {
|
||||
r.Response.Write(err.Error())
|
||||
} else {
|
||||
r.Response.Write(user.Name)
|
||||
}
|
||||
})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), "val")
|
||||
t.Assert(client.GetContent(ctx, "/useDef"), "defVal")
|
||||
t.Assert(client.PostContent(ctx, "/GetFormMap"), "{\"key\":\"val\"}")
|
||||
t.Assert(client.PostContent(ctx, "/GetFormMap", "array[]=1&array[]=2"), "{\"key\":\"val\"}")
|
||||
t.Assert(client.PostContent(ctx, "/GetFormMap1", "array[]=1&array[]=2"), "{\"array\":[\"1\",\"2\"]}")
|
||||
t.Assert(client.GetContent(ctx, "/GetFormMapStrVar", "a=1&b=2"), nil)
|
||||
t.Assert(client.PostContent(ctx, "/GetFormMapStrVar", "a=1&b=2"), `1`)
|
||||
t.Assert(client.PostContent(ctx, "/GetFormStruct", g.Map{
|
||||
"id": 1,
|
||||
"name": "john",
|
||||
}), "john")
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,9 +9,12 @@ package ghttp_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@ -171,7 +174,7 @@ func Test_Params_File_Batch(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Strict_Route_File_Single(t *testing.T) {
|
||||
func Test_Params_Strict_Route_File_Single_Ptr_Attrr(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
File *ghttp.UploadFile `type:"file"`
|
||||
@ -217,6 +220,48 @@ func Test_Params_Strict_Route_File_Single(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Strict_Route_File_Single_Struct_Attr(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
File ghttp.UploadFile `type:"file"`
|
||||
}
|
||||
type Res struct{}
|
||||
|
||||
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/upload/single", func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
var (
|
||||
r = g.RequestFromCtx(ctx)
|
||||
file = req.File
|
||||
)
|
||||
name, err := file.Save(dstDirPath)
|
||||
if err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(name)
|
||||
return
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// normal name
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
srcPath := gtest.DataPath("upload", "file1.txt")
|
||||
dstPath := gfile.Join(dstDirPath, "file1.txt")
|
||||
content := client.PostContent(ctx, "/upload/single", g.Map{
|
||||
"file": "@file:" + srcPath,
|
||||
})
|
||||
t.AssertNE(content, "")
|
||||
t.AssertNE(content, "upload failed")
|
||||
t.Assert(content, "file1.txt")
|
||||
t.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_File_Upload_Required(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
@ -241,3 +286,132 @@ func Test_Params_File_Upload_Required(t *testing.T) {
|
||||
t.Assert(content, `{"code":51,"message":"upload file is required","data":null}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_File_MarshalJSON(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/upload/single", func(r *ghttp.Request) {
|
||||
file := r.GetUploadFile("file")
|
||||
if file == nil {
|
||||
r.Response.WriteExit("upload file cannot be empty")
|
||||
}
|
||||
|
||||
if bytes, err := json.Marshal(file); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
} else {
|
||||
r.Response.WriteExit(bytes)
|
||||
}
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// normal name
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
srcPath := gtest.DataPath("upload", "file1.txt")
|
||||
content := client.PostContent(ctx, "/upload/single", g.Map{
|
||||
"file": "@file:" + srcPath,
|
||||
})
|
||||
t.Assert(strings.Contains(content, "file1.txt"), true)
|
||||
})
|
||||
}
|
||||
|
||||
// Select only one file when batch uploading
|
||||
func Test_Params_Strict_Route_File_Batch_Up_One(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
Files ghttp.UploadFiles `type:"file"`
|
||||
}
|
||||
type Res struct{}
|
||||
|
||||
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/upload/batch", func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
var (
|
||||
r = g.RequestFromCtx(ctx)
|
||||
files = req.Files
|
||||
)
|
||||
if len(files) == 0 {
|
||||
r.Response.WriteExit("upload file cannot be empty")
|
||||
}
|
||||
names, err := files.Save(dstDirPath)
|
||||
if err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(gstr.Join(names, ","))
|
||||
return
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// normal name
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
srcPath := gtest.DataPath("upload", "file1.txt")
|
||||
dstPath := gfile.Join(dstDirPath, "file1.txt")
|
||||
content := client.PostContent(ctx, "/upload/batch", g.Map{
|
||||
"files": "@file:" + srcPath,
|
||||
})
|
||||
t.AssertNE(content, "")
|
||||
t.AssertNE(content, "upload file cannot be empty")
|
||||
t.AssertNE(content, "upload failed")
|
||||
t.Assert(content, "file1.txt")
|
||||
t.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
|
||||
})
|
||||
}
|
||||
|
||||
// Select multiple files during batch upload
|
||||
func Test_Params_Strict_Route_File_Batch_Up_Multiple(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
Files ghttp.UploadFiles `type:"file"`
|
||||
}
|
||||
type Res struct{}
|
||||
|
||||
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/upload/batch", func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
var (
|
||||
r = g.RequestFromCtx(ctx)
|
||||
files = req.Files
|
||||
)
|
||||
if len(files) == 0 {
|
||||
r.Response.WriteExit("upload file cannot be empty")
|
||||
}
|
||||
names, err := files.Save(dstDirPath)
|
||||
if err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(gstr.Join(names, ","))
|
||||
return
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// normal name
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
srcPath1 := gtest.DataPath("upload", "file1.txt")
|
||||
srcPath2 := gtest.DataPath("upload", "file2.txt")
|
||||
dstPath1 := gfile.Join(dstDirPath, "file1.txt")
|
||||
dstPath2 := gfile.Join(dstDirPath, "file2.txt")
|
||||
content := client.PostContent(ctx, "/upload/batch",
|
||||
"files=@file:"+srcPath1+
|
||||
"&files=@file:"+srcPath2,
|
||||
)
|
||||
t.AssertNE(content, "")
|
||||
t.AssertNE(content, "upload file cannot be empty")
|
||||
t.AssertNE(content, "upload failed")
|
||||
t.Assert(content, "file1.txt,file2.txt")
|
||||
t.Assert(gfile.GetContents(dstPath1), gfile.GetContents(srcPath1))
|
||||
t.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
|
||||
})
|
||||
}
|
||||
|
||||
@ -32,6 +32,11 @@ func Test_Params_Parse(t *testing.T) {
|
||||
}
|
||||
r.Response.WriteExit(user.Map["id"], user.Map["score"])
|
||||
})
|
||||
s.BindHandler("/parseErr", func(r *ghttp.Request) {
|
||||
var user User
|
||||
err := r.Parse(user)
|
||||
r.Response.WriteExit(err != nil)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
@ -41,6 +46,7 @@ func Test_Params_Parse(t *testing.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`)
|
||||
t.Assert(client.PostContent(ctx, "/parseErr", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), true)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -439,6 +439,19 @@ func Test_Params_GetRequestMap(t *testing.T) {
|
||||
s.BindHandler("/map", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRequestMap())
|
||||
})
|
||||
s.BindHandler("/withKVMap", func(r *ghttp.Request) {
|
||||
m := r.GetRequestMap(map[string]interface{}{"id": 2})
|
||||
r.Response.Write(m["id"])
|
||||
})
|
||||
s.BindHandler("/paramsMapWithKVMap", func(r *ghttp.Request) {
|
||||
r.SetParam("name", "john")
|
||||
m := r.GetRequestMap(map[string]interface{}{"id": 2})
|
||||
r.Response.Write(m["id"])
|
||||
})
|
||||
s.BindHandler("/{name}.map", func(r *ghttp.Request) {
|
||||
m := r.GetRequestMap(map[string]interface{}{"id": 2})
|
||||
r.Response.Write(m["id"])
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
@ -456,6 +469,15 @@ func Test_Params_GetRequestMap(t *testing.T) {
|
||||
),
|
||||
`{"attach":"","returnmsg":"Success"}`,
|
||||
)
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/john.map", "name=john"), 2)
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/withKVMap", "name=john"), 2)
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/paramsMapWithKVMap"), 2)
|
||||
|
||||
client.SetContentType("application/json")
|
||||
t.Assert(client.GetContent(ctx, "/withKVMap", "name=john"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -636,3 +658,206 @@ func Test_Params_Parse_EmbeddedWithAliasName2(t *testing.T) {
|
||||
t.Assert(client.GetContent(ctx, "/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":1,"Page":2,"Size":10,"Sort":0,"UserId":0}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetParam(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetParam("key", "val"))
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/"), "val")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_SetQuery(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/SetQuery", func(r *ghttp.Request) {
|
||||
r.SetQuery("a", 100)
|
||||
r.Response.Write(r.GetQuery("a"))
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/SetQuery"), "100")
|
||||
t.Assert(client.GetContent(ctx, "/SetQuery?a=1"), "100")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetQuery(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetQuery", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetQuery("a", 200))
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetQuery"), 200)
|
||||
t.Assert(client.SetContentType("application/json").GetContent(ctx, "/GetQuery", "a=100"), 100)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetQueryMap(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetQueryMap", func(r *ghttp.Request) {
|
||||
if m := r.GetQueryMap(); len(m) > 0 {
|
||||
r.Response.Write(m["name"])
|
||||
}
|
||||
})
|
||||
s.BindHandler("/GetQueryMapWithKVMap", func(r *ghttp.Request) {
|
||||
if m := r.GetQueryMap(map[string]interface{}{"id": 1}); len(m) > 0 {
|
||||
r.Response.Write(m["id"])
|
||||
}
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
client.SetContentType("application/json")
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMap", "id=1&name=john"), `john`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapWithKVMap"), 1)
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapWithKVMap", "name=john"), 1)
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapWithKVMap", "id=2&name=john"), 2)
|
||||
client.SetContentType("application/json")
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapWithKVMap", "name=john"), 1)
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapWithKVMap", "id=2&name=john"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetQueryMapStrStr(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetQueryMapStrStr", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetQueryMapStrStr())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapStrStr"), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetQueryMapStrVar(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetQueryMapStrVar", func(r *ghttp.Request) {
|
||||
m := r.GetQueryMapStrVar()
|
||||
r.Response.Write(m["id"])
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapStrVar"), "")
|
||||
t.Assert(client.GetContent(ctx, "/GetQueryMapStrVar", "id=1"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetRequest(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetRequest", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRequest("id"))
|
||||
})
|
||||
s.BindHandler("/GetRequestWithDef", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRequest("id", 2))
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetRequestWithDef"), 2)
|
||||
|
||||
client.SetContentType("application/json")
|
||||
t.Assert(client.GetContent(ctx, "/GetRequest", "id=1"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetRequestMapStrStr(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetRequestMapStrStr", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRequestMapStrStr())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetRequestMapStrStr"), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_GetRequestMapStrVar(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/GetRequestMapStrVar", func(r *ghttp.Request) {
|
||||
m := r.GetRequestMapStrVar()
|
||||
r.Response.Write(m["id"])
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/GetRequestMapStrVar"), "")
|
||||
t.Assert(client.GetContent(ctx, "/GetRequestMapStrVar", "id=1"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
309
net/ghttp/ghttp_z_unit_feature_response_test.go
Normal file
309
net/ghttp/ghttp_z_unit_feature_response_test.go
Normal file
@ -0,0 +1,309 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/encoding/gxml"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gview"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func Test_Response_ServeFile(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/ServeFile", func(r *ghttp.Request) {
|
||||
filePath := r.GetQuery("filePath")
|
||||
r.Response.ServeFile(filePath.String())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
srcPath := gtest.DataPath("upload", "file1.txt")
|
||||
t.Assert(client.GetContent(ctx, "/ServeFile", "filePath=file1.txt"), "Not Found")
|
||||
|
||||
t.Assert(
|
||||
client.GetContent(ctx, "/ServeFile", "filePath="+srcPath),
|
||||
"file1.txt: This file is for uploading unit test case.")
|
||||
|
||||
t.Assert(
|
||||
strings.Contains(
|
||||
client.GetContent(ctx, "/ServeFile", "filePath=files/server.key"),
|
||||
"BEGIN RSA PRIVATE KEY"),
|
||||
true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_ServeFileDownload(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/ServeFileDownload", func(r *ghttp.Request) {
|
||||
filePath := r.GetQuery("filePath")
|
||||
r.Response.ServeFileDownload(filePath.String())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
srcPath := gtest.DataPath("upload", "file1.txt")
|
||||
t.Assert(client.GetContent(ctx, "/ServeFileDownload", "filePath=file1.txt"), "Not Found")
|
||||
|
||||
t.Assert(
|
||||
client.GetContent(ctx, "/ServeFileDownload", "filePath="+srcPath),
|
||||
"file1.txt: This file is for uploading unit test case.")
|
||||
|
||||
t.Assert(
|
||||
strings.Contains(
|
||||
client.GetContent(ctx, "/ServeFileDownload", "filePath=files/server.key"),
|
||||
"BEGIN RSA PRIVATE KEY"),
|
||||
true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_Redirect(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("RedirectResult")
|
||||
})
|
||||
s.BindHandler("/RedirectTo", func(r *ghttp.Request) {
|
||||
r.Response.RedirectTo("/")
|
||||
})
|
||||
s.BindHandler("/RedirectTo301", func(r *ghttp.Request) {
|
||||
r.Response.RedirectTo("/", http.StatusMovedPermanently)
|
||||
})
|
||||
s.BindHandler("/RedirectBack", func(r *ghttp.Request) {
|
||||
r.Response.RedirectBack()
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/RedirectTo"), "RedirectResult")
|
||||
t.Assert(client.GetContent(ctx, "/RedirectTo301"), "RedirectResult")
|
||||
t.Assert(client.SetHeader("Referer", "/").GetContent(ctx, "/RedirectBack"), "RedirectResult")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_Buffer(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/Buffer", func(r *ghttp.Request) {
|
||||
name := r.GetQuery("name").Bytes()
|
||||
r.Response.SetBuffer(name)
|
||||
buffer := r.Response.Buffer()
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.Write(buffer)
|
||||
})
|
||||
s.BindHandler("/BufferString", func(r *ghttp.Request) {
|
||||
name := r.GetQuery("name").Bytes()
|
||||
r.Response.SetBuffer(name)
|
||||
bufferString := r.Response.BufferString()
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.Write(bufferString)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/Buffer", "name=john"), []byte("john"))
|
||||
t.Assert(client.GetContent(ctx, "/BufferString", "name=john"), "john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_WriteTpl(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gview.New(gtest.DataPath("template", "basic"))
|
||||
s := g.Server(guid.S())
|
||||
s.SetView(v)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("noexist.html", g.Map{
|
||||
"name": "john",
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.AssertNE(client.GetContent(ctx, "/"), "Name:john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_WriteTplDefault(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gview.New()
|
||||
v.SetDefaultFile(gtest.DataPath("template", "basic", "index.html"))
|
||||
s := g.Server(guid.S())
|
||||
s.SetView(v)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTplDefault(g.Map{"name": "john"})
|
||||
t.AssertNil(err)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), "Name:john")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gview.New()
|
||||
v.SetDefaultFile(gtest.DataPath("template", "basic", "noexit.html"))
|
||||
s := g.Server(guid.S())
|
||||
s.SetView(v)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTplDefault(g.Map{"name": "john"})
|
||||
t.AssertNil(err)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.AssertNE(client.GetContent(ctx, "/"), "Name:john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_ParseTplDefault(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gview.New()
|
||||
v.SetDefaultFile(gtest.DataPath("template", "basic", "index.html"))
|
||||
s := g.Server(guid.S())
|
||||
s.SetView(v)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
res, err := r.Response.ParseTplDefault(g.Map{"name": "john"})
|
||||
t.AssertNil(err)
|
||||
r.Response.Write(res)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), "Name:john")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Response_Write(t *testing.T) {
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write()
|
||||
})
|
||||
s.BindHandler("/WriteOverExit", func(r *ghttp.Request) {
|
||||
r.Response.Write("WriteOverExit")
|
||||
r.Response.WriteOverExit("")
|
||||
})
|
||||
s.BindHandler("/WritefExit", func(r *ghttp.Request) {
|
||||
r.Response.WritefExit("%s", "WritefExit")
|
||||
})
|
||||
s.BindHandler("/Writeln", func(r *ghttp.Request) {
|
||||
name := r.GetQuery("name")
|
||||
r.Response.Writeln(name)
|
||||
})
|
||||
s.BindHandler("/WritelnNil", func(r *ghttp.Request) {
|
||||
r.Response.Writeln()
|
||||
})
|
||||
s.BindHandler("/Writefln", func(r *ghttp.Request) {
|
||||
name := r.GetQuery("name")
|
||||
r.Response.Writefln("%s", name)
|
||||
})
|
||||
s.BindHandler("/WriteJson", func(r *ghttp.Request) {
|
||||
m := map[string]string{"name": "john"}
|
||||
if bytes, err := json.Marshal(m); err == nil {
|
||||
r.Response.WriteJson(bytes)
|
||||
}
|
||||
})
|
||||
s.BindHandler("/WriteJsonP", func(r *ghttp.Request) {
|
||||
m := map[string]string{"name": "john"}
|
||||
if bytes, err := json.Marshal(m); err == nil {
|
||||
r.Response.WriteJsonP(bytes)
|
||||
}
|
||||
})
|
||||
s.BindHandler("/WriteJsonPWithStruct", func(r *ghttp.Request) {
|
||||
user := User{"john"}
|
||||
r.Response.WriteJsonP(user)
|
||||
})
|
||||
s.BindHandler("/WriteXml", func(r *ghttp.Request) {
|
||||
m := map[string]interface{}{"name": "john"}
|
||||
if bytes, err := gxml.Encode(m); err == nil {
|
||||
r.Response.WriteXml(bytes)
|
||||
}
|
||||
})
|
||||
s.BindHandler("/WriteXmlWithStruct", func(r *ghttp.Request) {
|
||||
user := User{"john"}
|
||||
r.Response.WriteXml(user)
|
||||
})
|
||||
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
t.Assert(client.GetContent(ctx, "/"), "")
|
||||
t.Assert(client.GetContent(ctx, "/WriteOverExit"), "")
|
||||
t.Assert(client.GetContent(ctx, "/WritefExit"), "WritefExit")
|
||||
t.Assert(client.GetContent(ctx, "/Writeln"), "\n")
|
||||
t.Assert(client.GetContent(ctx, "/WritelnNil"), "\n")
|
||||
t.Assert(client.GetContent(ctx, "/Writeln", "name=john"), "john\n")
|
||||
t.Assert(client.GetContent(ctx, "/Writefln", "name=john"), "john\n")
|
||||
t.Assert(client.GetContent(ctx, "/WriteJson"), "{\"name\":\"john\"}")
|
||||
t.Assert(client.GetContent(ctx, "/WriteJsonP"), "{\"name\":\"john\"}")
|
||||
t.Assert(client.GetContent(ctx, "/WriteJsonPWithStruct"), "{\"name\":\"john\"}")
|
||||
t.Assert(client.GetContent(ctx, "/WriteJsonPWithStruct", "callback=callback"),
|
||||
"callback({\"name\":\"john\"})")
|
||||
t.Assert(client.GetContent(ctx, "/WriteXml"), "<name>john</name>")
|
||||
t.Assert(client.GetContent(ctx, "/WriteXmlWithStruct"), "<name>john</name>")
|
||||
})
|
||||
}
|
||||
@ -72,6 +72,12 @@ func Test_Router_Basic2(t *testing.T) {
|
||||
|
||||
func Test_Router_Value(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRouterMap()["hash"])
|
||||
})
|
||||
s.BindHandler("/GetRouter", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRouter("name", "john").String())
|
||||
})
|
||||
s.BindHandler("/{hash}", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRouter("hash").String())
|
||||
})
|
||||
@ -89,6 +95,8 @@ func Test_Router_Value(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(client.GetContent(ctx, "/"), "")
|
||||
t.Assert(client.GetContent(ctx, "/GetRouter"), "john")
|
||||
t.Assert(client.GetContent(ctx, "/data"), "data")
|
||||
t.Assert(client.GetContent(ctx, "/data.json"), "json")
|
||||
t.Assert(client.GetContent(ctx, "/data.json.map"), "json")
|
||||
|
||||
@ -131,7 +131,6 @@ func Test_Issue1653(t *testing.T) {
|
||||
s.Group("/boot", func(grp *ghttp.RouterGroup) {
|
||||
grp.Bind(Issue1653Foo)
|
||||
})
|
||||
s.SetPort(9527)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
@ -207,7 +206,6 @@ func Test_Issue662(t *testing.T) {
|
||||
s.Group("/boot", func(grp *ghttp.RouterGroup) {
|
||||
grp.Bind(Foo1)
|
||||
})
|
||||
s.SetPort(8888)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
@ -251,7 +249,6 @@ func Test_Issue2172(t *testing.T) {
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Bind(api)
|
||||
})
|
||||
s.SetPort(8888)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
@ -264,3 +261,22 @@ func Test_Issue2172(t *testing.T) {
|
||||
t.Assert(c.PostContent(ctx, "/demo", dataReq), `{"code":0,"message":"","data":{"Content":"{\"asd\":1}"}}`)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/2334
|
||||
func Test_Issue2334(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.SetServerRoot(gtest.DataPath("static1"))
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client()
|
||||
c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(c.GetContent(ctx, "/index.html"), "index")
|
||||
|
||||
c.SetHeader("If-Modified-Since", "Mon, 12 Dec 2040 05:53:35 GMT")
|
||||
res, _ := c.Get(ctx, "/index.html")
|
||||
t.Assert(res.StatusCode, 304)
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,11 +10,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gurl"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/httputil"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -139,3 +141,30 @@ func Test_RoutePathParams(t *testing.T) {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BuildParams(t *testing.T) {
|
||||
// normal && special cases
|
||||
params := map[string]string{
|
||||
"val": "12345678",
|
||||
"code1": "x&a=1", // for fix
|
||||
"code2": "x&a=111",
|
||||
"id": "1+- ", // for fix
|
||||
"f": "1#a=+- ",
|
||||
"v": "",
|
||||
"n": "null",
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
res1 := httputil.BuildParams(params)
|
||||
vs, _ := url.ParseQuery(res1)
|
||||
t.Assert(len(params), len(vs))
|
||||
for k := range vs {
|
||||
vv := vs.Get(k)
|
||||
_, ok := params[k]
|
||||
// check no additional param
|
||||
t.Assert(ok, true)
|
||||
// check equal
|
||||
t.AssertEQ(params[k], vv)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
Responses: map[string]ResponseRef{},
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
seRequirement = SecurityRequirement{}
|
||||
)
|
||||
// Path check.
|
||||
if in.Path == "" {
|
||||
@ -145,6 +146,18 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
}
|
||||
}
|
||||
|
||||
// path security
|
||||
// note: the security schema type only support http and apiKey;not support oauth2 and openIdConnect.
|
||||
// multi schema separate with comma, e.g. `security: apiKey1,apiKey2`
|
||||
TagNameSecurity := gmeta.Get(inputObject.Interface(), gtag.Security).String()
|
||||
securities := gstr.SplitAndTrim(TagNameSecurity, ",")
|
||||
for _, sec := range securities {
|
||||
seRequirement[sec] = []string{}
|
||||
}
|
||||
if len(securities) > 0 {
|
||||
operation.Security = &SecurityRequirements{seRequirement}
|
||||
}
|
||||
|
||||
// =================================================================================================================
|
||||
// Request Parameter.
|
||||
// =================================================================================================================
|
||||
|
||||
@ -1046,3 +1046,56 @@ func Test_NameFromJsonTag(t *testing.T) {
|
||||
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateReq":{"properties":{"nick_name":{"format":"string","properties":{},"type":"string"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOpenApiV3_PathSecurity(t *testing.T) {
|
||||
type CommonResponse struct {
|
||||
Code int `json:"code" description:"Error code"`
|
||||
Message string `json:"message" description:"Error message"`
|
||||
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"PUT" security:"apiKey"` // 这里的apiKey要和openApi定义的key一致
|
||||
Product string `json:"product" v:"required" description:"Unique product key"`
|
||||
Name string `json:"name" v:"required" description:"Instance name"`
|
||||
}
|
||||
type Res struct{}
|
||||
|
||||
f := func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
oai = goai.New()
|
||||
)
|
||||
|
||||
oai.Config.CommonResponse = CommonResponse{}
|
||||
oai.Components = goai.Components{
|
||||
SecuritySchemes: goai.SecuritySchemes{
|
||||
"apiKey": goai.SecuritySchemeRef{
|
||||
Ref: "",
|
||||
Value: &goai.SecurityScheme{
|
||||
// 此处type是openApi的规定,详见 https://swagger.io/docs/specification/authentication/api-keys/
|
||||
Type: "apiKey",
|
||||
In: "header",
|
||||
Name: "X-API-KEY",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err = oai.Add(goai.AddInput{
|
||||
Path: "/index",
|
||||
Object: f,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Components.SecuritySchemes), 1)
|
||||
t.Assert(oai.Components.SecuritySchemes["apiKey"].Value.Type, "apiKey")
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -427,14 +427,17 @@ func (c *AdapterMemory) syncEventAndClearExpired(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
// Processing expired keys from LRU.
|
||||
if c.cap > 0 && c.lruGetList.Len() > 0 {
|
||||
for {
|
||||
if v := c.lruGetList.PopFront(); v != nil {
|
||||
c.lru.Push(v)
|
||||
} else {
|
||||
break
|
||||
if c.cap > 0 {
|
||||
if c.lruGetList.Len() > 0 {
|
||||
for {
|
||||
if v := c.lruGetList.PopFront(); v != nil {
|
||||
c.lru.Push(v)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
c.lru.SyncAndClear(ctx)
|
||||
}
|
||||
// ========================
|
||||
// Data Cleaning up.
|
||||
|
||||
@ -8,7 +8,6 @@ package gcache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
@ -35,7 +34,6 @@ func newMemCacheLru(cache *AdapterMemory) *adapterMemoryLru {
|
||||
rawList: glist.New(true),
|
||||
closed: gtype.NewBool(),
|
||||
}
|
||||
gtimer.AddSingleton(context.Background(), time.Second, lru.SyncAndClear)
|
||||
return lru
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ package gcache_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -60,6 +61,10 @@ func Test_AdapterRedis_Basic1(t *testing.T) {
|
||||
n, _ := cacheRedis.Size(ctx)
|
||||
t.Assert(n, 0)
|
||||
})
|
||||
// Close
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNil(cacheRedis.Close(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_Basic2(t *testing.T) {
|
||||
@ -152,6 +157,37 @@ func Test_AdapterRedis_UpdateExpire(t *testing.T) {
|
||||
t.Assert(d > time.Second, true)
|
||||
t.Assert(d <= 2*time.Second, true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key"
|
||||
value = "value"
|
||||
)
|
||||
t.AssertNil(cacheRedis.Set(ctx, key, value, time.Second))
|
||||
v, _ := cacheRedis.Get(ctx, key)
|
||||
t.Assert(v, value)
|
||||
|
||||
_, err := cacheRedis.UpdateExpire(ctx, key, -1)
|
||||
t.AssertNil(err)
|
||||
v, _ = cacheRedis.Get(ctx, key)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key"
|
||||
value = "value"
|
||||
)
|
||||
|
||||
t.AssertNil(cacheRedis.Set(ctx, key, value, time.Second))
|
||||
v, _ := cacheRedis.Get(ctx, key)
|
||||
t.Assert(v, value)
|
||||
|
||||
_, err := cacheRedis.UpdateExpire(ctx, key, 0)
|
||||
t.AssertNil(err)
|
||||
v, _ = cacheRedis.Get(ctx, key)
|
||||
t.Assert(v, value)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_SetIfNotExist(t *testing.T) {
|
||||
@ -176,6 +212,69 @@ func Test_AdapterRedis_SetIfNotExist(t *testing.T) {
|
||||
d, _ := cacheRedis.GetExpire(ctx, key)
|
||||
t.Assert(d > time.Millisecond*500, true)
|
||||
t.Assert(d <= time.Second, true)
|
||||
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key"
|
||||
value1 = "value1"
|
||||
key2 = "key2"
|
||||
value2 = "value2"
|
||||
)
|
||||
t.AssertNil(cacheRedis.Set(ctx, key, value1, time.Second))
|
||||
v, _ := cacheRedis.Get(ctx, key)
|
||||
t.Assert(v, value1)
|
||||
|
||||
r, _ := cacheRedis.SetIfNotExist(ctx, key, value1, -1)
|
||||
t.Assert(r, true)
|
||||
v, _ = cacheRedis.Get(ctx, key)
|
||||
t.AssertNil(v)
|
||||
|
||||
r, _ = cacheRedis.SetIfNotExist(ctx, key, value2, -1)
|
||||
t.Assert(r, false)
|
||||
|
||||
r, _ = cacheRedis.SetIfNotExist(ctx, key2, value2, time.Second)
|
||||
t.Assert(r, true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_SetIfNotExistFunc(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
exist, err := cacheRedis.SetIfNotExistFunc(ctx, 1, func(ctx context.Context) (value interface{}, err error) {
|
||||
return 11, nil
|
||||
}, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(exist, false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_SetIfNotExistFuncLock(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
exist, err := cacheRedis.SetIfNotExistFuncLock(ctx, 1, func(ctx context.Context) (value interface{}, err error) {
|
||||
return 11, nil
|
||||
}, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(exist, false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_GetOrSet(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key"
|
||||
value1 = "valueFunc"
|
||||
)
|
||||
v, err := cacheRedis.GetOrSet(ctx, key, value1, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v, value1)
|
||||
|
||||
v, err = cacheRedis.GetOrSet(ctx, key, value1, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v, value1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -192,6 +291,24 @@ func Test_AdapterRedis_GetOrSetFunc(t *testing.T) {
|
||||
}, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v, value1)
|
||||
|
||||
v, err = cacheRedis.GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) {
|
||||
value = value1
|
||||
return
|
||||
}, 0)
|
||||
t.AssertNil(err)
|
||||
t.Assert(v, value1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key1"
|
||||
)
|
||||
v, err := cacheRedis.GetOrSetFunc(ctx, key, func(ctx context.Context) (interface{}, error) {
|
||||
return nil, nil
|
||||
}, 0)
|
||||
t.AssertNil(err)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -210,3 +327,86 @@ func Test_AdapterRedis_GetOrSetFuncLock(t *testing.T) {
|
||||
t.Assert(v, value1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_SetMap(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNil(cacheRedis.SetMap(ctx, g.MapAnyAny{}, 0))
|
||||
|
||||
t.AssertNil(cacheRedis.SetMap(ctx, g.MapAnyAny{1: 11, 2: 22}, 0))
|
||||
v, _ := cacheRedis.Get(ctx, 1)
|
||||
t.Assert(v, 11)
|
||||
|
||||
t.AssertNil(cacheRedis.SetMap(ctx, g.MapAnyAny{1: 11, 2: 22}, -1))
|
||||
v, _ = cacheRedis.Get(ctx, 1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_Contains(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNil(cacheRedis.Set(ctx, "key", "value", 0))
|
||||
|
||||
result, err := cacheRedis.Contains(ctx, "key")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, true)
|
||||
|
||||
result, err = cacheRedis.Contains(ctx, "key1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_Keys(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNil(cacheRedis.Set(ctx, "key1", "value1", 0))
|
||||
|
||||
keys, err := cacheRedis.Keys(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(keys), 1)
|
||||
|
||||
t.AssertNil(cacheRedis.Set(ctx, "key2", "value2", 0))
|
||||
|
||||
keys, err = cacheRedis.Keys(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(keys), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_Values(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNil(cacheRedis.Set(ctx, "key1", "value1", 0))
|
||||
|
||||
values, err := cacheRedis.Values(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(values), 1)
|
||||
|
||||
t.AssertNil(cacheRedis.Set(ctx, "key2", "value2", 0))
|
||||
|
||||
values, err = cacheRedis.Values(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(values), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AdapterRedis_Remove(t *testing.T) {
|
||||
defer cacheRedis.Clear(ctx)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
key = "key"
|
||||
value = "value"
|
||||
)
|
||||
val, err := cacheRedis.Remove(ctx)
|
||||
t.AssertNil(val)
|
||||
t.AssertNil(err)
|
||||
|
||||
t.AssertNil(cacheRedis.Set(ctx, key, value, 0))
|
||||
|
||||
val, err = cacheRedis.Remove(ctx, key)
|
||||
t.Assert(val, value)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -569,3 +569,56 @@ func TestCache_Removes(t *testing.T) {
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_Basic_Must(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
defer gcache.Remove(ctx, g.Slice{1, 2, 3, 4}...)
|
||||
|
||||
t.AssertNil(gcache.Set(ctx, 1, 11, 0))
|
||||
v := gcache.MustGet(ctx, 1)
|
||||
t.Assert(v, 11)
|
||||
gcache.MustGetOrSet(ctx, 2, 22, 0)
|
||||
v = gcache.MustGet(ctx, 2)
|
||||
t.Assert(v, 22)
|
||||
|
||||
gcache.MustGetOrSetFunc(ctx, 3, func(ctx context.Context) (value interface{}, err error) {
|
||||
return 33, nil
|
||||
}, 0)
|
||||
v = gcache.MustGet(ctx, 3)
|
||||
t.Assert(v, 33)
|
||||
|
||||
gcache.GetOrSetFuncLock(ctx, 4, func(ctx context.Context) (value interface{}, err error) {
|
||||
return 44, nil
|
||||
}, 0)
|
||||
v = gcache.MustGet(ctx, 4)
|
||||
t.Assert(v, 44)
|
||||
|
||||
t.Assert(gcache.MustContains(ctx, 1), true)
|
||||
|
||||
t.AssertNil(gcache.Set(ctx, 1, 11, 3*time.Second))
|
||||
expire := gcache.MustGetExpire(ctx, 1)
|
||||
t.AssertGE(expire, 0)
|
||||
|
||||
n := gcache.MustSize(ctx)
|
||||
t.Assert(n, 4)
|
||||
|
||||
data := gcache.MustData(ctx)
|
||||
t.Assert(len(data), 4)
|
||||
|
||||
keys := gcache.MustKeys(ctx)
|
||||
t.Assert(len(keys), 4)
|
||||
|
||||
keyStrings := gcache.MustKeyStrings(ctx)
|
||||
t.Assert(len(keyStrings), 4)
|
||||
|
||||
values := gcache.MustValues(ctx)
|
||||
t.Assert(len(values), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCache_NewWithAdapter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
cache := gcache.NewWithAdapter(gcache.NewAdapterMemory())
|
||||
t.AssertNE(cache, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
@ -58,6 +59,15 @@ func Test_BuildOptions(t *testing.T) {
|
||||
}, "-test")
|
||||
t.Assert(s, "-testn=john")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gcmd.BuildOptions(g.MapStrStr{
|
||||
"n1": "john",
|
||||
"n2": "huang",
|
||||
})
|
||||
t.Assert(strings.Contains(s, "-n1=john"), true)
|
||||
t.Assert(strings.Contains(s, "-n2=huang"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetWithEnv(t *testing.T) {
|
||||
|
||||
@ -112,21 +112,16 @@ func TestCron_Add_FixedPattern(t *testing.T) {
|
||||
func doTestCronAddFixedPattern(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
now = time.Now()
|
||||
cron = gcron.New()
|
||||
array = garray.New(true)
|
||||
minutes = now.Minute()
|
||||
seconds = now.Second() + 2
|
||||
now = time.Now()
|
||||
cron = gcron.New()
|
||||
array = garray.New(true)
|
||||
expect = now.Add(time.Second * 2)
|
||||
)
|
||||
defer cron.Close()
|
||||
|
||||
if seconds >= 60 {
|
||||
seconds %= 60
|
||||
minutes++
|
||||
}
|
||||
var pattern = fmt.Sprintf(
|
||||
`%d %d %d %d %d %s`,
|
||||
seconds, minutes, now.Hour(), now.Day(), now.Month(), now.Weekday().String(),
|
||||
expect.Second(), expect.Minute(), expect.Hour(), expect.Day(), expect.Month(), expect.Weekday().String(),
|
||||
)
|
||||
cron.SetLogger(g.Log())
|
||||
g.Log().Debugf(ctx, `pattern: %s`, pattern)
|
||||
|
||||
@ -50,34 +50,35 @@ func CopyFile(src, dst string) (err error) {
|
||||
if src == dst {
|
||||
return nil
|
||||
}
|
||||
in, err := Open(src)
|
||||
var inFile *os.File
|
||||
inFile, err = Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if e := in.Close(); e != nil {
|
||||
if e := inFile.Close(); e != nil {
|
||||
err = gerror.Wrapf(e, `file close failed for "%s"`, src)
|
||||
}
|
||||
}()
|
||||
out, err := Create(dst)
|
||||
var outFile *os.File
|
||||
outFile, err = Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if e := out.Close(); e != nil {
|
||||
if e := outFile.Close(); e != nil {
|
||||
err = gerror.Wrapf(e, `file close failed for "%s"`, dst)
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
if _, err = io.Copy(outFile, inFile); err != nil {
|
||||
err = gerror.Wrapf(err, `io.Copy failed from "%s" to "%s"`, src, dst)
|
||||
return
|
||||
}
|
||||
if err = out.Sync(); err != nil {
|
||||
if err = outFile.Sync(); err != nil {
|
||||
err = gerror.Wrapf(err, `file sync failed for file "%s"`, dst)
|
||||
return
|
||||
}
|
||||
err = Chmod(dst, DefaultPermCopy)
|
||||
if err != nil {
|
||||
if err = Chmod(dst, DefaultPermCopy); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
|
||||
@ -72,6 +72,15 @@ func Test_Create(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tmpPath := gfile.Join(gfile.Temp(), "test/testfile_cc1.txt")
|
||||
fileobj, err := gfile.Create(tmpPath)
|
||||
defer gfile.Remove(tmpPath)
|
||||
t.AssertNE(fileobj, nil)
|
||||
t.AssertNil(err)
|
||||
fileobj.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Open(t *testing.T) {
|
||||
|
||||
@ -9,6 +9,7 @@ package glog_test
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@ -20,6 +21,386 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
func TestCase(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(glog.Instance(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultLogger(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(defaultLog, nil)
|
||||
log := glog.New()
|
||||
glog.SetDefaultLogger(log)
|
||||
t.AssertEQ(glog.DefaultLogger(), defaultLog)
|
||||
t.AssertEQ(glog.Expose(), defaultLog)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.Print(ctx, "Print")
|
||||
glog.Printf(ctx, "%s", "Printf")
|
||||
glog.Info(ctx, "Info")
|
||||
glog.Infof(ctx, "%s", "Infof")
|
||||
glog.Debug(ctx, "Debug")
|
||||
glog.Debugf(ctx, "%s", "Debugf")
|
||||
glog.Notice(ctx, "Notice")
|
||||
glog.Noticef(ctx, "%s", "Noticef")
|
||||
glog.Warning(ctx, "Warning")
|
||||
glog.Warningf(ctx, "%s", "Warningf")
|
||||
glog.Error(ctx, "Error")
|
||||
glog.Errorf(ctx, "%s", "Errorf")
|
||||
glog.Critical(ctx, "Critical")
|
||||
glog.Criticalf(ctx, "%s", "Criticalf")
|
||||
})
|
||||
}
|
||||
|
||||
func TestChaining(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(glog.Cat("module"), nil)
|
||||
t.AssertNE(glog.File("test.log"), nil)
|
||||
t.AssertNE(glog.Level(glog.LEVEL_ALL), nil)
|
||||
t.AssertNE(glog.LevelStr("all"), nil)
|
||||
t.AssertNE(glog.Skip(1), nil)
|
||||
t.AssertNE(glog.Stack(false), nil)
|
||||
t.AssertNE(glog.StackWithFilter("none"), nil)
|
||||
t.AssertNE(glog.Stdout(false), nil)
|
||||
t.AssertNE(glog.Header(false), nil)
|
||||
t.AssertNE(glog.Line(false), nil)
|
||||
t.AssertNE(glog.Async(false), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetFile(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetFile("test.log")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetLevel(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetLevel(glog.LEVEL_ALL)
|
||||
t.Assert(glog.GetLevel()&glog.LEVEL_ALL, glog.LEVEL_ALL)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetAsync(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetAsync(false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetStdoutPrint(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetStdoutPrint(false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetHeaderPrint(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetHeaderPrint(false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetPrefix(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetPrefix("log_prefix")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetConfigWithMap(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(glog.SetConfigWithMap(map[string]interface{}{
|
||||
"level": "all",
|
||||
}), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetPath(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(glog.SetPath("/var/log"), nil)
|
||||
t.Assert(glog.GetPath(), "/var/log")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetWriter(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetWriter(os.Stdout)
|
||||
t.Assert(glog.GetWriter(), os.Stdout)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetFlags(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetFlags(glog.F_ASYNC)
|
||||
t.Assert(glog.GetFlags(), glog.F_ASYNC)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetCtxKeys(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetCtxKeys("SpanId", "TraceId")
|
||||
t.Assert(glog.GetCtxKeys(), []string{"SpanId", "TraceId"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_PrintStack(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.PrintStack(ctx, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetStack(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetStack(true)
|
||||
t.Assert(glog.GetStack(1), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetLevelStr(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(glog.SetLevelStr("all"), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
t.AssertNE(l.SetLevelStr("test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetLevelPrefix(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetLevelPrefix(glog.LEVEL_ALL, "LevelPrefix")
|
||||
t.Assert(glog.GetLevelPrefix(glog.LEVEL_ALL), "LevelPrefix")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetLevelPrefixes(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetLevelPrefixes(map[int]string{
|
||||
glog.LEVEL_ALL: "ALL_Prefix",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetHandlers(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetHandlers(func(ctx context.Context, in *glog.HandlerInput) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SetWriterColorEnable(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
glog.SetWriterColorEnable(true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Instance(t *testing.T) {
|
||||
defaultLog := glog.DefaultLogger().Clone()
|
||||
defer glog.SetDefaultLogger(defaultLog)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertNE(glog.Instance("gf"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetConfig(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
config := glog.DefaultLogger().GetConfig()
|
||||
t.Assert(config.Path, "")
|
||||
t.Assert(config.StdoutPrint, true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Write(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
len, err := l.Write([]byte("GoFrame"))
|
||||
t.AssertNil(err)
|
||||
t.Assert(len, 7)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_To(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.DefaultLogger().Clone()
|
||||
logTo := l.To(os.Stdout)
|
||||
t.AssertNE(logTo, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logTo := l.To(os.Stdout)
|
||||
t.AssertNE(logTo, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Path(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.DefaultLogger().Clone()
|
||||
logPath := l.Path("./")
|
||||
t.AssertNE(logPath, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logPath := l.Path("./")
|
||||
t.AssertNE(logPath, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Cat(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logCat := l.Cat(".gf")
|
||||
t.AssertNE(logCat, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Level(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logLevel := l.Level(glog.LEVEL_ALL)
|
||||
t.AssertNE(logLevel, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_LevelStr(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logLevelStr := l.LevelStr("all")
|
||||
t.AssertNE(logLevelStr, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Skip(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logSkip := l.Skip(1)
|
||||
t.AssertNE(logSkip, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Stack(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logStack := l.Stack(true)
|
||||
t.AssertNE(logStack, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_StackWithFilter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logStackWithFilter := l.StackWithFilter("gtest")
|
||||
t.AssertNE(logStackWithFilter, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Stdout(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logStdout := l.Stdout(true)
|
||||
t.AssertNE(logStdout, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Header(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logHeader := l.Header(true)
|
||||
t.AssertNE(logHeader, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Line(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logLine := l.Line(true)
|
||||
t.AssertNE(logLine, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Chaining_Async(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
logAsync := l.Async(true)
|
||||
t.AssertNE(logAsync, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Config_SetDebug(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
l.SetDebug(false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Config_AppendCtxKeys(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
l.AppendCtxKeys("Trace-Id", "Span-Id", "Test")
|
||||
l.AppendCtxKeys("Trace-Id-New", "Span-Id-New", "Test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Config_SetPath(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
t.AssertNE(l.SetPath(""), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Config_SetStdoutColorDisabled(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := glog.New()
|
||||
l.SetStdoutColorDisabled(false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Ctx(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
@ -42,8 +423,13 @@ func Test_Ctx_Config(t *testing.T) {
|
||||
m := map[string]interface{}{
|
||||
"CtxKeys": g.SliceStr{"Trace-Id", "Span-Id", "Test"},
|
||||
}
|
||||
var nilMap map[string]interface{}
|
||||
|
||||
err := l.SetConfigWithMap(m)
|
||||
t.AssertNil(err)
|
||||
err = l.SetConfigWithMap(nilMap)
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
|
||||
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
|
||||
|
||||
|
||||
@ -92,6 +92,15 @@ func Test_Format(t *testing.T) {
|
||||
}
|
||||
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var ti *gtime.Time = nil
|
||||
t.Assert(ti.Format("Y-m-d h:i:s"), "")
|
||||
t.Assert(ti.FormatNew("Y-m-d h:i:s"), nil)
|
||||
t.Assert(ti.FormatTo("Y-m-d h:i:s"), nil)
|
||||
t.Assert(ti.Layout("Y-m-d h:i:s"), "")
|
||||
t.Assert(ti.LayoutNew("Y-m-d h:i:s"), nil)
|
||||
t.Assert(ti.LayoutTo("Y-m-d h:i:s"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Format_ZeroString(t *testing.T) {
|
||||
|
||||
@ -18,10 +18,19 @@ import (
|
||||
func Test_SetTimeZone(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gtime.SetTimeZone("Asia/Shanghai"), nil)
|
||||
t.AssertNE(gtime.SetTimeZone("testzone"), nil)
|
||||
// t.Assert(time.Local.String(), "Asia/Shanghai")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TimestampStr(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.AssertGT(len(gtime.TimestampMilliStr()), 0)
|
||||
t.AssertGT(len(gtime.TimestampMicroStr()), 0)
|
||||
t.AssertGT(len(gtime.TimestampNanoStr()), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Nanosecond(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
nanos := gtime.TimestampNano()
|
||||
@ -69,6 +78,14 @@ func Test_Datetime(t *testing.T) {
|
||||
}
|
||||
t.Assert(datetime, timeTemp.Time.Format("2006-01-02 15:04:05"))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp, err := gtime.StrToTime("")
|
||||
t.Assert(err, nil)
|
||||
t.AssertLT(timeTemp.Unix(), 0)
|
||||
timeTemp, err = gtime.StrToTime("2006-01")
|
||||
t.AssertNE(err, nil)
|
||||
t.Assert(timeTemp, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ISO8601(t *testing.T) {
|
||||
|
||||
@ -8,6 +8,7 @@ package gtime_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -48,6 +49,27 @@ func Test_New(t *testing.T) {
|
||||
timeTemp := gtime.New("2021-2-9 08:01:21")
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
|
||||
timeTemp = gtime.New("2021-02-09 08:01:21", []byte("Y-m-d H:i:s"))
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
|
||||
timeTemp = gtime.New([]byte("2021-02-09 08:01:21"))
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
|
||||
timeTemp = gtime.New([]byte("2021-02-09 08:01:21"), "Y-m-d H:i:s")
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
|
||||
timeTemp = gtime.New([]byte("2021-02-09 08:01:21"), []byte("Y-m-d H:i:s"))
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
})
|
||||
//
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gtime.New(gtime.Time{}), nil)
|
||||
t.Assert(gtime.New(>ime.Time{}), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -124,6 +146,8 @@ func Test_NewFromTimeStamp(t *testing.T) {
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2019-04-05 18:24:06")
|
||||
timeTemp1 := gtime.NewFromTimeStamp(0)
|
||||
t.Assert(timeTemp1.Format("Y-m-d H:i:s"), "0001-01-01 00:00:00")
|
||||
timeTemp2 := gtime.NewFromTimeStamp(155445984)
|
||||
t.Assert(timeTemp2.Format("Y-m-d H:i:s"), "1974-12-05 11:26:24")
|
||||
})
|
||||
}
|
||||
|
||||
@ -134,6 +158,50 @@ func Test_Time_Second(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_IsZero(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var ti *gtime.Time = nil
|
||||
t.Assert(ti.IsZero(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_AddStr(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gt := gtime.New("2018-08-08 08:08:08")
|
||||
gt1, err := gt.AddStr("10T")
|
||||
t.Assert(gt1, nil)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_Equal(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var t1 *gtime.Time = nil
|
||||
var t2 = gtime.New()
|
||||
t.Assert(t1.Equal(t2), false)
|
||||
t.Assert(t1.Equal(t1), true)
|
||||
t.Assert(t2.Equal(t1), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_After(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var t1 *gtime.Time = nil
|
||||
var t2 = gtime.New()
|
||||
t.Assert(t1.After(t2), false)
|
||||
t.Assert(t2.After(t1), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_Sub(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var t1 *gtime.Time = nil
|
||||
var t2 = gtime.New()
|
||||
t.Assert(t1.Sub(t2), time.Duration(0))
|
||||
t.Assert(t2.Sub(t1), time.Duration(0))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Time_Nanosecond(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.Now()
|
||||
@ -468,4 +536,15 @@ func Test_DeepCopy(t *testing.T) {
|
||||
u2 := gutil.Copy(u1).(*User)
|
||||
t.Assert(u1, u2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var t1 *gtime.Time = nil
|
||||
t.Assert(t1.DeepCopy(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UnmarshalJSON(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var t1 gtime.Time
|
||||
t.AssertNE(json.Unmarshal([]byte("{}"), &t1), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -15,6 +15,13 @@ import (
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
)
|
||||
|
||||
type recursiveType string
|
||||
|
||||
const (
|
||||
recursiveTypeAuto recursiveType = "auto"
|
||||
recursiveTypeTrue recursiveType = "true"
|
||||
)
|
||||
|
||||
// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
|
||||
// map/struct/*struct type, then the conversion will fail and returns nil.
|
||||
//
|
||||
@ -22,7 +29,7 @@ import (
|
||||
// tags that will be detected, otherwise it detects the tags in order of:
|
||||
// gconv, json, field name.
|
||||
func Map(value interface{}, tags ...string) map[string]interface{} {
|
||||
return doMapConvert(value, false, tags...)
|
||||
return doMapConvert(value, recursiveTypeAuto, tags...)
|
||||
}
|
||||
|
||||
// MapDeep does Map function recursively, which means if the attribute of `value`
|
||||
@ -30,14 +37,14 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
|
||||
// a map[string]interface{} type variable.
|
||||
// Also see Map.
|
||||
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
|
||||
return doMapConvert(value, true, tags...)
|
||||
return doMapConvert(value, recursiveTypeTrue, tags...)
|
||||
}
|
||||
|
||||
// doMapConvert implements the map converting.
|
||||
// It automatically checks and converts json string to map if `value` is string/[]byte.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types, especially the map.
|
||||
func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]interface{} {
|
||||
func doMapConvert(value interface{}, recursive recursiveType, tags ...string) map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
@ -73,7 +80,15 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Tags: newTags,
|
||||
},
|
||||
)
|
||||
}
|
||||
case map[interface{}]string:
|
||||
for k, v := range r {
|
||||
@ -120,10 +135,18 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if recursive {
|
||||
if recursive == recursiveTypeTrue {
|
||||
// A copy of current map.
|
||||
for k, v := range r {
|
||||
dataMap[k] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
|
||||
dataMap[k] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Tags: newTags,
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// It returns the map directly without any changing.
|
||||
@ -131,7 +154,15 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
}
|
||||
case map[int]interface{}:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(false, v, recursive, newTags...)
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Tags: newTags,
|
||||
},
|
||||
)
|
||||
}
|
||||
case map[int]string:
|
||||
for k, v := range r {
|
||||
@ -171,7 +202,15 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
}
|
||||
}
|
||||
case reflect.Map, reflect.Struct, reflect.Interface:
|
||||
convertedValue := doMapConvertForMapOrStructValue(true, value, recursive, newTags...)
|
||||
convertedValue := doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: true,
|
||||
Value: value,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Tags: newTags,
|
||||
},
|
||||
)
|
||||
if m, ok := convertedValue.(map[string]interface{}); ok {
|
||||
return m
|
||||
}
|
||||
@ -183,16 +222,25 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string]
|
||||
return dataMap
|
||||
}
|
||||
|
||||
func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive bool, tags ...string) interface{} {
|
||||
if isRoot == false && recursive == false {
|
||||
return value
|
||||
type doMapConvertForMapOrStructValueInput struct {
|
||||
IsRoot bool // It returns directly if it is not root and with no recursive converting.
|
||||
Value interface{} // Current operation value.
|
||||
RecursiveType recursiveType // The type from top function entry.
|
||||
RecursiveOption bool // Whether convert recursively for `current` operation.
|
||||
Tags []string // Map key mapping.
|
||||
}
|
||||
|
||||
func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} {
|
||||
if in.IsRoot == false && in.RecursiveOption == false {
|
||||
return in.Value
|
||||
}
|
||||
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := value.(reflect.Value); ok {
|
||||
if v, ok := in.Value.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
value = v.Interface()
|
||||
in.Value = v.Interface()
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectValue = reflect.ValueOf(in.Value)
|
||||
}
|
||||
reflectKind := reflectValue.Kind()
|
||||
// If it is a pointer, we should find its real data type.
|
||||
@ -208,10 +256,13 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
)
|
||||
for _, k := range mapKeys {
|
||||
dataMap[String(k.Interface())] = doMapConvertForMapOrStructValue(
|
||||
false,
|
||||
reflectValue.MapIndex(k).Interface(),
|
||||
recursive,
|
||||
tags...,
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: reflectValue.MapIndex(k).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
},
|
||||
)
|
||||
}
|
||||
return dataMap
|
||||
@ -219,11 +270,19 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
case reflect.Struct:
|
||||
var dataMap = make(map[string]interface{})
|
||||
// Map converting interface check.
|
||||
if v, ok := value.(iMapStrAny); ok {
|
||||
if v, ok := in.Value.(iMapStrAny); ok {
|
||||
// Value copy, in case of concurrent safety.
|
||||
for mapK, mapV := range v.MapStrAny() {
|
||||
if recursive {
|
||||
dataMap[mapK] = doMapConvertForMapOrStructValue(false, mapV, recursive, tags...)
|
||||
if in.RecursiveOption {
|
||||
dataMap[mapK] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapV,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
dataMap[mapK] = mapV
|
||||
}
|
||||
@ -247,7 +306,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
}
|
||||
mapKey = ""
|
||||
fieldTag := rtField.Tag
|
||||
for _, tag := range tags {
|
||||
for _, tag := range in.Tags {
|
||||
if mapKey = fieldTag.Get(tag); mapKey != "" {
|
||||
break
|
||||
}
|
||||
@ -274,7 +333,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
}
|
||||
}
|
||||
}
|
||||
if recursive || rtField.Anonymous {
|
||||
if in.RecursiveOption || rtField.Anonymous {
|
||||
// Do map converting recursively.
|
||||
var (
|
||||
rvAttrField = rvField
|
||||
@ -292,25 +351,48 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
continue
|
||||
}
|
||||
var (
|
||||
hasNoTag = mapKey == fieldName
|
||||
rvAttrInterface = rvAttrField.Interface()
|
||||
hasNoTag = mapKey == fieldName
|
||||
// DO NOT use rvAttrField.Interface() here,
|
||||
// as it might be changed from pointer to struct.
|
||||
rvInterface = rvField.Interface()
|
||||
)
|
||||
if hasNoTag && rtField.Anonymous {
|
||||
switch {
|
||||
case hasNoTag && rtField.Anonymous:
|
||||
// It means this attribute field has no tag.
|
||||
// Overwrite the attribute with sub-struct attribute fields.
|
||||
anonymousValue := doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
|
||||
anonymousValue := doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Tags: in.Tags,
|
||||
})
|
||||
if m, ok := anonymousValue.(map[string]interface{}); ok {
|
||||
for k, v := range m {
|
||||
dataMap[k] = v
|
||||
}
|
||||
} else {
|
||||
dataMap[mapKey] = rvAttrInterface
|
||||
dataMap[mapKey] = rvInterface
|
||||
}
|
||||
} else if !hasNoTag && rtField.Anonymous {
|
||||
// It means this attribute field has desired tag.
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
|
||||
} else {
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, recursive, tags...)
|
||||
|
||||
// It means this attribute field has desired tag.
|
||||
case !hasNoTag && rtField.Anonymous:
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Tags: in.Tags,
|
||||
})
|
||||
|
||||
default:
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
})
|
||||
}
|
||||
|
||||
// The struct attribute is type of slice.
|
||||
@ -323,7 +405,13 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
array := make([]interface{}, length)
|
||||
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
|
||||
array[arrayIndex] = doMapConvertForMapOrStructValue(
|
||||
false, rvAttrField.Index(arrayIndex), recursive, tags...,
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvAttrField.Index(arrayIndex),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
},
|
||||
)
|
||||
}
|
||||
dataMap[mapKey] = array
|
||||
@ -334,10 +422,13 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
)
|
||||
for _, k := range mapKeys {
|
||||
nestedMap[String(k.Interface())] = doMapConvertForMapOrStructValue(
|
||||
false,
|
||||
rvAttrField.MapIndex(k).Interface(),
|
||||
recursive,
|
||||
tags...,
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvAttrField.MapIndex(k).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
},
|
||||
)
|
||||
}
|
||||
dataMap[mapKey] = nestedMap
|
||||
@ -358,7 +449,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
}
|
||||
}
|
||||
if len(dataMap) == 0 {
|
||||
return value
|
||||
return in.Value
|
||||
}
|
||||
return dataMap
|
||||
|
||||
@ -370,11 +461,17 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
}
|
||||
array := make([]interface{}, reflectValue.Len())
|
||||
for i := 0; i < length; i++ {
|
||||
array[i] = doMapConvertForMapOrStructValue(false, reflectValue.Index(i), recursive, tags...)
|
||||
array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: reflectValue.Index(i),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Tags: in.Tags,
|
||||
})
|
||||
}
|
||||
return array
|
||||
}
|
||||
return value
|
||||
return in.Value
|
||||
}
|
||||
|
||||
// MapStrStr converts `value` to map[string]string.
|
||||
|
||||
@ -145,9 +145,29 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
|
||||
// If `params` and `pointer` are the same type, the do directly assignment.
|
||||
// For performance enhancement purpose.
|
||||
if pointerElemReflectValue.IsValid() && pointerElemReflectValue.Type() == paramsReflectValue.Type() {
|
||||
pointerElemReflectValue.Set(paramsReflectValue)
|
||||
return nil
|
||||
if pointerElemReflectValue.IsValid() {
|
||||
switch {
|
||||
// Eg:
|
||||
// UploadFile => UploadFile
|
||||
// *UploadFile => *UploadFile
|
||||
case pointerElemReflectValue.Type() == paramsReflectValue.Type():
|
||||
pointerElemReflectValue.Set(paramsReflectValue)
|
||||
return nil
|
||||
|
||||
// Eg:
|
||||
// UploadFile => *UploadFile
|
||||
case pointerElemReflectValue.Kind() == reflect.Ptr && pointerElemReflectValue.Elem().IsValid() &&
|
||||
pointerElemReflectValue.Elem().Type() == paramsReflectValue.Type():
|
||||
pointerElemReflectValue.Elem().Set(paramsReflectValue)
|
||||
return nil
|
||||
|
||||
// Eg:
|
||||
// *UploadFile => UploadFile
|
||||
case paramsReflectValue.Kind() == reflect.Ptr && paramsReflectValue.Elem().IsValid() &&
|
||||
pointerElemReflectValue.Type() == paramsReflectValue.Elem().Type():
|
||||
pointerElemReflectValue.Set(paramsReflectValue.Elem())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Normal unmarshalling interfaces checks.
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -41,103 +41,6 @@ func (s1 S1) Error() string {
|
||||
return "22222"
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1227
|
||||
func Test_Issue1227(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StructFromIssue1227 struct {
|
||||
Name string `json:"n1"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
origin interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Case1",
|
||||
origin: `{"n1":"n1"}`,
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case2",
|
||||
origin: `{"name":"name"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case3",
|
||||
origin: `{"NaMe":"NaMe"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case4",
|
||||
origin: g.Map{"n1": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"NaMe": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
p := StructFromIssue1227{}
|
||||
if err := gconv.Struct(tt.origin, &p); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(p.Name, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
// Chinese key.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StructFromIssue1227 struct {
|
||||
Name string `json:"中文Key"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
origin interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Case1",
|
||||
origin: `{"中文Key":"n1"}`,
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case2",
|
||||
origin: `{"Key":"name"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case3",
|
||||
origin: `{"NaMe":"NaMe"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case4",
|
||||
origin: g.Map{"中文Key": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"中文KEY": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"KEY": "n1"},
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
p := StructFromIssue1227{}
|
||||
if err := gconv.Struct(tt.origin, &p); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(p.Name, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Bool_All(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var any interface{} = nil
|
||||
@ -1545,76 +1448,3 @@ func Test_Struct_Time_All(t *testing.T) {
|
||||
t.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Issue1946(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
init: gtype.NewBool(true),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.init.Val(), true)
|
||||
})
|
||||
// It cannot change private attribute.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
init: gtype.NewBool(true),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"init": 0,
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.init.Val(), true)
|
||||
})
|
||||
// It can change public attribute.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
Init: gtype.NewBool(),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"Init": 1,
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.Init.Val(), true)
|
||||
})
|
||||
}
|
||||
|
||||
225
util/gconv/gconv_z_unit_issue_test.go
Normal file
225
util/gconv/gconv_z_unit_issue_test.go
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// https://github.com/gogf/gf/issues/1227
|
||||
func Test_Issue1227(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StructFromIssue1227 struct {
|
||||
Name string `json:"n1"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
origin interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Case1",
|
||||
origin: `{"n1":"n1"}`,
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case2",
|
||||
origin: `{"name":"name"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case3",
|
||||
origin: `{"NaMe":"NaMe"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case4",
|
||||
origin: g.Map{"n1": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"NaMe": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
p := StructFromIssue1227{}
|
||||
if err := gconv.Struct(tt.origin, &p); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(p.Name, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
// Chinese key.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type StructFromIssue1227 struct {
|
||||
Name string `json:"中文Key"`
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
origin interface{}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Case1",
|
||||
origin: `{"中文Key":"n1"}`,
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case2",
|
||||
origin: `{"Key":"name"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case3",
|
||||
origin: `{"NaMe":"NaMe"}`,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Case4",
|
||||
origin: g.Map{"中文Key": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"中文KEY": "n1"},
|
||||
want: "n1",
|
||||
},
|
||||
{
|
||||
name: "Case5",
|
||||
origin: g.Map{"KEY": "n1"},
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
p := StructFromIssue1227{}
|
||||
if err := gconv.Struct(tt.origin, &p); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(p.Name, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Issue1946(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
init: gtype.NewBool(true),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.init.Val(), true)
|
||||
})
|
||||
// It cannot change private attribute.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
init: gtype.NewBool(true),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"init": 0,
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.init.Val(), true)
|
||||
})
|
||||
// It can change public attribute.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Init *gtype.Bool
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B *B
|
||||
}
|
||||
a := &A{
|
||||
B: &B{
|
||||
Init: gtype.NewBool(),
|
||||
},
|
||||
}
|
||||
err := gconv.Struct(g.Map{
|
||||
"B": g.Map{
|
||||
"Init": 1,
|
||||
"Name": "init",
|
||||
},
|
||||
}, a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a.B.Name, "init")
|
||||
t.Assert(a.B.Init.Val(), true)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/2381
|
||||
func Test_Issue2381(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Inherit struct {
|
||||
Id int64 `json:"id" description:"Id"`
|
||||
Flag *gjson.Json `json:"flag" description:"标签"`
|
||||
Title string `json:"title" description:"标题"`
|
||||
CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"`
|
||||
}
|
||||
type Test1 struct {
|
||||
Inherit
|
||||
}
|
||||
type Test2 struct {
|
||||
Inherit
|
||||
}
|
||||
var (
|
||||
a1 Test1
|
||||
a2 Test2
|
||||
)
|
||||
|
||||
a1 = Test1{
|
||||
Inherit{
|
||||
Id: 2,
|
||||
Flag: gjson.New("[1, 2]"),
|
||||
Title: "测试",
|
||||
CreatedAt: gtime.Now(),
|
||||
},
|
||||
}
|
||||
err := gconv.Scan(a1, &a2)
|
||||
t.AssertNil(err)
|
||||
t.Assert(a1.Id, a2.Id)
|
||||
t.Assert(a1.Title, a2.Title)
|
||||
t.Assert(a1.CreatedAt, a2.CreatedAt)
|
||||
t.Assert(a1.Flag.String(), a2.Flag.String())
|
||||
})
|
||||
}
|
||||
@ -592,10 +592,10 @@ func TestMapsDeep(t *testing.T) {
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
string_interface_map_list := []map[string]interface{}{}
|
||||
string_interface_map_list = append(string_interface_map_list, map[string]interface{}{"id": 100})
|
||||
string_interface_map_list = append(string_interface_map_list, map[string]interface{}{"id": 200})
|
||||
list := gconv.MapsDeep(string_interface_map_list)
|
||||
stringInterfaceMapList := make([]map[string]interface{}, 0)
|
||||
stringInterfaceMapList = append(stringInterfaceMapList, map[string]interface{}{"id": 100})
|
||||
stringInterfaceMapList = append(stringInterfaceMapList, map[string]interface{}{"id": 200})
|
||||
list := gconv.MapsDeep(stringInterfaceMapList)
|
||||
t.Assert(len(list), 2)
|
||||
t.Assert(list[0]["id"], 100)
|
||||
t.Assert(list[1]["id"], 200)
|
||||
|
||||
@ -44,4 +44,5 @@ const (
|
||||
GConv = "gconv" // GConv defines the converting target name for specified struct field.
|
||||
GConvShort = "c" // GConv defines the converting target name for specified struct field.
|
||||
Json = "json" // Json tag is supported by stdlib.
|
||||
Security = "security" // Security defines scheme for authentication. Detail to see https://swagger.io/docs/specification/authentication/
|
||||
)
|
||||
|
||||
@ -81,11 +81,16 @@ func DumpTo(writer io.Writer, value interface{}, option DumpOption) {
|
||||
}
|
||||
|
||||
type doDumpOption struct {
|
||||
WithType bool
|
||||
ExportedOnly bool
|
||||
WithType bool
|
||||
ExportedOnly bool
|
||||
DumpedPointerSet map[string]struct{}
|
||||
}
|
||||
|
||||
func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDumpOption) {
|
||||
if option.DumpedPointerSet == nil {
|
||||
option.DumpedPointerSet = map[string]struct{}{}
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
buffer.WriteString(`<nil>`)
|
||||
return
|
||||
@ -111,26 +116,29 @@ func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDum
|
||||
var (
|
||||
reflectKind = reflectValue.Kind()
|
||||
reflectTypeName = reflectValue.Type().String()
|
||||
ptrAddress string
|
||||
newIndent = indent + dumpIndent
|
||||
)
|
||||
reflectTypeName = strings.ReplaceAll(reflectTypeName, `[]uint8`, `[]byte`)
|
||||
if !option.WithType {
|
||||
reflectTypeName = ""
|
||||
}
|
||||
for reflectKind == reflect.Ptr {
|
||||
if ptrAddress == "" {
|
||||
ptrAddress = fmt.Sprintf(`0x%x`, reflectValue.Pointer())
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
var (
|
||||
exportInternalInput = doDumpInternalInput{
|
||||
Value: value,
|
||||
Indent: indent,
|
||||
NewIndent: newIndent,
|
||||
Buffer: buffer,
|
||||
Option: option,
|
||||
ReflectValue: reflectValue,
|
||||
ReflectTypeName: reflectTypeName,
|
||||
ExportedOnly: option.ExportedOnly,
|
||||
Value: value,
|
||||
Indent: indent,
|
||||
NewIndent: newIndent,
|
||||
Buffer: buffer,
|
||||
Option: option,
|
||||
PtrAddress: ptrAddress,
|
||||
ReflectValue: reflectValue,
|
||||
ReflectTypeName: reflectTypeName,
|
||||
ExportedOnly: option.ExportedOnly,
|
||||
DumpedPointerSet: option.DumpedPointerSet,
|
||||
}
|
||||
)
|
||||
switch reflectKind {
|
||||
@ -185,14 +193,16 @@ func doDump(value interface{}, indent string, buffer *bytes.Buffer, option doDum
|
||||
}
|
||||
|
||||
type doDumpInternalInput struct {
|
||||
Value interface{}
|
||||
Indent string
|
||||
NewIndent string
|
||||
Buffer *bytes.Buffer
|
||||
Option doDumpOption
|
||||
ReflectValue reflect.Value
|
||||
ReflectTypeName string
|
||||
ExportedOnly bool
|
||||
Value interface{}
|
||||
Indent string
|
||||
NewIndent string
|
||||
Buffer *bytes.Buffer
|
||||
Option doDumpOption
|
||||
ReflectValue reflect.Value
|
||||
ReflectTypeName string
|
||||
PtrAddress string
|
||||
ExportedOnly bool
|
||||
DumpedPointerSet map[string]struct{}
|
||||
}
|
||||
|
||||
func doDumpSlice(in doDumpInternalInput) {
|
||||
@ -295,6 +305,14 @@ func doDumpMap(in doDumpInternalInput) {
|
||||
}
|
||||
|
||||
func doDumpStruct(in doDumpInternalInput) {
|
||||
if in.PtrAddress != "" {
|
||||
if _, ok := in.DumpedPointerSet[in.PtrAddress]; ok {
|
||||
in.Buffer.WriteString(fmt.Sprintf(`<cycle dump %s>`, in.PtrAddress))
|
||||
return
|
||||
}
|
||||
}
|
||||
in.DumpedPointerSet[in.PtrAddress] = struct{}{}
|
||||
|
||||
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
|
||||
Pointer: in.Value,
|
||||
RecursiveOption: gstructs.RecursiveOptionEmbedded,
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
@ -273,3 +274,17 @@ func Test_Dump_Issue1661(t *testing.T) {
|
||||
]`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Dump_Cycle_Attribute(t *testing.T) {
|
||||
type Abc struct {
|
||||
ab int
|
||||
cd *Abc
|
||||
}
|
||||
abc := Abc{ab: 3}
|
||||
abc.cd = &abc
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
g.DumpTo(buffer, abc, gutil.DumpOption{})
|
||||
t.Assert(gstr.Contains(buffer.String(), "cycle"), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -2,5 +2,5 @@ package gf
|
||||
|
||||
const (
|
||||
// VERSION is the current GoFrame version.
|
||||
VERSION = "v2.2.4"
|
||||
VERSION = "v2.2.6"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user