Compare commits

...

31 Commits

Author SHA1 Message Date
5a8b33fa09 fix gf.yaml (#2385)
* fix gf.yaml

* up
2023-01-03 14:33:41 +08:00
5884a0e05f fix issue #2381 (#2382)
* fix issue #2381

* up

* up
2023-01-03 11:00:23 +08:00
31e44062a8 revert from int64 to int for returning value of Count (#2378)
* revert from int64 to int for returning value of Count

* up

* up

* up
2022-12-30 16:54:43 +08:00
87cb1c9b8e add security tag support for openapi (#2377)
* support openapi path security

* add security path test case

* go format

* fix test case

* add doc for security
2022-12-29 20:56:20 +08:00
0266d24d0a fix Unknown setting charset for clickhouse driver (#2375) 2022-12-27 14:46:15 +08:00
0876e00eb8 fix issue in NewIntArrayRange function for package garray (#2374) 2022-12-26 19:28:01 +08:00
85c4794ceb fix BuildParams with urlEncode when len(v) <= 6 (#2308)
* fix: check urlEncode when len(v) <= 6

* fix BuildParams with urlEncode when len(v) <= 6

* fix BuildParams with urlEncode when len(v) <= 6

Co-authored-by: Prime Xiao <primexiao.dev@gmail.com>
2022-12-23 10:33:28 +08:00
e007bf35b2 parseConfigNodeLink support Chinese database name #2231 (#2238) 2022-12-22 17:33:51 +08:00
74e968e93b fix: ghttp server static path config (#2335) 2022-12-22 17:21:33 +08:00
18507fb836 由于 clickhouse 的 position的初始值为 1,导致gdb_core_utility.HasField 中对 fieldsArray 初始化出错 (#2346)
* 由于 clickhouse 的 position的初始值为 1,导致gdb_core_utility.HasField 中对 fieldsArray 初始化出错

* 修复单元测试

* 修复单元测试

* 补充单元测试

* 增加CK防御性代码

Co-authored-by: longl <longlei@dealmap.cloud>
Co-authored-by: houseme <housemecn@gmail.com>
2022-12-22 17:00:08 +08:00
3b245837b9 fix issue in cycle dumping for g.Dump (#2367)
* fix issue in cycle dumping for g.Dump

* up

* up

* up

Co-authored-by: houseme <housemecn@gmail.com>
2022-12-22 14:43:02 +08:00
a853984f52 fix issue #2334 when accessing static files with cache time (#2366)
* Solve the problem of error when accessing static files with cache time.
Error message:
2022-11-29 19:40:11.090 [ERRO] http: superfluous response.WriteHeader call from github.com/gogf/gf/v2/net/ghttp.(*ResponseWriter).Flush (ghttp_response_writer.go:58)
Stack:

Verification method:
curl 'http://127.0.0.1:8000/' -H 'If-Modified-Since: Thu, 08 Dec 2022 03:13:55 GMT' --compressed

* Solve the problem of error when accessing static files with cache time.
Error message:
2022-11-29 19:40:11.090 [ERRO] http: superfluous response.WriteHeader call from github.com/gogf/gf/v2/net/ghttp.(*ResponseWriter).Flush (ghttp_response_writer.go:58)
Stack:

Verification method:
curl 'http://127.0.0.1:8000/' -H 'If-Modified-Since: Thu, 08 Dec 2022 03:13:55 GMT' --compressed

* Solve the problem of error when accessing static files with cache time.
Error message:
2022-11-29 19:40:11.090 [ERRO] http: superfluous response.WriteHeader call from github.com/gogf/gf/v2/net/ghttp.(*ResponseWriter).Flush (ghttp_response_writer.go:58)
Stack:

Verification method:
curl 'http://127.0.0.1:8000/' -H 'If-Modified-Since: Thu, 08 Dec 2022 03:13:55 GMT' --compressed

* fix issue #2334 when accessing static files with cache time

* up

Co-authored-by: 曾洪亮 <hongliang.zeng@i-soft.com.cn>
Co-authored-by: houseme <housemecn@gmail.com>
2022-12-22 10:25:30 +08:00
00c544ee99 fix issue when only one file was uploaded in batch receiver attribute (#2365)
* fix fixed An error occurred when only one file was uploaded in batches and add unit testing(#2092)

* fix issue uploading files for ghttp.Server

Co-authored-by: yxh <yxh1103@qq.com>
2022-12-21 10:38:19 +08:00
e7b9e41a5e improve ut case for package internal/rwmutex (#2364) 2022-12-20 15:56:29 +08:00
e254b4f3c0 add ut cases for package gcache (#2341)
* gTcp Example Function:
1.NewConn 2.NewConnTLS 3.NewConnKeyCrt

* gTcp Example Function:
1.Send

* add example function ExampleConn_Recv and ExampleConn_RecvWithTimeout

* add example function
1. ExampleConn_SendWithTimeout
2. ExampleConn_RecvLine
3. ExampleConn_RecvTill

* add example function
1. ExampleConn_SendRecv
2. ExampleConn_SendRecvWithTimeout
3. ExampleConn_SetDeadline
4. ExampleConn_SetReceiveBufferWait

* add gtcp test function
1. Test_Package_Option_HeadSize4
2. Test_Package_Option_Error

* add gtcp example function
1. ExampleGetFreePorts
2. ExampleSend
3. ExampleSendRecv
4. ExampleSendWithTimeout
5. ExampleSendRecvWithTimeout
6. ExampleMustGetFreePort

* add gtcp example function
1. ExampleSendPkg
2. ExampleSendRecvPkg
3. ExampleSendPkgWithTimeout
4. ExampleSendRecvPkgWithTimeout

* add gtcp test function
1. Test_Pool_Send
2. Test_Pool_Recv
3. Test_Pool_RecvLine
4. Test_Pool_RecvTill
5. Test_Pool_RecvWithTimeout
6. Test_Pool_SendWithTimeout
7. Test_Pool_SendRecvWithTimeout

* fix

* add gtcp example function
1. ExampleGetServer
2. ExampleSetAddress
3. ExampleSetHandler
4. ExampleRun_NilHandle

* exec CI

* exec CI

* exec CI

* modify test server address

* modify and exec CI

* modify and exec CI

* modify and exec CI

* modify and exec CI

* modify and exec CI

* modify and exec CI

* add example funcion ExampleConn_Recv_Once and fix

* fix

* add some error case in example function

* add some error case in example function

* 1.add example function ExampleNewServerKeyCrt
2.add function SendRecvPkgWithTimeout unit test

* add function Test_Server_NewServerKeyCrt unit test

* revert

* add function Test_Package_Timeout, Test_Package_Option_HeadSize3, Test_Conn_RecvPkgError unit test

* fix

* add example function
1.ExampleClient_Clone
2.ExampleLoadKeyCrt

* add example function
1.ExampleNewNetConnKeyCrt

* fix

* add example function
1.ExampleClient_DeleteBytes
2.ExampleClient_HeadBytes
3.ExampleClient_PatchBytes
4.ExampleClient_ConnectBytes
5.ExampleClient_OptionsBytes
6.ExampleClient_TraceBytes
7.ExampleClient_PutBytes

* add example function
1.ExampleClient_Prefix
2.ExampleClient_Retry
3.ExampleClient_RedirectLimit

* add example function
1.ExampleClient_SetBrowserMode
2.ExampleClient_SetHeader
3.ExampleClient_SetRedirectLimit

* add example function
1.ExampleClient_SetTLSKeyCrt
2.ExampleClient_SetTLSConfig
modify example funcion
1.ExampleClient_SetProxy
2.ExampleClient_Proxy

* add example function
1.ExampleClient_PutContent
2.ExampleClient_DeleteContent
3.ExampleClient_HeadContent
4.ExampleClient_PatchContent
5.ExampleClient_ConnectContent
6.ExampleClient_OptionsContent
7.ExampleClient_TraceContent
8.ExampleClient_RequestContent

* add example function
1.ExampleClient_RawRequest

* add unit function
1.TestGetFreePorts
2.TestNewConn
3.TestNewConnTLS
4.TestNewConnKeyCrt
5.TestConn_SendWithTimeout

* add unit function
1.TestConn_Send
2.TestConn_SendRecv
3.TestConn_SendRecvWithTimeout

* modify

* modify

* add example function
1.TestConn_SetReceiveBufferWait
2.TestNewNetConnKeyCrt
3.TestSend

* add example function
1.TestSendRecv
2.TestSendWithTimeout

* add unit function
1.TestMustGetFreePort
2.TestSendRecvWithTimeout
3.TestSendPkg

* add client recevied server's response content assert

* modify

* modify

* add example function
1.TestSendRecvPkg
2.TestSendPkgWithTimeout
3.TestSendRecvPkgWithTimeout

* add GetAddress() function
add unit funciton
1.TestNewServer
2.TestGetServer
3.TestServer_SetAddress
4.TestServer_SetHandler
5.TestServer_Run

* modify

* modify

* add unit funciton
1.TestLoadKeyCrt

* modify

* delete function fromHex

* add gclient dump unit test

* add example function
1.ExampleClient_Put
2.ExampleClient_Delete
3.ExampleClient_Head
4.ExampleClient_Patch
5.ExampleClient_Connect
6.ExampleClient_Options
7.ExampleClient_Trace

* add example function
1.TestClient_DoRequest

* add example function
1.ExampleClient_PutVar
2.ExampleClient_DeleteVar
3.ExampleClient_HeadVar
4.ExampleClient_PatchVar
5.ExampleClient_ConnectVar
6.ExampleClient_OptionsVar
7.ExampleClient_TraceVar

* modify

* modify

* add CustomProvider function

* modify

* add unit funciton
1.Test_NewConn
2.Test_GetFreePorts

* add unit funciton
1.Test_Server

* garray_normal_any code converage

* garray_normal_int code converage

* garray_normal_str code converage

* garray_sorted_any code converage

* garray_sorted_int code converage

* garray_sorted_str code converage

* glist code converage

* gmap, gmap_hash_any_any_map code converage

* gmap_hash_int_any_map code converage

* gmap_hash_int_any_map code converage

* gmap_hash_int_int_map code converage

* gmap_hash_int_str_map code converage

* gmap_hash_str_any_map code converage

* gmap_hash_str_int_map code converage

* gmap_hash_str_str_map code converage

* gmap_list_map code converage

* gmap_list_map code converage

* revert gf.yml

* add gtest unit test function

* add ut cases for package gcache

* add ut cases for package gcache

* add ut cases for package gcache

* add ut cases for package gcache

* add ut cases for package gcache

* modify

Co-authored-by: John Guo <john@johng.cn>
2022-12-20 14:49:31 +08:00
b0c9c68c9c add ut cases for package ghttp_request (#2351)
* add ut cases for package ghttp_middleware

* add ut cases for package ghttp_request

* add ut cases for package ghttp_request

* add ut cases for package ghttp_request

* add ut cases for package ghttp_request - form

* add ut cases for package ghttp_request - query

* add ut cases for package ghttp_request - request

* add ut cases for package ghttp_request - router
2022-12-12 10:28:58 +08:00
1030434ce6 add ut cases for package ghttp_response (#2352)
* add ut cases for package ghttp_response

* add ut cases for package ghttp_response

* add ut cases for package ghttp_response
2022-12-12 10:28:35 +08:00
2f08c4b00f add ut cases for package ghttp_middleware and ghttp_request (#2344)
* add ut cases for package ghttp_middleware

* add ut cases for package ghttp_request

* add ut cases for package ghttp_request
2022-12-07 20:02:46 +08:00
4553f90a83 improve lru clearing for package gcache (#2327) 2022-11-25 10:45:56 +08:00
ef7fec7e24 fix issue in failed installing when there's shortcut between file paths for command install (#2326)
* fix issue in failed installing when has shortcut between file paths for command install

* version updates

* template for command gf updates
2022-11-25 10:34:00 +08:00
0a76b9c61b fix invalid UpdatedAt usage in soft deleting feature for package gdb (#2323) 2022-11-24 21:23:15 +08:00
fbeb8f81ac add ut cases for package glog part2 (#2317) 2022-11-21 10:32:08 +08:00
62af4f1c2c add zookeeper registry support (#2284) 2022-11-18 14:07:17 +08:00
ed43efe4fb add ut cases for package gdebug (#2313)
* add ut cases for package gdebug

* add ut cases for package gdebug

* add ut cases for package gdebug

Co-authored-by: houseme <housemecn@gmail.com>
2022-11-18 14:05:39 +08:00
1cb42c32e3 add ut cases for package g (#2315) 2022-11-18 14:05:16 +08:00
628b454ebc feat: cmd gf prebuild suport oracle (#2312) 2022-11-17 21:07:12 +08:00
38a858d7d3 change result data type of function Count from int to int64 for package gdb (#2298)
* feat: modify model count value int64

* fix

* fix:modify int64

* fix
2022-11-17 19:47:17 +08:00
83b92ddfa4 add ut cases for package glog (#2302)
* add ut cases for package glog

* add ut cases for package glog

* add ut cases for package glog

* add ut cases for package glog

* add ut cases for package glog

* add ut cases for package glog
2022-11-17 19:44:48 +08:00
7cd415b1df add ut cases for package gtime (#2303)
* add ut cases for package gtime

* add ut cases for package gtime

* add ut cases for package gtime
2022-11-17 19:44:20 +08:00
d2113b4d23 add ut cases for package gerror (#2304)
* add ut cases for package gerror

* add ut cases for package gerror
2022-11-17 19:43:54 +08:00
d445987f95 add ut cases for package gcode (#2307) 2022-11-17 19:43:04 +08:00
87 changed files with 3986 additions and 575 deletions

View File

@ -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

View File

@ -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
View File

@ -17,4 +17,5 @@ cbuild
cmd/gf/main
cmd/gf/gf
go.work
go.work.sum
temp/

View File

@ -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 => ../../

View File

@ -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=

View File

@ -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

View File

@ -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
}

View File

@ -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...)

View File

@ -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...)

View File

@ -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...)

View File

@ -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...)

View File

@ -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() {

View File

@ -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]`)
})
}

View File

@ -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()
}

View File

@ -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(),

View File

@ -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)

View File

@ -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)
}

View File

@ -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))
})
}

View File

@ -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))
})
}

View File

@ -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))
})
}

View File

@ -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")
})
}

View File

@ -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))
})
}

View File

@ -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))
})
}

View File

@ -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)

View File

@ -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))
})
}

View File

@ -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))
})
}

View File

@ -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))
})
}

View 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.

View 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 => ../../../

View 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=

View 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,
}
}

View 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)
}

View 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)
}

View 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
}
}
}

View 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
}
}
}
}

View 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()
}
}

View File

@ -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&...&paramN=valueN]
linkPattern = `(\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([\w\-]*)\?{0,1}(.*)`
linkPattern = `(\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([^\?]*)\?{0,1}(.*)`
)
const (

View File

@ -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.

View File

@ -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)

View 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)
})
}

View File

@ -208,5 +208,8 @@ func (j *Json) Dump() {
}
j.mu.RLock()
defer j.mu.RUnlock()
if j.p == nil {
return
}
gutil.Dump(*j.p)
}

View File

@ -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()
}

View File

@ -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)

View File

@ -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")
})
}

View File

@ -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
View 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)
})
}

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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()))

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View 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)
})
}

View File

@ -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()
})
}

View File

@ -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")
})
}

View File

@ -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))
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View 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>")
})
}

View File

@ -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")

View File

@ -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)
})
}

View File

@ -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)
}
})
}

View File

@ -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.
// =================================================================================================================

View File

@ -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)
})
}

View File

@ -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.

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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) {

View File

@ -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)

View File

@ -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

View File

@ -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) {

View File

@ -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")

View File

@ -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) {

View File

@ -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) {

View File

@ -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(&gtime.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)
})
}

View File

@ -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.

View File

@ -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.

View File

@ -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)
})
}

View 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())
})
}

View File

@ -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)

View File

@ -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/
)

View File

@ -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,

View File

@ -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)
})
}

View File

@ -2,5 +2,5 @@ package gf
const (
// VERSION is the current GoFrame version.
VERSION = "v2.2.4"
VERSION = "v2.2.6"
)