Compare commits

...

158 Commits

Author SHA1 Message Date
dependabot[bot]
b245159417 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.8 to 2.0.9 (#3423)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-14 19:57:36 +08:00
dependabot[bot]
c26ea17669 chore(deps): bump github.com/iancoleman/strcase from 0.2.0 to 0.3.0 in /tools/goctl (#3424) 2023-07-14 13:20:06 +08:00
Kevin Wan
a7daff3587 chore: make servicegroup panic as demand (#3422) 2023-07-13 14:08:35 +00:00
dependabot[bot]
6719d06146 chore(deps): bump github.com/jackc/pgx/v5 from 5.4.1 to 5.4.2 (#3417)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 10:56:18 +08:00
Kevin Wan
0c6eaeda9f chore: coding style (#3413) 2023-07-12 01:08:09 +08:00
Xinyan Lu
b9c0c0f8b5 feat: add detail type mismatch info in number fields check (#3386) (#3387) 2023-07-11 16:29:42 +00:00
Kevin Wan
77da459165 chore: make test stable (#3412) 2023-07-11 16:20:41 +00:00
Kevin Wan
13cdbdc98b chore: avoid nested WithCodeResponseWriter (#3406) 2023-07-11 15:59:43 +00:00
guangwu
e8c1e6e09b fix: log format error (#3409) 2023-07-11 05:28:53 +00:00
guangwu
f1171e01f2 chore: slice replace loop (#3410) 2023-07-11 05:27:46 +00:00
cong
61e562d0c7 refactor(rest): keep rest log collector context key private (#3407) 2023-07-10 01:52:26 +00:00
chen quan
b71453985c feat(sqlx): support for custom Acceptable function (#3405) 2023-07-10 01:16:45 +00:00
Kevin Wan
31b9ba19a2 chore: refactor httpx.TimeoutHandler (#3400) 2023-07-09 07:04:59 +00:00
dependabot[bot]
3170afd57b chore(deps): bump google.golang.org/grpc from 1.56.1 to 1.56.2 in /tools/goctl (#3403)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-08 16:21:50 +08:00
dependabot[bot]
03e365a5d8 chore(deps): bump google.golang.org/grpc from 1.56.1 to 1.56.2 (#3402)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-08 15:34:45 +08:00
dependabot[bot]
7d4fce9588 chore(deps): bump golang.org/x/net from 0.11.0 to 0.12.0 (#3401)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 14:54:13 +08:00
扶桑花间
916cea858f 1. Fix w. (http. Flusher). Flush() error (#3388) 2023-07-05 15:27:15 +00:00
dependabot[bot]
a86942d532 chore(deps): bump golang.org/x/sys from 0.9.0 to 0.10.0 (#3396)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-05 22:49:07 +08:00
dependabot[bot]
f76c70ea9a chore(deps): bump golang.org/x/text from 0.10.0 to 0.11.0 in /tools/goctl (#3397)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-05 22:19:04 +08:00
MarkJoyMa
4cbfdb3d74 feat: optimize must log add stack (#3384) 2023-06-30 01:11:03 +00:00
dependabot[bot]
aefa6dfb50 chore(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#3376) 2023-06-30 09:06:20 +08:00
dependabot[bot]
9047029475 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.3 to 2.30.4 (#3381) 2023-06-30 09:05:05 +08:00
dependabot[bot]
f296c182f7 chore(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 in /tools/goctl (#3377)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 22:45:00 +08:00
Kevin Wan
40e7a4cd07 chore: refactor httpx.SetOkHandler (#3373) 2023-06-26 00:27:26 +08:00
Kevin Wan
92e5819e91 opt: improve logx performance (#3371) 2023-06-25 15:41:28 +08:00
2822132073
8d23ab158b fix In goctl new api, occur error invalid character 'A' looking for beginning of value (#3357) 2023-06-25 07:26:21 +00:00
唐小鸭
bcccfab824 [fix] The directory is not recognized when it is in a soft link (#3337) 2023-06-25 05:06:27 +00:00
dependabot[bot]
f7e701a634 chore(deps): bump google.golang.org/grpc from 1.56.0 to 1.56.1 (#3367) 2023-06-25 13:04:13 +08:00
dependabot[bot]
7c2d8e5cc2 chore(deps): bump google.golang.org/grpc from 1.56.0 to 1.56.1 in /tools/goctl (#3369) 2023-06-25 12:57:03 +08:00
dependabot[bot]
5b622d6265 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.7 to 1.12.0 (#3370)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-23 14:50:48 +08:00
dependabot[bot]
c5510a4e1b chore(deps): bump github.com/jackc/pgx/v5 from 5.4.0 to 5.4.1 (#3363)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 15:16:06 +08:00
Kevin Wan
2a33b74b35 chore: coding style (#3362) 2023-06-17 22:59:00 +08:00
anqiansong
45bb547a81 (goctl)fix: #3328 (#3348)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-06-17 13:23:57 +00:00
Mikael
f5f5261556 add whether to generate rpc client option (#3361)
Co-authored-by: admin <admin@admindeMacBook-Pro.local>
2023-06-17 12:52:49 +00:00
Kevin Wan
b176d5d434 chore: add more tests (#3359) 2023-06-17 20:51:33 +08:00
MarkJoyMa
92f6c48349 fix: NewClientWithTarget miss default config (#3358) 2023-06-16 23:29:52 +08:00
dependabot[bot]
71e8230e65 chore(deps): bump google.golang.org/grpc from 1.55.0 to 1.56.0 (#3354) 2023-06-16 07:46:33 +08:00
dependabot[bot]
018fa8e0a0 chore(deps): bump google.golang.org/grpc from 1.55.0 to 1.56.0 in /tools/goctl (#3355) 2023-06-16 07:31:48 +08:00
dependabot[bot]
979fe9718a chore(deps): bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#3352) 2023-06-16 07:29:38 +08:00
Kevin Wan
f998803131 chore: refactor and add more tests (#3351) 2023-06-16 01:04:58 +08:00
TaoYu
1262266ac2 feat: httpx add common handler (#3269) 2023-06-15 15:31:15 +00:00
dependabot[bot]
9c32bf8478 chore(deps): bump github.com/zeromicro/ddl-parser from 1.0.4 to 1.0.5 in /tools/goctl (#3350) 2023-06-15 07:34:19 +08:00
dependabot[bot]
37ec7f6443 chore(deps): bump github.com/jackc/pgx/v5 from 5.3.1 to 5.4.0 (#3349) 2023-06-15 07:31:16 +08:00
dependabot[bot]
2fdc4dfc0f chore(deps): bump golang.org/x/net from 0.10.0 to 0.11.0 (#3346) 2023-06-14 07:05:24 +08:00
dependabot[bot]
4b2a6ba3de chore(deps): bump golang.org/x/text from 0.9.0 to 0.10.0 in /tools/goctl (#3342) 2023-06-13 07:37:52 +08:00
dependabot[bot]
7fa3f10f22 chore(deps): bump golang.org/x/sys from 0.8.0 to 0.9.0 (#3341) 2023-06-13 07:37:30 +08:00
elza
4a29a0b642 fix: fixed goctl api go --home parameter error when loading non-exist… (#3319)
Co-authored-by: yuanyou <yuanyou@kezaihui.com>
2023-06-12 16:00:41 +00:00
Kevin Wan
a62745a152 Update readme-cn.md 2023-06-12 23:35:33 +08:00
Kevin Wan
28314326e7 chore: more tests (#3340) 2023-06-12 23:29:23 +08:00
Kevin Wan
f6bdb6e1de chore: add more tests (#3338) 2023-06-12 01:22:20 +08:00
Kevin Wan
efa6940001 chore: improve logx gzip (#3332) 2023-06-09 22:50:59 +08:00
Ron_haur
da81d8f774 Fix: logx with Compress auto delete old logs (#3329)
Co-authored-by: haoran.ren <haoran.ren@mihoyo.com>
2023-06-08 11:08:04 +00:00
dependabot[bot]
fd84b27bdc chore(deps): bump go.mongodb.org/mongo-driver from 1.11.6 to 1.11.7 (#3325) 2023-06-08 07:21:49 +08:00
Kevin Wan
6b4d0d89c0 chore: add more tests (#3324) 2023-06-07 00:46:43 +08:00
Kevin Wan
d61a55f779 chore: update readme to remove upgrade parts. (#3318) 2023-06-04 23:34:38 +08:00
Kevin Wan
8ef4164209 chore: make test stable (#3317) 2023-06-04 23:20:58 +08:00
Kevin Wan
50e29e2075 chore: update go-zero for goctl (#3316) 2023-06-04 17:28:27 +08:00
Kevin Wan
452c9dbcaf chore: add more tests (#3315) 2023-06-01 21:08:44 +08:00
dependabot[bot]
3564e36a35 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.2 to 2.30.3 (#3309)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 10:42:26 +08:00
dependabot[bot]
e479e47634 chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 in /tools/goctl (#3306)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 11:28:15 +08:00
dependabot[bot]
ad921a6419 chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#3305)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 11:20:12 +08:00
Kevin Wan
44c8d6f269 chore: add more tests (#3304) 2023-05-30 23:27:27 +08:00
Kevin Wan
8a4cc4f98d chore: add more tests (#3299) 2023-05-29 23:44:36 +08:00
kangqi
e751736516 trace exporter: add new type file (#3298)
Co-authored-by: zhaikangqi <794556486@qq.com>
2023-05-29 18:24:59 +08:00
Kevin Wan
032f2419a2 Update readme-cn.md 2023-05-29 17:36:10 +08:00
Kevin Wan
84adc054bc chore: add more tests (#3296) 2023-05-29 07:39:41 +08:00
Kevin Wan
b92e706ce1 chore: refactor (#3295) 2023-05-28 21:31:36 +08:00
MiNG
1b5946346e feat: support optional otel global initialization for #3284 (#3292) 2023-05-28 11:41:48 +00:00
Kevin Wan
28d3905731 chore: add more tests (#3294) 2023-05-28 19:26:45 +08:00
hc
3726851c7f feat: sqlc add SetCacheWithExpire method (#3249)
Co-authored-by: luohancai <luohancai@taqu.cn>
2023-05-28 12:27:30 +08:00
Kevin Wan
2f2ddd373b chore: refactor retry (#3291) 2023-05-28 12:11:55 +08:00
Xiaoju Jiang
8d48e34eed update: expand the retry method to support timeout and interval control (#3283) 2023-05-28 10:17:50 +08:00
Kevin Wan
32f78668db chore: add more tests (#3290) 2023-05-27 23:57:33 +08:00
Kevin Wan
cd0f3726ed chore: add more tests (#3288) 2023-05-27 21:49:11 +08:00
me-cs
0217044900 update:Use the Milliseconds method of duration to get the number of milliseconds (#3285)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-05-26 14:32:46 +00:00
Kevin Wan
8b4382dcec chore: add more tests (#3286) 2023-05-26 22:30:03 +08:00
Kevin Wan
fa33329a44 chore: add more tests (#3282) 2023-05-26 00:21:47 +08:00
dependabot[bot]
d76a39ac26 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.7 to 2.0.8 (#3280)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-25 16:50:36 +08:00
guangwu
76a7a17e57 typo (#3281) 2023-05-25 15:58:11 +08:00
Kevin Wan
4a2a8d9e45 chore: add more tests (#3279) 2023-05-24 23:58:45 +08:00
guangwu
ef26b39b4c misspelling (#3248) 2023-05-24 08:15:27 +00:00
anqiansong
3ca40001b4 feat(goctl): Add with session for model tpl (#3272) 2023-05-24 07:34:26 +00:00
Toby
278ae3d26a feat: add OtlpHttpPath config support for ZincObserve Telemetry (#3271)
Signed-off-by: Toby Yan <me@tobyan.com>
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-05-23 03:11:58 +00:00
dependabot[bot]
fa1d6d50a8 chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#3267) 2023-05-22 09:41:17 +08:00
dependabot[bot]
0f4973be06 chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 in /tools/goctl (#3268) 2023-05-21 21:43:24 +08:00
Kevin Wan
a9aac7e420 chore: add more tests (#3265) 2023-05-19 23:29:30 +08:00
Kevin Wan
925cf8d3d1 chore: add more tests (#3261) 2023-05-19 12:15:43 +08:00
Kevin Wan
99ce24e2ab chore: add more tests (#3260) 2023-05-19 00:56:50 +08:00
Kevin Wan
701bb31ed2 chore: add more tests (#3259) 2023-05-18 23:43:50 +08:00
Kevin Wan
55e2c7ee83 chore: add more tests (#3258) 2023-05-18 23:11:32 +08:00
Kevin Wan
90839965fa chore: remove directive for tests (#3257) 2023-05-18 21:34:33 +08:00
Kevin Wan
f7228e9af1 chore: add more tests (#3256) 2023-05-18 12:24:04 +08:00
Kevin Wan
f95adae3c1 Update readme-cn.md 2023-05-18 11:00:03 +08:00
Kevin Wan
bff5b81ad9 feat: support using session to execute statements in transaction (#3252) 2023-05-17 14:15:24 +00:00
guangwu
f0bdfb928f fix call error func (#3245) 2023-05-12 04:34:08 +00:00
Kevin Wan
e4a1b7bb39 chore: format the code (#3243) 2023-05-12 10:38:05 +08:00
dependabot[bot]
b6906b5d21 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.8 to 3.5.9 (#3242)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 08:27:46 +08:00
guangwu
116da96178 add ignore file (#3240) 2023-05-11 08:53:13 +00:00
lchjczw
9fa98c2bd3 api imports take the form of relative paths (#3201)
Co-authored-by: 李春华 <lichunhua@threesoft.cn>
2023-05-10 03:40:07 +00:00
Kevin Wan
b1c4c4736f chore: better comments (#3232) 2023-05-09 22:58:40 +08:00
Thirteen
ef410e8083 fix: generate client directory for goctl (#3166) 2023-05-08 21:03:48 +00:00
fondoger
c22bc1c8ea [dart-gen] Fix lists containing atomic types (#3210) 2023-05-08 21:00:46 +00:00
Toby
1853428011 feat: add otlptracegrpc otlptracehttp headers support for Uptrace (#3219)
Signed-off-by: Toby Yan <me@tobyan.com>
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-05-08 20:58:29 +00:00
dependabot[bot]
3637e10815 chore(deps): bump golang.org/x/net from 0.9.0 to 0.10.0 (#3230) 2023-05-09 04:53:33 +08:00
Kevin Wan
93124329ac chore: add more tests (#3229) 2023-05-08 23:49:13 +08:00
yangtao
851a72f1cc Add RunSafe with context (#3224)
Co-authored-by: yangtao <mrynag8614@163.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-05-08 23:08:31 +08:00
SleeplessBot
a93c24ce84 Add method label for prometheus middleware metrics (#3226)
Co-authored-by: 蓝益尤 <lan.yiyou@intellif.com>
2023-05-08 12:59:20 +00:00
Kevin Wan
9f42eda9ff fix: timeout handler not implementing http.Flusher (#3225) 2023-05-08 18:07:02 +08:00
soasurs
8762a3b7ba fix: Errorv should generate JSON Object for content field in log (#3222)
Signed-off-by: soasurs <soasurs@gmail.com>
2023-05-08 09:16:44 +00:00
Kevin Wan
2684a157ff chore: remove fgprof, use pprof directly (#3220) 2023-05-07 18:21:10 +08:00
guangwu
63368d8b0c io/ioutil deprecated (#3217) 2023-05-06 09:50:54 +00:00
guangwu
4f13fe8188 io/ioutil deprecated (#3215) 2023-05-06 16:37:43 +08:00
Kevin Wan
9fc7874336 chore: optimize stat calculation (#3213) 2023-05-06 15:31:56 +08:00
dependabot[bot]
e6518521eb chore(deps): bump google.golang.org/grpc from 1.54.0 to 1.55.0 in /tools/goctl (#3209)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-06 14:29:51 +08:00
Kevin Wan
8f5a0a2de7 fix: remove etcd pings to avoid too-many-pings error (#3212) 2023-05-06 12:39:19 +08:00
ALMAS
774e8d1d08 feat: replaced color package to support Windows (#3207) 2023-05-05 13:09:54 +00:00
cong
8ad0668612 fix(zrpc): remove default keepalive params for NewClientWithTarget (#3208) 2023-05-05 13:00:59 +00:00
dependabot[bot]
8a043d2443 chore(deps): bump golang.org/x/sys from 0.7.0 to 0.8.0 (#3204)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 11:03:18 +08:00
dependabot[bot]
0e2ee97a02 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.4 to 1.11.6 (#3205)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 10:41:23 +08:00
Kevin Wan
42300a7d83 chore: add more tests (#3203) 2023-05-04 23:43:34 +08:00
dependabot[bot]
fe97fab274 chore(deps): bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 (#3200)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-04 11:00:51 +08:00
dependabot[bot]
f93e752f98 chore(deps): bump github.com/emicklei/proto from 1.11.1 to 1.11.2 in /tools/goctl (#3199)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-04 10:24:24 +08:00
fondoger
3a66fc038f [dart-gen] Fix nullable list item issue (#3192) 2023-05-01 15:01:28 +08:00
Kevin Wan
b028ed058d chore: change port to 6060 by default in devserver (#3191) 2023-05-01 11:18:15 +08:00
guonaihong
1fd0c3992b fix panic (#3176) 2023-04-30 14:58:30 +00:00
Kevin Wan
1aebb3e5e4 chore: update readme (#3190) 2023-04-30 11:21:28 +08:00
Kevin Wan
8ffe4c01d1 chore: use logx.Must instead of log.Fatal (#3189) 2023-04-29 23:46:04 +08:00
Kevin Wan
a31256b327 chore: add more tests (#3187) 2023-04-29 22:59:07 +08:00
Kevin Wan
14caf5c799 chore: simplify tests with logtest (#3184) 2023-04-29 20:36:29 +08:00
chensy
c0f8a58ed7 no \t to space line (#3177)
Co-authored-by: chenjieping <chenjieping@kezaihui.com>
2023-04-29 08:35:04 +00:00
dependabot[bot]
3189ec7be6 chore(deps): bump github.com/go-sql-driver/mysql from 1.7.0 to 1.7.1 (#3174)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-28 23:27:01 +08:00
dependabot[bot]
f51e9f0ea7 chore(deps): bump github.com/go-sql-driver/mysql from 1.7.0 to 1.7.1 in /tools/goctl (#3175)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-27 23:37:28 +08:00
cong
ba9d510cdb fix(metrics): enable prometheus global switch when user enable devsever metrics (#3169) 2023-04-24 20:02:04 +08:00
Kevin Wan
8c9b619199 chore: update go-zero for goctl (#3165) 2023-04-24 13:02:26 +08:00
Kevin Wan
49f73265b9 chore: refactor (#3164) 2023-04-24 12:34:52 +08:00
Kevin Wan
7568674b2b chore: use %q instead of %s for unmarshaling (#3163) 2023-04-24 12:11:12 +08:00
Kevin Wan
3da740b7fc chore: remove unnecessary code (#3161) 2023-04-23 22:58:22 +08:00
chen quan
ce4eb6ed61 fix: fixed #2945 (#2953)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-23 14:22:03 +00:00
MarkJoyMa
9970ff55cd feat: refactor gateway code (#3160) 2023-04-23 22:05:10 +08:00
MarkJoyMa
d10740f871 feat: inheritance rewrite error prompt is more friendly (#3156) 2023-04-23 12:56:54 +00:00
Kevin Wan
027193dc99 chore: refactor gateway (#3157) 2023-04-22 23:25:51 +08:00
Kevin Wan
de1e0f2410 Update readme-cn.md 2023-04-22 22:36:55 +08:00
Kevin Wan
062073ce58 chore: update codecov to ignore mock files (#3155) 2023-04-22 14:07:15 +08:00
MarkJoyMa
e20b02f311 gateway: open timeout function cabinet (#3047)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-22 05:23:47 +00:00
Kevin Wan
02357d2616 chore: make error message more readable (#3154) 2023-04-22 13:03:59 +08:00
guangwu
489d69f779 Add debug message on unmarshal errors (#3153) 2023-04-21 13:12:02 +00:00
Kevin Wan
117611a170 fix: default value for keepalive set to 0 (#3152) 2023-04-21 03:15:05 +00:00
Kevin Wan
0a46ad7ac1 chore: fix go 1.18 dep problem (#3149) 2023-04-21 01:01:17 +08:00
Liam Hao
bf905eaff3 fix default grpc-gateway connect timeout (#3142) 2023-04-20 15:13:13 +00:00
dependabot[bot]
88cb35e3d5 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.1 to 2.30.2 (#3144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-20 22:59:57 +08:00
fondoger
078825b4eb [dart-gen] Support Null-safe and omitempty json tag (#3134) 2023-04-17 05:06:52 +00:00
Kevin Wan
bbfce6abe9 chore: refactor max/min in fx (#3135) 2023-04-16 23:40:30 +08:00
suplalalala
0d11ce03a8 feature: two evaluate operate func addtion in package stream (#3129)
Co-authored-by: Riven <Riven.chen@hairobotics.com>
2023-04-16 15:14:25 +00:00
dependabot[bot]
757ed19dc5 chore(deps): bump k8s.io/apimachinery from 0.26.3 to 0.27.1 (#3131)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:29:46 +08:00
dependabot[bot]
c5fd074aac chore(deps): bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 (#3122)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:01:02 +08:00
LiKe
8fa0bd1f1c fix(goctl): test field (#3114) 2023-04-15 21:46:37 +08:00
dependabot[bot]
ede19a89ec chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.8 (#3123)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 21:24:11 +08:00
anqiansong
73664b92f0 fix(goctl): missing rpc --style flag (#3112) 2023-04-10 09:48:48 +00:00
208 changed files with 5425 additions and 2116 deletions

View File

@@ -3,4 +3,7 @@ comment:
behavior: once
require_changes: true
ignore:
- "tools"
- "tools"
- "**/mock"
- "**/*_mock.go"
- "**/*test"

View File

@@ -17,7 +17,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.18
go-version: 1.18
check-latest: true
cache: true
id: go
@@ -30,11 +30,6 @@ jobs:
run: |
go vet -stdmethods=false $(go list ./...)
if ! test -z "$(gofmt -l .)"; then
echo "Please run 'gofmt -l -w .'"
exit 1
fi
go mod tidy
if ! test -z "$(git status --porcelain)"; then
echo "Please run 'go mod tidy'"
@@ -66,5 +61,5 @@ jobs:
run: |
go mod verify
go mod download
go test -v -race ./...
go test ./...
cd tools/goctl && go build -v goctl.go

4
.gitignore vendored
View File

@@ -12,11 +12,13 @@
# ignore
**/.idea
**/.vscode
**/.DS_Store
**/logs
**/adhoc
**/coverage.txt
# for test purpose
**/adhoc
go.work
go.work.sum

View File

@@ -2,6 +2,7 @@ package codec
import (
"encoding/base64"
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -41,6 +42,7 @@ func TestCryption(t *testing.T) {
file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err)
defer os.Remove(file)
dec, err := NewRsaDecrypter(file)
assert.Nil(t, err)
actual, err := dec.Decrypt(ret)

View File

@@ -73,7 +73,7 @@ func LoadConfig(file string, v any, opts ...Option) error {
// LoadFromJsonBytes loads config into v from content json bytes.
func LoadFromJsonBytes(content []byte, v any) error {
info, err := buildFieldsInfo(reflect.TypeOf(v))
info, err := buildFieldsInfo(reflect.TypeOf(v), "")
if err != nil {
return err
}
@@ -127,13 +127,13 @@ func MustLoad(path string, v any, opts ...Option) {
}
}
func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo) error {
func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo, fullName string) error {
if prev, ok := info.children[key]; ok {
if child.mapField != nil {
return newConflictKeyError(key)
return newConflictKeyError(fullName)
}
if err := mergeFields(prev, key, child.children); err != nil {
if err := mergeFields(prev, key, child.children, fullName); err != nil {
return err
}
} else {
@@ -143,27 +143,27 @@ func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo) error {
return nil
}
func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type) error {
func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
switch ft.Kind() {
case reflect.Struct:
fields, err := buildFieldsInfo(ft)
fields, err := buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
for k, v := range fields.children {
if err = addOrMergeFields(info, k, v); err != nil {
if err = addOrMergeFields(info, k, v, fullName); err != nil {
return err
}
}
case reflect.Map:
elemField, err := buildFieldsInfo(mapping.Deref(ft.Elem()))
elemField, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
if err != nil {
return err
}
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(lowerCaseName)
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
@@ -172,7 +172,7 @@ func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.T
}
default:
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(lowerCaseName)
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
@@ -183,14 +183,14 @@ func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.T
return nil
}
func buildFieldsInfo(tp reflect.Type) (*fieldInfo, error) {
func buildFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
tp = mapping.Deref(tp)
switch tp.Kind() {
case reflect.Struct:
return buildStructFieldsInfo(tp)
return buildStructFieldsInfo(tp, fullName)
case reflect.Array, reflect.Slice:
return buildFieldsInfo(mapping.Deref(tp.Elem()))
return buildFieldsInfo(mapping.Deref(tp.Elem()), fullName)
case reflect.Chan, reflect.Func:
return nil, fmt.Errorf("unsupported type: %s", tp.Kind())
default:
@@ -200,23 +200,23 @@ func buildFieldsInfo(tp reflect.Type) (*fieldInfo, error) {
}
}
func buildNamedFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type) error {
func buildNamedFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
var finfo *fieldInfo
var err error
switch ft.Kind() {
case reflect.Struct:
finfo, err = buildFieldsInfo(ft)
finfo, err = buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
case reflect.Array, reflect.Slice:
finfo, err = buildFieldsInfo(ft.Elem())
finfo, err = buildFieldsInfo(ft.Elem(), fullName)
if err != nil {
return err
}
case reflect.Map:
elemInfo, err := buildFieldsInfo(mapping.Deref(ft.Elem()))
elemInfo, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
if err != nil {
return err
}
@@ -226,16 +226,16 @@ func buildNamedFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type)
mapField: elemInfo,
}
default:
finfo, err = buildFieldsInfo(ft)
finfo, err = buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
}
return addOrMergeFields(info, lowerCaseName, finfo)
return addOrMergeFields(info, lowerCaseName, finfo, fullName)
}
func buildStructFieldsInfo(tp reflect.Type) (*fieldInfo, error) {
func buildStructFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
info := &fieldInfo{
children: make(map[string]*fieldInfo),
}
@@ -251,10 +251,12 @@ func buildStructFieldsInfo(tp reflect.Type) (*fieldInfo, error) {
ft := mapping.Deref(field.Type)
// flatten anonymous fields
if field.Anonymous {
if err := buildAnonymousFieldInfo(info, lowerCaseName, ft); err != nil {
if err := buildAnonymousFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
} else if err := buildNamedFieldInfo(info, lowerCaseName, ft); err != nil {
} else if err := buildNamedFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
}
@@ -279,15 +281,15 @@ func getTagName(field reflect.StructField) string {
return field.Name
}
func mergeFields(prev *fieldInfo, key string, children map[string]*fieldInfo) error {
func mergeFields(prev *fieldInfo, key string, children map[string]*fieldInfo, fullName string) error {
if len(prev.children) == 0 || len(children) == 0 {
return newConflictKeyError(key)
return newConflictKeyError(fullName)
}
// merge fields
for k, v := range children {
if _, ok := prev.children[k]; ok {
return newConflictKeyError(k)
return newConflictKeyError(fullName)
}
prev.children[k] = v
@@ -349,3 +351,11 @@ func newConflictKeyError(key string) conflictKeyError {
func (e conflictKeyError) Error() string {
return fmt.Sprintf("conflict key %s, pay attention to anonymous fields", e.key)
}
func getFullName(parent, child string) string {
if len(parent) == 0 {
return child
}
return strings.Join([]string{parent, child}, ".")
}

View File

@@ -2,6 +2,7 @@ package conf
import (
"os"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
@@ -34,11 +35,11 @@ func TestConfigJson(t *testing.T) {
"c": "${FOO}",
"d": "abcd!@#$112"
}`
t.Setenv("FOO", "2")
for _, test := range tests {
test := test
t.Run(test, func(t *testing.T) {
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
tmpfile, err := createTempFile(test, text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
@@ -80,8 +81,7 @@ b = 1
c = "${FOO}"
d = "abcd!@#$112"
`
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
t.Setenv("FOO", "2")
tmpfile, err := createTempFile(".toml", text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
@@ -206,8 +206,7 @@ b = 1
c = "${FOO}"
d = "abcd!@#112"
`
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
t.Setenv("FOO", "2")
tmpfile, err := createTempFile(".toml", text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
@@ -238,11 +237,10 @@ func TestConfigJsonEnv(t *testing.T) {
"c": "${FOO}",
"d": "abcd!@#$a12 3"
}`
t.Setenv("FOO", "2")
for _, test := range tests {
test := test
t.Run(test, func(t *testing.T) {
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
tmpfile, err := createTempFile(test, text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
@@ -733,7 +731,7 @@ func Test_FieldOverwrite(t *testing.T) {
input := []byte(`{"Name": "hello"}`)
err := LoadFromJsonBytes(input, val)
assert.ErrorAs(t, err, &dupErr)
assert.Equal(t, newConflictKeyError("name").Error(), err.Error())
assert.Error(t, err)
}
validate(&St0{})
@@ -1196,6 +1194,100 @@ Email = "bar"`)
})
}
func Test_getFullName(t *testing.T) {
assert.Equal(t, "a.b", getFullName("a", "b"))
assert.Equal(t, "a", getFullName("", "a"))
}
func Test_buildFieldsInfo(t *testing.T) {
type ParentSt struct {
Name string
M map[string]int
}
tests := []struct {
name string
t reflect.Type
ok bool
containsKey string
}{
{
name: "normal",
t: reflect.TypeOf(struct{ A string }{}),
ok: true,
},
{
name: "struct anonymous",
t: reflect.TypeOf(struct {
ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
{
name: "struct ptr anonymous",
t: reflect.TypeOf(struct {
*ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
{
name: "more struct anonymous",
t: reflect.TypeOf(struct {
Value struct {
ParentSt
Name string
}
}{}),
ok: false,
containsKey: newConflictKeyError("value.name").Error(),
},
{
name: "map anonymous",
t: reflect.TypeOf(struct {
ParentSt
M string
}{}),
ok: false,
containsKey: newConflictKeyError("m").Error(),
},
{
name: "map more anonymous",
t: reflect.TypeOf(struct {
Value struct {
ParentSt
M string
}
}{}),
ok: false,
containsKey: newConflictKeyError("value.m").Error(),
},
{
name: "struct slice anonymous",
t: reflect.TypeOf([]struct {
ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := buildFieldsInfo(tt.t, "")
if tt.ok {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.Equal(t, err.Error(), tt.containsKey)
}
})
}
}
func createTempFile(ext, text string) (string, error) {
tmpFile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
if err != nil {

View File

@@ -45,8 +45,7 @@ func TestPropertiesEnv(t *testing.T) {
assert.Nil(t, err)
defer os.Remove(tmpfile)
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
t.Setenv("FOO", "2")
props, err := LoadProperties(tmpfile, UseEnv())
assert.Nil(t, err)

View File

@@ -337,13 +337,11 @@ func (c *cluster) watchConnState(cli EtcdClient) {
// DialClient dials an etcd cluster with given endpoints.
func DialClient(endpoints []string) (EtcdClient, error) {
cfg := clientv3.Config{
Endpoints: endpoints,
AutoSyncInterval: autoSyncInterval,
DialTimeout: DialTimeout,
DialKeepAliveTime: dialKeepAliveTime,
DialKeepAliveTimeout: DialTimeout,
RejectOldCluster: true,
PermitWithoutStream: true,
Endpoints: endpoints,
AutoSyncInterval: autoSyncInterval,
DialTimeout: DialTimeout,
RejectOldCluster: true,
PermitWithoutStream: true,
}
if account, ok := GetAccount(endpoints); ok {
cfg.Username = account.User

View File

@@ -9,7 +9,6 @@ const (
autoSyncInterval = time.Minute
coolDownInterval = time.Second
dialTimeout = 5 * time.Second
dialKeepAliveTime = 5 * time.Second
requestTimeout = 3 * time.Second
endpointsSeparator = ","
)

View File

@@ -78,7 +78,7 @@ func TestBulkExecutorFlush(t *testing.T) {
wait.Wait()
}
func TestBuldExecutorFlushSlowTasks(t *testing.T) {
func TestBulkExecutorFlushSlowTasks(t *testing.T) {
const total = 1500
lock := new(sync.Mutex)
result := make([]any, 0, 10000)

View File

@@ -168,23 +168,23 @@ func TestPeriodicalExecutor_FlushPanic(t *testing.T) {
func TestPeriodicalExecutor_Wait(t *testing.T) {
var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []any) {
executor := NewBulkExecutor(func(tasks []any) {
lock.Lock()
defer lock.Unlock()
time.Sleep(10 * time.Millisecond)
}, WithBulkTasks(1), WithBulkInterval(time.Second))
for i := 0; i < 10; i++ {
executer.Add(1)
executor.Add(1)
}
executer.Flush()
executer.Wait()
executor.Flush()
executor.Wait()
}
func TestPeriodicalExecutor_WaitFast(t *testing.T) {
const total = 3
var cnt int
var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []any) {
executor := NewBulkExecutor(func(tasks []any) {
defer func() {
cnt++
}()
@@ -193,10 +193,10 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
time.Sleep(10 * time.Millisecond)
}, WithBulkTasks(1), WithBulkInterval(10*time.Millisecond))
for i := 0; i < total; i++ {
executer.Add(2)
executor.Add(2)
}
executer.Flush()
executer.Wait()
executor.Flush()
executor.Wait()
assert.Equal(t, total, cnt)
}

View File

@@ -74,6 +74,11 @@ func TestFirstLineShort(t *testing.T) {
assert.Equal(t, "first line", val)
}
func TestFirstLineError(t *testing.T) {
_, err := FirstLine("/tmp/does-not-exist")
assert.Error(t, err)
}
func TestLastLine(t *testing.T) {
filename, err := fs.TempFilenameWithText(text)
assert.Nil(t, err)
@@ -113,3 +118,8 @@ func TestLastLineWithLastNewlineShort(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, "last line", val)
}
func TestLastLineError(t *testing.T) {
_, err := LastLine("/tmp/does-not-exist")
assert.Error(t, err)
}

View File

@@ -11,29 +11,29 @@ import (
// The file is kept as open, the caller should close the file handle,
// and remove the file by name.
func TempFileWithText(text string) (*os.File, error) {
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text)))
tmpFile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text)))
if err != nil {
return nil, err
}
if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
if err := os.WriteFile(tmpFile.Name(), []byte(text), os.ModeTemporary); err != nil {
return nil, err
}
return tmpfile, nil
return tmpFile, nil
}
// TempFilenameWithText creates the file with the given content,
// and returns the filename (full path).
// The caller should remove the file after use.
func TempFilenameWithText(text string) (string, error) {
tmpfile, err := TempFileWithText(text)
tmpFile, err := TempFileWithText(text)
if err != nil {
return "", err
}
filename := tmpfile.Name()
if err = tmpfile.Close(); err != nil {
filename := tmpFile.Name()
if err = tmpFile.Close(); err != nil {
return "", err
}

View File

@@ -1,31 +1,87 @@
package fx
import "github.com/zeromicro/go-zero/core/errorx"
import (
"context"
"errors"
"time"
"github.com/zeromicro/go-zero/core/errorx"
)
const defaultRetryTimes = 3
var errTimeout = errors.New("retry timeout")
type (
// RetryOption defines the method to customize DoWithRetry.
RetryOption func(*retryOptions)
retryOptions struct {
times int
times int
interval time.Duration
timeout time.Duration
}
)
// DoWithRetry runs fn, and retries if failed. Default to retry 3 times.
// Note that if the fn function accesses global variables outside the function
// and performs modification operations, it is best to lock them,
// otherwise there may be data race issues
func DoWithRetry(fn func() error, opts ...RetryOption) error {
return retry(func(errChan chan error, retryCount int) {
errChan <- fn()
}, opts...)
}
// DoWithRetryCtx runs fn, and retries if failed. Default to retry 3 times.
// fn retryCount indicates the current number of retries, starting from 0
// Note that if the fn function accesses global variables outside the function
// and performs modification operations, it is best to lock them,
// otherwise there may be data race issues
func DoWithRetryCtx(ctx context.Context, fn func(ctx context.Context, retryCount int) error,
opts ...RetryOption) error {
return retry(func(errChan chan error, retryCount int) {
errChan <- fn(ctx, retryCount)
}, opts...)
}
func retry(fn func(errChan chan error, retryCount int), opts ...RetryOption) error {
options := newRetryOptions()
for _, opt := range opts {
opt(options)
}
var berr errorx.BatchError
var cancelFunc context.CancelFunc
ctx := context.Background()
if options.timeout > 0 {
ctx, cancelFunc = context.WithTimeout(ctx, options.timeout)
defer cancelFunc()
}
errChan := make(chan error, 1)
for i := 0; i < options.times; i++ {
if err := fn(); err != nil {
berr.Add(err)
} else {
return nil
go fn(errChan, i)
select {
case err := <-errChan:
if err != nil {
berr.Add(err)
} else {
return nil
}
case <-ctx.Done():
berr.Add(errTimeout)
return berr.Err()
}
if options.interval > 0 {
select {
case <-ctx.Done():
berr.Add(errTimeout)
return berr.Err()
case <-time.After(options.interval):
}
}
}
@@ -39,6 +95,18 @@ func WithRetry(times int) RetryOption {
}
}
func WithInterval(interval time.Duration) RetryOption {
return func(options *retryOptions) {
options.interval = interval
}
}
func WithTimeout(timeout time.Duration) RetryOption {
return func(options *retryOptions) {
options.timeout = timeout
}
}
func newRetryOptions() *retryOptions {
return &retryOptions{
times: defaultRetryTimes,

View File

@@ -1,8 +1,10 @@
package fx
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
@@ -12,31 +14,103 @@ func TestRetry(t *testing.T) {
return errors.New("any")
}))
var times int
times1 := 0
assert.Nil(t, DoWithRetry(func() error {
times++
if times == defaultRetryTimes {
times1++
if times1 == defaultRetryTimes {
return nil
}
return errors.New("any")
}))
times = 0
times2 := 0
assert.NotNil(t, DoWithRetry(func() error {
times++
if times == defaultRetryTimes+1 {
times2++
if times2 == defaultRetryTimes+1 {
return nil
}
return errors.New("any")
}))
total := 2 * defaultRetryTimes
times = 0
times3 := 0
assert.Nil(t, DoWithRetry(func() error {
times++
if times == total {
times3++
if times3 == total {
return nil
}
return errors.New("any")
}, WithRetry(total)))
}
func TestRetryWithTimeout(t *testing.T) {
assert.Nil(t, DoWithRetry(func() error {
return nil
}, WithTimeout(time.Millisecond*500)))
times1 := 0
assert.Nil(t, DoWithRetry(func() error {
times1++
if times1 == 1 {
return errors.New("any ")
}
time.Sleep(time.Millisecond * 150)
return nil
}, WithTimeout(time.Millisecond*250)))
total := defaultRetryTimes
times2 := 0
assert.Nil(t, DoWithRetry(func() error {
times2++
if times2 == total {
return nil
}
time.Sleep(time.Millisecond * 50)
return errors.New("any")
}, WithTimeout(time.Millisecond*50*(time.Duration(total)+2))))
assert.NotNil(t, DoWithRetry(func() error {
return errors.New("any")
}, WithTimeout(time.Millisecond*250)))
}
func TestRetryWithInterval(t *testing.T) {
times1 := 0
assert.NotNil(t, DoWithRetry(func() error {
times1++
if times1 == 1 {
return errors.New("any")
}
time.Sleep(time.Millisecond * 150)
return nil
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
times2 := 0
assert.NotNil(t, DoWithRetry(func() error {
times2++
if times2 == 2 {
return nil
}
time.Sleep(time.Millisecond * 150)
return errors.New("any ")
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
}
func TestRetryCtx(t *testing.T) {
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
if retryCount == 0 {
return errors.New("any")
}
time.Sleep(time.Millisecond * 150)
return nil
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
assert.NotNil(t, DoWithRetryCtx(context.Background(), func(ctx context.Context, retryCount int) error {
if retryCount == 1 {
return nil
}
time.Sleep(time.Millisecond * 150)
return errors.New("any ")
}, WithTimeout(time.Millisecond*250), WithInterval(time.Millisecond*150)))
}

View File

@@ -292,6 +292,18 @@ func (s Stream) Map(fn MapFunc, opts ...Option) Stream {
}, opts...)
}
// Max returns the maximum item from the underlying source.
func (s Stream) Max(less LessFunc) any {
var max any
for item := range s.source {
if max == nil || less(max, item) {
max = item
}
}
return max
}
// Merge merges all the items into a slice and generates a new stream.
func (s Stream) Merge() Stream {
var items []any
@@ -306,6 +318,18 @@ func (s Stream) Merge() Stream {
return Range(source)
}
// Min returns the minimum item from the underlying source.
func (s Stream) Min(less LessFunc) any {
var min any
for item := range s.source {
if min == nil || less(item, min) {
min = item
}
}
return min
}
// NoneMatch returns whether all elements of this stream don't match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then true is returned and the predicate is not evaluated.

View File

@@ -503,6 +503,83 @@ func TestStream_Concat(t *testing.T) {
})
}
func TestStream_Max(t *testing.T) {
runCheckedTest(t, func(t *testing.T) {
tests := []struct {
name string
elements []any
max any
}{
{
name: "no elements with nil",
},
{
name: "no elements",
elements: []any{},
max: nil,
},
{
name: "1 element",
elements: []any{1},
max: 1,
},
{
name: "multiple elements",
elements: []any{1, 2, 9, 5, 8},
max: 9,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
val := Just(test.elements...).Max(func(a, b any) bool {
return a.(int) < b.(int)
})
assetEqual(t, test.max, val)
})
}
})
}
func TestStream_Min(t *testing.T) {
runCheckedTest(t, func(t *testing.T) {
tests := []struct {
name string
elements []any
min any
}{
{
name: "no elements with nil",
min: nil,
},
{
name: "no elements",
elements: []any{},
min: nil,
},
{
name: "1 element",
elements: []any{1},
min: 1,
},
{
name: "multiple elements",
elements: []any{-1, 1, 2, 9, 5, 8},
min: -1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
val := Just(test.elements...).Min(func(a, b any) bool {
return a.(int) < b.(int)
})
assetEqual(t, test.min, val)
})
}
})
}
func BenchmarkParallelMapReduce(b *testing.B) {
b.ReportAllocs()

View File

@@ -40,11 +40,11 @@ b`,
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
tmpfile, err := fs.TempFilenameWithText(test.input)
tmpFile, err := fs.TempFilenameWithText(test.input)
assert.Nil(t, err)
defer os.Remove(tmpfile)
defer os.Remove(tmpFile)
content, err := ReadText(tmpfile)
content, err := ReadText(tmpFile)
assert.Nil(t, err)
assert.Equal(t, test.expect, content)
})
@@ -59,9 +59,9 @@ func TestReadTextLines(t *testing.T) {
#a
3`
tmpfile, err := fs.TempFilenameWithText(text)
tmpFile, err := fs.TempFilenameWithText(text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
defer os.Remove(tmpFile)
tests := []struct {
options []TextReadOption
@@ -87,7 +87,7 @@ func TestReadTextLines(t *testing.T) {
for _, test := range tests {
t.Run(stringx.Rand(), func(t *testing.T) {
lines, err := ReadTextLines(tmpfile, test.options...)
lines, err := ReadTextLines(tmpFile, test.options...)
assert.Nil(t, err)
assert.Equal(t, test.expectLines, len(lines))
})

View File

@@ -1,7 +1,6 @@
package logc
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -11,14 +10,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
)
func TestAddGlobalFields(t *testing.T) {
var buf bytes.Buffer
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
Info(context.Background(), "hello")
buf.Reset()
@@ -34,155 +30,90 @@ func TestAddGlobalFields(t *testing.T) {
}
func TestAlert(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
Alert(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), "foo"), buf.String())
}
func TestError(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Error(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestErrorf(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Errorf(context.Background(), "foo %s", "bar")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestErrorv(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Errorv(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestErrorw(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Errorw(context.Background(), "foo", Field("a", "b"))
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestInfo(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Info(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestInfof(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Infof(context.Background(), "foo %s", "bar")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestInfov(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Infov(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestInfow(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Infow(context.Background(), "foo", Field("a", "b"))
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebug(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Debug(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugf(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Debugf(context.Background(), "foo %s", "bar")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugv(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Debugv(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugw(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Debugw(context.Background(), "foo", Field("a", "b"))
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
@@ -204,48 +135,28 @@ func TestMisc(t *testing.T) {
}
func TestSlow(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Slow(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
}
func TestSlowf(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Slowf(context.Background(), "foo %s", "bar")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
}
func TestSlowv(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Slowv(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
}
func TestSloww(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
buf := logtest.NewCollector(t)
file, line := getFileLine()
Sloww(context.Background(), "foo", Field("a", "b"))
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())

View File

@@ -32,7 +32,7 @@ type LogConf struct {
StackCooldownMillis int `json:",default=100"`
// MaxBackups represents how many backup log files will be kept. 0 means all files will be kept forever.
// Only take effect when RotationRuleType is `size`.
// Even thougth `MaxBackups` sets 0, log files will still be removed
// Even though `MaxBackups` sets 0, log files will still be removed
// if the `KeepDays` limitation is reached.
MaxBackups int `json:",default=0"`
// MaxSize represents how much space the writing log file takes up. 0 means no limit. The unit is `MB`.

40
core/logx/fs.go Normal file
View File

@@ -0,0 +1,40 @@
package logx
import (
"io"
"os"
)
var fileSys realFileSystem
type (
fileSystem interface {
Close(closer io.Closer) error
Copy(writer io.Writer, reader io.Reader) (int64, error)
Create(name string) (*os.File, error)
Open(name string) (*os.File, error)
Remove(name string) error
}
realFileSystem struct{}
)
func (fs realFileSystem) Close(closer io.Closer) error {
return closer.Close()
}
func (fs realFileSystem) Copy(writer io.Writer, reader io.Reader) (int64, error) {
return io.Copy(writer, reader)
}
func (fs realFileSystem) Create(name string) (*os.File, error) {
return os.Create(name)
}
func (fs realFileSystem) Open(name string) (*os.File, error) {
return os.Open(name)
}
func (fs realFileSystem) Remove(name string) error {
return os.Remove(name)
}

View File

@@ -68,22 +68,30 @@ func Close() error {
// Debug writes v into access log.
func Debug(v ...any) {
writeDebug(fmt.Sprint(v...))
if shallLog(DebugLevel) {
writeDebug(fmt.Sprint(v...))
}
}
// Debugf writes v with format into access log.
func Debugf(format string, v ...any) {
writeDebug(fmt.Sprintf(format, v...))
if shallLog(DebugLevel) {
writeDebug(fmt.Sprintf(format, v...))
}
}
// Debugv writes v into access log with json content.
func Debugv(v any) {
writeDebug(v)
if shallLog(DebugLevel) {
writeDebug(v)
}
}
// Debugw writes msg along with fields into access log.
func Debugw(msg string, fields ...LogField) {
writeDebug(msg, fields...)
if shallLog(DebugLevel) {
writeDebug(msg, fields...)
}
}
// Disable disables the logging.
@@ -99,35 +107,47 @@ func DisableStat() {
// Error writes v into error log.
func Error(v ...any) {
writeError(fmt.Sprint(v...))
if shallLog(ErrorLevel) {
writeError(fmt.Sprint(v...))
}
}
// Errorf writes v with format into error log.
func Errorf(format string, v ...any) {
writeError(fmt.Errorf(format, v...).Error())
if shallLog(ErrorLevel) {
writeError(fmt.Errorf(format, v...).Error())
}
}
// ErrorStack writes v along with call stack into error log.
func ErrorStack(v ...any) {
// there is newline in stack string
writeStack(fmt.Sprint(v...))
if shallLog(ErrorLevel) {
// there is newline in stack string
writeStack(fmt.Sprint(v...))
}
}
// ErrorStackf writes v along with call stack in format into error log.
func ErrorStackf(format string, v ...any) {
// there is newline in stack string
writeStack(fmt.Sprintf(format, v...))
if shallLog(ErrorLevel) {
// there is newline in stack string
writeStack(fmt.Sprintf(format, v...))
}
}
// Errorv writes v into error log with json content.
// No call stack attached, because not elegant to pack the messages.
func Errorv(v any) {
writeError(v)
if shallLog(ErrorLevel) {
writeError(v)
}
}
// Errorw writes msg along with fields into error log.
func Errorw(msg string, fields ...LogField) {
writeError(msg, fields...)
if shallLog(ErrorLevel) {
writeError(msg, fields...)
}
}
// Field returns a LogField for the given key and value.
@@ -170,22 +190,30 @@ func Field(key string, value any) LogField {
// Info writes v into access log.
func Info(v ...any) {
writeInfo(fmt.Sprint(v...))
if shallLog(InfoLevel) {
writeInfo(fmt.Sprint(v...))
}
}
// Infof writes v with format into access log.
func Infof(format string, v ...any) {
writeInfo(fmt.Sprintf(format, v...))
if shallLog(InfoLevel) {
writeInfo(fmt.Sprintf(format, v...))
}
}
// Infov writes v into access log with json content.
func Infov(v any) {
writeInfo(v)
if shallLog(InfoLevel) {
writeInfo(v)
}
}
// Infow writes msg along with fields into access log.
func Infow(msg string, fields ...LogField) {
writeInfo(msg, fields...)
if shallLog(InfoLevel) {
writeInfo(msg, fields...)
}
}
// Must checks if err is nil, otherwise logs the error and exits.
@@ -194,7 +222,7 @@ func Must(err error) {
return
}
msg := err.Error()
msg := fmt.Sprintf("%+v\n\n%s", err.Error(), debug.Stack())
log.Print(msg)
getWriter().Severe(msg)
@@ -269,42 +297,58 @@ func SetUp(c LogConf) (err error) {
// Severe writes v into severe log.
func Severe(v ...any) {
writeSevere(fmt.Sprint(v...))
if shallLog(SevereLevel) {
writeSevere(fmt.Sprint(v...))
}
}
// Severef writes v with format into severe log.
func Severef(format string, v ...any) {
writeSevere(fmt.Sprintf(format, v...))
if shallLog(SevereLevel) {
writeSevere(fmt.Sprintf(format, v...))
}
}
// Slow writes v into slow log.
func Slow(v ...any) {
writeSlow(fmt.Sprint(v...))
if shallLog(ErrorLevel) {
writeSlow(fmt.Sprint(v...))
}
}
// Slowf writes v with format into slow log.
func Slowf(format string, v ...any) {
writeSlow(fmt.Sprintf(format, v...))
if shallLog(ErrorLevel) {
writeSlow(fmt.Sprintf(format, v...))
}
}
// Slowv writes v into slow log with json content.
func Slowv(v any) {
writeSlow(v)
if shallLog(ErrorLevel) {
writeSlow(v)
}
}
// Sloww writes msg along with fields into slow log.
func Sloww(msg string, fields ...LogField) {
writeSlow(msg, fields...)
if shallLog(ErrorLevel) {
writeSlow(msg, fields...)
}
}
// Stat writes v into stat log.
func Stat(v ...any) {
writeStat(fmt.Sprint(v...))
if shallLogStat() && shallLog(InfoLevel) {
writeStat(fmt.Sprint(v...))
}
}
// Statf writes v with format into stat log.
func Statf(format string, v ...any) {
writeStat(fmt.Sprintf(format, v...))
if shallLogStat() && shallLog(InfoLevel) {
writeStat(fmt.Sprintf(format, v...))
}
}
// WithCooldownMillis customizes logging on writing call stack interval.
@@ -358,14 +402,16 @@ func createOutput(path string) (io.WriteCloser, error) {
return nil, ErrLogPathNotSet
}
var rule RotateRule
switch options.rotationRule {
case sizeRotationRule:
return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays,
options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled)
rule = NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays, options.maxSize,
options.maxBackups, options.gzipEnabled)
default:
return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
options.gzipEnabled), options.gzipEnabled)
rule = DefaultRotateRule(path, backupFileDelimiter, options.keepDays, options.gzipEnabled)
}
return NewLogger(path, rule, options.gzipEnabled)
}
func getWriter() Writer {
@@ -427,44 +473,58 @@ func shallLogStat() bool {
return atomic.LoadUint32(&disableStat) == 0
}
// writeDebug writes v into debug log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeDebug(val any, fields ...LogField) {
if shallLog(DebugLevel) {
getWriter().Debug(val, addCaller(fields...)...)
}
getWriter().Debug(val, addCaller(fields...)...)
}
// writeError writes v into error log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeError(val any, fields ...LogField) {
if shallLog(ErrorLevel) {
getWriter().Error(val, addCaller(fields...)...)
}
getWriter().Error(val, addCaller(fields...)...)
}
// writeInfo writes v into info log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeInfo(val any, fields ...LogField) {
if shallLog(InfoLevel) {
getWriter().Info(val, addCaller(fields...)...)
}
getWriter().Info(val, addCaller(fields...)...)
}
// writeSevere writes v into severe log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeSevere(msg string) {
if shallLog(SevereLevel) {
getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
}
getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
}
// writeSlow writes v into slow log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeSlow(val any, fields ...LogField) {
if shallLog(ErrorLevel) {
getWriter().Slow(val, addCaller(fields...)...)
}
getWriter().Slow(val, addCaller(fields...)...)
}
// writeStack writes v into stack log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeStack(msg string) {
if shallLog(ErrorLevel) {
getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
}
getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
}
// writeStat writes v into stat log.
// Not checking shallLog here is for performance consideration.
// If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled.
// The caller should check shallLog before calling this function.
func writeStat(msg string) {
if shallLogStat() && shallLog(InfoLevel) {
getWriter().Stat(msg, addCaller()...)
}
getWriter().Stat(msg, addCaller()...)
}

View File

@@ -0,0 +1,84 @@
package logtest
import (
"bytes"
"encoding/json"
"io"
"testing"
"github.com/zeromicro/go-zero/core/logx"
)
type Buffer struct {
buf *bytes.Buffer
t *testing.T
}
func Discard(t *testing.T) {
prev := logx.Reset()
logx.SetWriter(logx.NewWriter(io.Discard))
t.Cleanup(func() {
logx.SetWriter(prev)
})
}
func NewCollector(t *testing.T) *Buffer {
var buf bytes.Buffer
writer := logx.NewWriter(&buf)
prev := logx.Reset()
logx.SetWriter(writer)
t.Cleanup(func() {
logx.SetWriter(prev)
})
return &Buffer{
buf: &buf,
t: t,
}
}
func (b *Buffer) Bytes() []byte {
return b.buf.Bytes()
}
func (b *Buffer) Content() string {
var m map[string]interface{}
if err := json.Unmarshal(b.buf.Bytes(), &m); err != nil {
return ""
}
content, ok := m["content"]
if !ok {
return ""
}
switch val := content.(type) {
case string:
return val
default:
// err is impossible to be not nil, unmarshaled from b.buf.Bytes()
bs, _ := json.Marshal(content)
return string(bs)
}
}
func (b *Buffer) Reset() {
b.buf.Reset()
}
func (b *Buffer) String() string {
return b.buf.String()
}
func PanicOnFatal(t *testing.T) {
ok := logx.ExitOnFatal.CompareAndSwap(true, false)
if !ok {
return
}
t.Cleanup(func() {
logx.ExitOnFatal.CompareAndSwap(false, true)
})
}

View File

@@ -0,0 +1,44 @@
package logtest
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
)
func TestCollector(t *testing.T) {
const input = "hello"
c := NewCollector(t)
logx.Info(input)
assert.Equal(t, input, c.Content())
assert.Contains(t, c.String(), input)
c.Reset()
assert.Empty(t, c.Bytes())
}
func TestPanicOnFatal(t *testing.T) {
const input = "hello"
Discard(t)
logx.Info(input)
PanicOnFatal(t)
PanicOnFatal(t)
assert.Panics(t, func() {
logx.Must(errors.New("foo"))
})
}
func TestCollectorContent(t *testing.T) {
const input = "hello"
c := NewCollector(t)
c.buf.WriteString(input)
assert.Empty(t, c.Content())
c.Reset()
c.buf.WriteString(`{}`)
assert.Empty(t, c.Content())
c.Reset()
c.buf.WriteString(`{"content":1}`)
assert.Equal(t, "1", c.Content())
}

View File

@@ -65,7 +65,7 @@ func (l *richLogger) Errorf(format string, v ...any) {
}
func (l *richLogger) Errorv(v any) {
l.err(fmt.Sprint(v))
l.err(v)
}
func (l *richLogger) Errorw(msg string, fields ...LogField) {

View File

@@ -66,6 +66,9 @@ func TestTraceDebug(t *testing.T) {
l.WithDuration(time.Second).Debugv(testlog)
validate(t, w.String(), true, true)
w.Reset()
l.WithDuration(time.Second).Debugv(testobj)
validateContentType(t, w.String(), map[string]any{}, true, true)
w.Reset()
l.WithDuration(time.Second).Debugw(testlog, Field("foo", "bar"))
validate(t, w.String(), true, true)
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
@@ -103,6 +106,9 @@ func TestTraceError(t *testing.T) {
l.WithDuration(time.Second).Errorv(testlog)
validate(t, w.String(), true, true)
w.Reset()
l.WithDuration(time.Second).Errorv(testobj)
validateContentType(t, w.String(), map[string]any{}, true, true)
w.Reset()
l.WithDuration(time.Second).Errorw(testlog, Field("basket", "ball"))
validate(t, w.String(), true, true)
assert.True(t, strings.Contains(w.String(), "basket"), w.String())
@@ -137,6 +143,9 @@ func TestTraceInfo(t *testing.T) {
l.WithDuration(time.Second).Infov(testlog)
validate(t, w.String(), true, true)
w.Reset()
l.WithDuration(time.Second).Infov(testobj)
validateContentType(t, w.String(), map[string]any{}, true, true)
w.Reset()
l.WithDuration(time.Second).Infow(testlog, Field("basket", "ball"))
validate(t, w.String(), true, true)
assert.True(t, strings.Contains(w.String(), "basket"), w.String())
@@ -173,6 +182,9 @@ func TestTraceInfoConsole(t *testing.T) {
w.Reset()
l.WithDuration(time.Second).Infov(testlog)
validate(t, w.String(), true, true)
w.Reset()
l.WithDuration(time.Second).Infov(testobj)
validateContentType(t, w.String(), map[string]any{}, true, true)
}
func TestTraceSlow(t *testing.T) {
@@ -204,6 +216,9 @@ func TestTraceSlow(t *testing.T) {
l.WithDuration(time.Second).Slowv(testlog)
validate(t, w.String(), true, true)
w.Reset()
l.WithDuration(time.Second).Slowv(testobj)
validateContentType(t, w.String(), map[string]any{}, true, true)
w.Reset()
l.WithDuration(time.Second).Sloww(testlog, Field("basket", "ball"))
validate(t, w.String(), true, true)
assert.True(t, strings.Contains(w.String(), "basket"), w.String())
@@ -311,8 +326,32 @@ func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) {
assert.Equal(t, expectedSpan, len(val.Span) > 0, body)
}
type mockValue struct {
Trace string `json:"trace"`
Span string `json:"span"`
Foo string `json:"foo"`
func validateContentType(t *testing.T, body string, expectedType any, expectedTrace, expectedSpan bool) {
var val mockValue
dec := json.NewDecoder(strings.NewReader(body))
for {
var doc mockValue
err := dec.Decode(&doc)
if err == io.EOF {
// all done
break
}
if err != nil {
continue
}
val = doc
}
assert.IsType(t, expectedType, val.Content, body)
assert.Equal(t, expectedTrace, len(val.Trace) > 0, body)
assert.Equal(t, expectedSpan, len(val.Span) > 0, body)
}
type mockValue struct {
Trace string `json:"trace"`
Span string `json:"span"`
Foo string `json:"foo"`
Content any `json:"content"`
}

View File

@@ -4,7 +4,6 @@ import (
"compress/gzip"
"errors"
"fmt"
"io"
"log"
"os"
"path"
@@ -406,7 +405,7 @@ func (l *RotateLogger) write(v []byte) {
func compressLogFile(file string) {
start := time.Now()
Infof("compressing log file: %s", file)
if err := gzipFile(file); err != nil {
if err := gzipFile(file, fileSys); err != nil {
Errorf("compress error: %s", err)
} else {
Infof("compressed log file: %s, took %s", file, time.Since(start))
@@ -421,25 +420,37 @@ func getNowDateInRFC3339Format() string {
return time.Now().Format(fileTimeFormat)
}
func gzipFile(file string) error {
in, err := os.Open(file)
func gzipFile(file string, fsys fileSystem) (err error) {
in, err := fsys.Open(file)
if err != nil {
return err
}
defer in.Close()
defer func() {
if e := fsys.Close(in); e != nil {
Errorf("failed to close file: %s, error: %v", file, e)
}
if err == nil {
// only remove the original file when compression is successful
err = fsys.Remove(file)
}
}()
out, err := os.Create(fmt.Sprintf("%s%s", file, gzipExt))
out, err := fsys.Create(fmt.Sprintf("%s%s", file, gzipExt))
if err != nil {
return err
}
defer out.Close()
defer func() {
e := fsys.Close(out)
if err == nil {
err = e
}
}()
w := gzip.NewWriter(out)
if _, err = io.Copy(w, in); err != nil {
return err
} else if err = w.Close(); err != nil {
if _, err = fsys.Copy(w, in); err != nil {
// failed to copy, no need to close w
return err
}
return os.Remove(file)
return fsys.Close(w)
}

View File

@@ -1,8 +1,12 @@
package logx
import (
"errors"
"io"
"os"
"path"
"path/filepath"
"sync/atomic"
"syscall"
"testing"
"time"
@@ -13,18 +17,58 @@ import (
)
func TestDailyRotateRuleMarkRotated(t *testing.T) {
var rule DailyRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDate(), rule.rotatedTime)
t.Run("daily rule", func(t *testing.T) {
var rule DailyRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDate(), rule.rotatedTime)
})
t.Run("daily rule", func(t *testing.T) {
rule := DefaultRotateRule("test", "-", 1, false)
_, ok := rule.(*DailyRotateRule)
assert.True(t, ok)
})
}
func TestDailyRotateRuleOutdatedFiles(t *testing.T) {
var rule DailyRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
t.Run("no files", func(t *testing.T) {
var rule DailyRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
})
t.Run("bad files", func(t *testing.T) {
rule := DailyRotateRule{
filename: "[a-z",
}
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
})
t.Run("temp files", func(t *testing.T) {
boundary := time.Now().Add(-time.Hour * time.Duration(hoursPerDay) * 2).Format(dateFormat)
f1, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
_ = f1.Close()
f2, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
_ = f2.Close()
t.Cleanup(func() {
_ = os.Remove(f1.Name())
_ = os.Remove(f2.Name())
})
rule := DailyRotateRule{
filename: path.Join(os.TempDir(), "go-zero-test-"),
days: 1,
}
assert.NotEmpty(t, rule.OutdatedFiles())
})
}
func TestDailyRotateRuleShallRotate(t *testing.T) {
@@ -34,20 +78,101 @@ func TestDailyRotateRuleShallRotate(t *testing.T) {
}
func TestSizeLimitRotateRuleMarkRotated(t *testing.T) {
var rule SizeLimitRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDateInRFC3339Format(), rule.rotatedTime)
t.Run("size limit rule", func(t *testing.T) {
var rule SizeLimitRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDateInRFC3339Format(), rule.rotatedTime)
})
t.Run("size limit rule", func(t *testing.T) {
rule := NewSizeLimitRotateRule("foo", "-", 1, 1, 1, false)
rule.MarkRotated()
assert.Equal(t, getNowDateInRFC3339Format(), rule.(*SizeLimitRotateRule).rotatedTime)
})
}
func TestSizeLimitRotateRuleOutdatedFiles(t *testing.T) {
var rule SizeLimitRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
rule.maxBackups = 0
assert.Empty(t, rule.OutdatedFiles())
t.Run("no files", func(t *testing.T) {
var rule SizeLimitRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
rule.maxBackups = 0
assert.Empty(t, rule.OutdatedFiles())
})
t.Run("bad files", func(t *testing.T) {
rule := SizeLimitRotateRule{
DailyRotateRule: DailyRotateRule{
filename: "[a-z",
},
}
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
})
t.Run("temp files", func(t *testing.T) {
boundary := time.Now().Add(-time.Hour * time.Duration(hoursPerDay) * 2).Format(dateFormat)
f1, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
f2, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
boundary1 := time.Now().Add(time.Hour * time.Duration(hoursPerDay) * 2).Format(dateFormat)
f3, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary1)
assert.NoError(t, err)
t.Cleanup(func() {
_ = f1.Close()
_ = os.Remove(f1.Name())
_ = f2.Close()
_ = os.Remove(f2.Name())
_ = f3.Close()
_ = os.Remove(f3.Name())
})
rule := SizeLimitRotateRule{
DailyRotateRule: DailyRotateRule{
filename: path.Join(os.TempDir(), "go-zero-test-"),
days: 1,
},
maxBackups: 3,
}
assert.NotEmpty(t, rule.OutdatedFiles())
})
t.Run("no backups", func(t *testing.T) {
boundary := time.Now().Add(-time.Hour * time.Duration(hoursPerDay) * 2).Format(dateFormat)
f1, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
f2, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary)
assert.NoError(t, err)
boundary1 := time.Now().Add(time.Hour * time.Duration(hoursPerDay) * 2).Format(dateFormat)
f3, err := os.CreateTemp(os.TempDir(), "go-zero-test-"+boundary1)
assert.NoError(t, err)
t.Cleanup(func() {
_ = f1.Close()
_ = os.Remove(f1.Name())
_ = f2.Close()
_ = os.Remove(f2.Name())
_ = f3.Close()
_ = os.Remove(f3.Name())
})
rule := SizeLimitRotateRule{
DailyRotateRule: DailyRotateRule{
filename: path.Join(os.TempDir(), "go-zero-test-"),
days: 1,
},
}
assert.NotEmpty(t, rule.OutdatedFiles())
logger := new(RotateLogger)
logger.rule = &rule
logger.maybeDeleteOutdatedFiles()
assert.Empty(t, rule.OutdatedFiles())
})
}
func TestSizeLimitRotateRuleShallRotate(t *testing.T) {
@@ -61,14 +186,26 @@ func TestSizeLimitRotateRuleShallRotate(t *testing.T) {
}
func TestRotateLoggerClose(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
assert.Nil(t, logger.Close())
t.Run("close", func(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
_, err = logger.Write([]byte("foo"))
assert.Nil(t, err)
assert.Nil(t, logger.Close())
})
t.Run("close and write", func(t *testing.T) {
logger := new(RotateLogger)
logger.done = make(chan struct{})
close(logger.done)
_, err := logger.Write([]byte("foo"))
assert.ErrorIs(t, err, ErrLogFileClosed)
})
}
func TestRotateLoggerGetBackupFilename(t *testing.T) {
@@ -179,7 +316,7 @@ func TestRotateLoggerWithSizeLimitRotateRuleClose(t *testing.T) {
}
logger, err := NewLogger(filename, new(SizeLimitRotateRule), false)
assert.Nil(t, err)
assert.Nil(t, logger.Close())
_ = logger.Close()
}
func TestRotateLoggerGetBackupWithSizeLimitRotateRuleFilename(t *testing.T) {
@@ -295,6 +432,70 @@ func TestRotateLoggerWithSizeLimitRotateRuleWrite(t *testing.T) {
logger.write([]byte(`baz`))
}
func TestGzipFile(t *testing.T) {
err := errors.New("any error")
t.Run("gzip file open failed", func(t *testing.T) {
fsys := &fakeFileSystem{
openFn: func(name string) (*os.File, error) {
return nil, err
},
}
assert.ErrorIs(t, err, gzipFile("any", fsys))
assert.False(t, fsys.Removed())
})
t.Run("gzip file create failed", func(t *testing.T) {
fsys := &fakeFileSystem{
createFn: func(name string) (*os.File, error) {
return nil, err
},
}
assert.ErrorIs(t, err, gzipFile("any", fsys))
assert.False(t, fsys.Removed())
})
t.Run("gzip file copy failed", func(t *testing.T) {
fsys := &fakeFileSystem{
copyFn: func(writer io.Writer, reader io.Reader) (int64, error) {
return 0, err
},
}
assert.ErrorIs(t, err, gzipFile("any", fsys))
assert.False(t, fsys.Removed())
})
t.Run("gzip file last close failed", func(t *testing.T) {
var called int32
fsys := &fakeFileSystem{
closeFn: func(closer io.Closer) error {
if atomic.AddInt32(&called, 1) > 2 {
return err
}
return nil
},
}
assert.NoError(t, gzipFile("any", fsys))
assert.True(t, fsys.Removed())
})
t.Run("gzip file remove failed", func(t *testing.T) {
fsys := &fakeFileSystem{
removeFn: func(name string) error {
return err
},
}
assert.Error(t, err, gzipFile("any", fsys))
assert.True(t, fsys.Removed())
})
t.Run("gzip file everything ok", func(t *testing.T) {
fsys := &fakeFileSystem{}
assert.NoError(t, gzipFile("any", fsys))
assert.True(t, fsys.Removed())
})
}
func BenchmarkRotateLogger(b *testing.B) {
filename := "./test.log"
filename2 := "./test2.log"
@@ -346,3 +547,53 @@ func BenchmarkRotateLogger(b *testing.B) {
}
})
}
type fakeFileSystem struct {
removed int32
closeFn func(closer io.Closer) error
copyFn func(writer io.Writer, reader io.Reader) (int64, error)
createFn func(name string) (*os.File, error)
openFn func(name string) (*os.File, error)
removeFn func(name string) error
}
func (f *fakeFileSystem) Close(closer io.Closer) error {
if f.closeFn != nil {
return f.closeFn(closer)
}
return nil
}
func (f *fakeFileSystem) Copy(writer io.Writer, reader io.Reader) (int64, error) {
if f.copyFn != nil {
return f.copyFn(writer, reader)
}
return 0, nil
}
func (f *fakeFileSystem) Create(name string) (*os.File, error) {
if f.createFn != nil {
return f.createFn(name)
}
return nil, nil
}
func (f *fakeFileSystem) Open(name string) (*os.File, error) {
if f.openFn != nil {
return f.openFn(name)
}
return nil, nil
}
func (f *fakeFileSystem) Remove(name string) error {
atomic.AddInt32(&f.removed, 1)
if f.removeFn != nil {
return f.removeFn(name)
}
return nil
}
func (f *fakeFileSystem) Removed() bool {
return atomic.LoadInt32(&f.removed) > 0
}

View File

@@ -12,6 +12,8 @@ import (
const testlog = "Stay hungry, stay foolish."
var testobj = map[string]any{"foo": "bar"}
func TestCollectSysLog(t *testing.T) {
CollectSysLog()
content := getContent(captureOutput(func() {

View File

@@ -97,6 +97,15 @@ func TestConsoleWriter(t *testing.T) {
w.(*concreteWriter).statLog = easyToCloseWriter{}
}
func TestNewFileWriter(t *testing.T) {
t.Run("access", func(t *testing.T) {
_, err := newFileWriter(LogConf{
Path: "/not-exists",
})
assert.Error(t, err)
})
}
func TestNopWriter(t *testing.T) {
assert.NotPanics(t, func() {
var w nopWriter

View File

@@ -148,14 +148,17 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
return errValueNotSettable
}
baseType := fieldType.Elem()
dereffedBaseType := Deref(baseType)
dereffedBaseKind := dereffedBaseType.Kind()
refValue := reflect.ValueOf(mapValue)
if refValue.Kind() != reflect.Slice {
return errTypeMismatch
}
if refValue.IsNil() {
return nil
}
baseType := fieldType.Elem()
dereffedBaseType := Deref(baseType)
dereffedBaseKind := dereffedBaseType.Kind()
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
if refValue.Len() == 0 {
value.Set(conv)
@@ -173,7 +176,12 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
switch dereffedBaseKind {
case reflect.Struct:
target := reflect.New(dereffedBaseType)
if err := u.Unmarshal(ithValue.(map[string]any), target.Interface()); err != nil {
val, ok := ithValue.(map[string]any)
if !ok {
return errTypeMismatch
}
if err := u.Unmarshal(val, target.Interface()); err != nil {
return err
}
@@ -347,7 +355,12 @@ func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue any)
return emptyValue, errTypeMismatch
}
targetValue.SetMapIndex(key, reflect.ValueOf(v))
val := reflect.ValueOf(v)
if !val.Type().AssignableTo(dereffedElemType) {
return emptyValue, errTypeMismatch
}
targetValue.SetMapIndex(key, val)
case json.Number:
target := reflect.New(dereffedElemType)
if err := setValueFromString(dereffedElemKind, target.Elem(), v.String()); err != nil {
@@ -490,7 +503,7 @@ func (u *Unmarshaler) processAnonymousStructFieldOptional(fieldType reflect.Type
}
if filled && required != requiredFilled {
return fmt.Errorf("%s is not fully set", key)
return fmt.Errorf("%q is not fully set", key)
}
return nil
@@ -513,8 +526,8 @@ func (u *Unmarshaler) processFieldNotFromString(fieldType reflect.Type, value re
vp valueWithParent, opts *fieldOptionsWithContext, fullName string) error {
derefedFieldType := Deref(fieldType)
typeKind := derefedFieldType.Kind()
valueKind := reflect.TypeOf(vp.value).Kind()
mapValue := vp.value
valueKind := reflect.TypeOf(mapValue).Kind()
switch {
case valueKind == reflect.Map && typeKind == reflect.Struct:
@@ -527,6 +540,8 @@ func (u *Unmarshaler) processFieldNotFromString(fieldType reflect.Type, value re
current: mapValuer(mv),
parent: vp.parent,
}, fullName)
case typeKind == reflect.Slice && valueKind == reflect.Slice:
return u.fillSlice(fieldType, value, mapValue)
case valueKind == reflect.Map && typeKind == reflect.Map:
return u.fillMap(fieldType, value, mapValue)
case valueKind == reflect.String && typeKind == reflect.Map:
@@ -545,23 +560,16 @@ func (u *Unmarshaler) processFieldPrimitive(fieldType reflect.Type, value reflec
typeKind := Deref(fieldType).Kind()
valueKind := reflect.TypeOf(mapValue).Kind()
switch {
case typeKind == reflect.Slice && valueKind == reflect.Slice:
return u.fillSlice(fieldType, value, mapValue)
case typeKind == reflect.Map && valueKind == reflect.Map:
return u.fillMap(fieldType, value, mapValue)
switch v := mapValue.(type) {
case json.Number:
return u.processFieldPrimitiveWithJSONNumber(fieldType, value, v, opts, fullName)
default:
switch v := mapValue.(type) {
case json.Number:
return u.processFieldPrimitiveWithJSONNumber(fieldType, value, v, opts, fullName)
default:
if typeKind == valueKind {
if err := validateValueInOptions(mapValue, opts.options()); err != nil {
return err
}
return fillWithSameType(fieldType, value, mapValue, opts)
if typeKind == valueKind {
if err := validateValueInOptions(mapValue, opts.options()); err != nil {
return err
}
return fillWithSameType(fieldType, value, mapValue, opts)
}
}
@@ -610,7 +618,7 @@ func (u *Unmarshaler) processFieldPrimitiveWithJSONNumber(fieldType reflect.Type
target.SetFloat(fValue)
default:
return newTypeMismatchError(fullName)
return newTypeMismatchErrorWithHint(fullName, typeKind.String(), value.Type().String())
}
SetValue(fieldType, value, target)
@@ -723,7 +731,7 @@ func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect
// When fillDefault is used, m is a null value, hasValue must be false, all priority judgments fillDefault.
if u.opts.fillDefault {
if !value.IsZero() {
return fmt.Errorf("set the default value, %s must be zero", fullName)
return fmt.Errorf("set the default value, %q must be zero", fullName)
}
return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName)
} else if !hasValue {
@@ -744,11 +752,11 @@ func (u *Unmarshaler) processNamedFieldWithValue(fieldType reflect.Type, value r
return nil
}
return fmt.Errorf("field %s mustn't be nil", key)
return fmt.Errorf("field %q mustn't be nil", key)
}
if !value.CanSet() {
return fmt.Errorf("field %s is not settable", key)
return fmt.Errorf("field %q is not settable", key)
}
maybeNewValue(fieldType, value)
@@ -792,7 +800,7 @@ func (u *Unmarshaler) processNamedFieldWithValueFromString(fieldType reflect.Typ
}
if !stringx.Contains(options, checkValue) {
return fmt.Errorf(`value "%s" for field "%s" is not defined in options "%v"`,
return fmt.Errorf(`value "%s" for field %q is not defined in options "%v"`,
mapValue, key, options)
}
}
@@ -881,6 +889,11 @@ func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName
typeField := baseType.Field(i)
valueField := valElem.Field(i)
if err := u.processField(typeField, valueField, m, fullName); err != nil {
if len(fullName) > 0 {
err = fmt.Errorf("%w, fullName: %s, field: %s, type: %s",
err, fullName, typeField.Name, valueField.Type().Name())
}
return err
}
}
@@ -1034,11 +1047,16 @@ func join(elem ...string) string {
}
func newInitError(name string) error {
return fmt.Errorf("field %s is not set", name)
return fmt.Errorf("field %q is not set", name)
}
func newTypeMismatchError(name string) error {
return fmt.Errorf("type mismatch for field %s", name)
return fmt.Errorf("type mismatch for field %q", name)
}
func newTypeMismatchErrorWithHint(name, expectType, actualType string) error {
return fmt.Errorf("type mismatch for field %q, expect %q, actual %q",
name, expectType, actualType)
}
func readKeys(key string) []string {

File diff suppressed because it is too large Load Diff

View File

@@ -370,10 +370,8 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error {
fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1]
default:
return fmt.Errorf("field %s has wrong optional", fieldName)
return fmt.Errorf("field %q has wrong optional", fieldName)
}
case option == optionalOption:
fieldOpts.Optional = true
case strings.HasPrefix(option, optionsOption):
val, err := parseProperty(fieldName, optionsOption, option)
if err != nil {
@@ -429,7 +427,7 @@ func parseOptions(val string) []string {
func parseProperty(field, tag, val string) (string, error) {
segs := strings.Split(val, equalToken)
if len(segs) != 2 {
return "", fmt.Errorf("field %s has wrong %s", field, tag)
return "", fmt.Errorf("field %q has wrong tag value %q", field, tag)
}
return strings.TrimSpace(segs[1]), nil
@@ -628,7 +626,7 @@ func validateValueInOptions(val any, options []string) error {
switch v := val.(type) {
case string:
if !stringx.Contains(options, v) {
return fmt.Errorf(`error: value "%s" is not defined in options "%v"`, v, options)
return fmt.Errorf(`error: value %q is not defined in options "%v"`, v, options)
}
default:
if !stringx.Contains(options, Repr(v)) {

View File

@@ -241,7 +241,8 @@ func TestValidatePtrWithZeroValue(t *testing.T) {
func TestSetValueNotSettable(t *testing.T) {
var i int
assert.NotNil(t, setValueFromString(reflect.Int, reflect.ValueOf(i), "1"))
assert.Error(t, setValueFromString(reflect.Int, reflect.ValueOf(i), "1"))
assert.Error(t, validateAndSetValue(reflect.Int, reflect.ValueOf(i), "1", nil))
}
func TestParseKeyAndOptionsErrors(t *testing.T) {
@@ -300,3 +301,36 @@ func TestSetValueFormatErrors(t *testing.T) {
})
}
}
func TestValidateValueRange(t *testing.T) {
t.Run("float", func(t *testing.T) {
assert.NoError(t, validateValueRange(1.2, nil))
})
t.Run("float number range", func(t *testing.T) {
assert.NoError(t, validateNumberRange(1.2, nil))
})
t.Run("bad float", func(t *testing.T) {
assert.Error(t, validateValueRange("a", &fieldOptionsWithContext{
Range: &numberRange{},
}))
})
t.Run("bad float validate", func(t *testing.T) {
var v struct {
Foo float32
}
assert.Error(t, validateAndSetValue(reflect.Int, reflect.ValueOf(&v).Elem().Field(0),
"1", &fieldOptionsWithContext{
Range: &numberRange{
left: 2,
right: 3,
},
}))
})
}
func TestSetMatchedPrimitiveValue(t *testing.T) {
assert.Error(t, setMatchedPrimitiveValue(reflect.Func, reflect.ValueOf(2), "1"))
}

View File

@@ -3,7 +3,7 @@ package mr
import (
"context"
"errors"
"io/ioutil"
"io"
"log"
"runtime"
"sync/atomic"
@@ -17,7 +17,7 @@ import (
var errDummy = errors.New("dummy")
func init() {
log.SetOutput(ioutil.Discard)
log.SetOutput(io.Discard)
}
func TestFinish(t *testing.T) {
@@ -574,6 +574,7 @@ func TestMapReduceWithContext(t *testing.T) {
cancel()
}
writer.Write(i)
time.Sleep(time.Millisecond)
}, func(pipe <-chan int, cancel func(error)) {
for item := range pipe {
i := item

View File

@@ -1,7 +1,6 @@
package proc
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -21,13 +20,11 @@ func TestEnvInt(t *testing.T) {
val, ok := EnvInt("any")
assert.Equal(t, 0, val)
assert.False(t, ok)
err := os.Setenv("anyInt", "10")
assert.Nil(t, err)
t.Setenv("anyInt", "10")
val, ok = EnvInt("anyInt")
assert.Equal(t, 10, val)
assert.True(t, ok)
err = os.Setenv("anyString", "a")
assert.Nil(t, err)
t.Setenv("anyString", "a")
val, ok = EnvInt("anyString")
assert.Equal(t, 0, val)
assert.False(t, ok)

View File

@@ -1,6 +0,0 @@
//go:build windows
package proc
func dumpGoroutines() {
}

View File

@@ -18,7 +18,11 @@ const (
debugLevel = 2
)
func dumpGoroutines() {
type creator interface {
Create(name string) (file *os.File, err error)
}
func dumpGoroutines(ctor creator) {
command := path.Base(os.Args[0])
pid := syscall.Getpid()
dumpFile := path.Join(os.TempDir(), fmt.Sprintf("%s-%d-goroutines-%s.dump",
@@ -26,10 +30,16 @@ func dumpGoroutines() {
logx.Infof("Got dump goroutine signal, printing goroutine profile to %s", dumpFile)
if f, err := os.Create(dumpFile); err != nil {
if f, err := ctor.Create(dumpFile); err != nil {
logx.Errorf("Failed to dump goroutine profile, error: %v", err)
} else {
defer f.Close()
pprof.Lookup(goroutineProfile).WriteTo(f, debugLevel)
}
}
type fileCreator struct{}
func (fc fileCreator) Create(name string) (file *os.File, err error) {
return os.Create(name)
}

View File

@@ -1,23 +1,41 @@
//go:build linux || darwin
package proc
import (
"errors"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
)
func TestDumpGoroutines(t *testing.T) {
var buf strings.Builder
w := logx.NewWriter(&buf)
o := logx.Reset()
logx.SetWriter(w)
defer func() {
logx.Reset()
logx.SetWriter(o)
}()
t.Run("real file", func(t *testing.T) {
buf := logtest.NewCollector(t)
dumpGoroutines(fileCreator{})
assert.True(t, strings.Contains(buf.String(), ".dump"))
})
dumpGoroutines()
assert.True(t, strings.Contains(buf.String(), ".dump"))
t.Run("fake file", func(t *testing.T) {
const msg = "any message"
buf := logtest.NewCollector(t)
err := errors.New(msg)
dumpGoroutines(fakeCreator{
file: &os.File{},
err: err,
})
assert.True(t, strings.Contains(buf.String(), msg))
})
}
type fakeCreator struct {
file *os.File
err error
}
func (fc fakeCreator) Create(name string) (file *os.File, err error) {
return fc.file, fc.err
}

View File

@@ -5,25 +5,16 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
)
func TestProfile(t *testing.T) {
var buf strings.Builder
w := logx.NewWriter(&buf)
o := logx.Reset()
logx.SetWriter(w)
defer func() {
logx.Reset()
logx.SetWriter(o)
}()
c := logtest.NewCollector(t)
profiler := StartProfile()
// start again should not work
assert.NotNil(t, StartProfile())
profiler.Stop()
// stop twice
profiler.Stop()
assert.True(t, strings.Contains(buf.String(), ".pprof"))
assert.True(t, strings.Contains(c.String(), ".pprof"))
}

View File

@@ -26,7 +26,7 @@ func init() {
v := <-signals
switch v {
case syscall.SIGUSR1:
dumpGoroutines()
dumpGoroutines(fileCreator{})
case syscall.SIGUSR2:
if profiler == nil {
profiler = StartProfile()

View File

@@ -1,3 +1,5 @@
//go:build linux || darwin
package proc
import (

View File

@@ -2,6 +2,8 @@ package prof
import (
"fmt"
"io"
"os"
"runtime"
"time"
)
@@ -13,6 +15,10 @@ const (
// DisplayStats prints the goroutine, memory, GC stats with given interval, default to 5 seconds.
func DisplayStats(interval ...time.Duration) {
displayStatsWithWriter(os.Stdout, interval...)
}
func displayStatsWithWriter(writer io.Writer, interval ...time.Duration) {
duration := defaultInterval
for _, val := range interval {
duration = val
@@ -24,7 +30,7 @@ func DisplayStats(interval ...time.Duration) {
for range ticker.C {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Goroutines: %d, Alloc: %vm, TotalAlloc: %vm, Sys: %vm, NumGC: %v\n",
fmt.Fprintf(writer, "Goroutines: %d, Alloc: %vm, TotalAlloc: %vm, Sys: %vm, NumGC: %v\n",
runtime.NumGoroutine(), m.Alloc/mega, m.TotalAlloc/mega, m.Sys/mega, m.NumGC)
}
}()

36
core/prof/runtime_test.go Normal file
View File

@@ -0,0 +1,36 @@
package prof
import (
"strings"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestDisplayStats(t *testing.T) {
writer := &threadSafeBuffer{
buf: strings.Builder{},
}
displayStatsWithWriter(writer, time.Millisecond*10)
time.Sleep(time.Millisecond * 50)
assert.Contains(t, writer.String(), "Goroutines: ")
}
type threadSafeBuffer struct {
buf strings.Builder
lock sync.Mutex
}
func (b *threadSafeBuffer) String() string {
b.lock.Lock()
defer b.lock.Unlock()
return b.buf.String()
}
func (b *threadSafeBuffer) Write(p []byte) (n int, err error) {
b.lock.Lock()
defer b.lock.Unlock()
return b.buf.Write(p)
}

View File

@@ -21,6 +21,11 @@ func Enabled() bool {
return enabled.True()
}
// Enable enables prometheus.
func Enable() {
enabled.Set(true)
}
// StartAgent starts a prometheus agent.
func StartAgent(c Config) {
if len(c.Host) == 0 {

View File

@@ -1,6 +1,8 @@
package queue
import (
"errors"
"math"
"sync"
"sync/atomic"
"testing"
@@ -37,10 +39,82 @@ func TestQueue(t *testing.T) {
assert.Equal(t, int32(rounds), atomic.LoadInt32(&consumer.count))
}
func TestQueue_Broadcast(t *testing.T) {
producer := newMockedProducer(math.MaxInt32)
consumer := newMockedConsumer()
consumer.wait.Add(consumers)
q := NewQueue(func() (Producer, error) {
return producer, nil
}, func() (Consumer, error) {
return consumer, nil
})
q.AddListener(new(mockedListener))
q.SetName("mockqueue")
q.SetNumConsumer(consumers)
q.SetNumProducer(1)
go func() {
time.Sleep(time.Millisecond * 100)
q.Stop()
}()
go q.Start()
time.Sleep(time.Millisecond * 50)
q.Broadcast("message")
consumer.wait.Wait()
assert.Equal(t, int32(consumers), atomic.LoadInt32(&consumer.events))
}
func TestQueue_PauseResume(t *testing.T) {
producer := newMockedProducer(rounds)
consumer := newMockedConsumer()
consumer.wait.Add(consumers)
q := NewQueue(func() (Producer, error) {
return producer, nil
}, func() (Consumer, error) {
return consumer, nil
})
q.AddListener(new(mockedListener))
q.SetName("mockqueue")
q.SetNumConsumer(consumers)
q.SetNumProducer(1)
go func() {
producer.wait.Wait()
q.Stop()
}()
q.Start()
producer.listener.OnProducerPause()
assert.Equal(t, int32(0), atomic.LoadInt32(&q.active))
producer.listener.OnProducerResume()
assert.Equal(t, int32(1), atomic.LoadInt32(&q.active))
assert.Equal(t, int32(rounds), atomic.LoadInt32(&consumer.count))
}
func TestQueue_ConsumeError(t *testing.T) {
producer := newMockedProducer(rounds)
consumer := newMockedConsumer()
consumer.consumeErr = errors.New("consume error")
consumer.wait.Add(consumers)
q := NewQueue(func() (Producer, error) {
return producer, nil
}, func() (Consumer, error) {
return consumer, nil
})
q.AddListener(new(mockedListener))
q.SetName("mockqueue")
q.SetNumConsumer(consumers)
q.SetNumProducer(1)
go func() {
producer.wait.Wait()
q.Stop()
}()
q.Start()
assert.Equal(t, int32(rounds), atomic.LoadInt32(&consumer.count))
}
type mockedConsumer struct {
count int32
events int32
wait sync.WaitGroup
count int32
events int32
consumeErr error
wait sync.WaitGroup
}
func newMockedConsumer() *mockedConsumer {
@@ -49,7 +123,7 @@ func newMockedConsumer() *mockedConsumer {
func (c *mockedConsumer) Consume(string) error {
atomic.AddInt32(&c.count, 1)
return nil
return c.consumeErr
}
func (c *mockedConsumer) OnEvent(any) {
@@ -59,9 +133,10 @@ func (c *mockedConsumer) OnEvent(any) {
}
type mockedProducer struct {
total int32
count int32
wait sync.WaitGroup
total int32
count int32
listener ProduceListener
wait sync.WaitGroup
}
func newMockedProducer(total int32) *mockedProducer {
@@ -72,6 +147,7 @@ func newMockedProducer(total int32) *mockedProducer {
}
func (p *mockedProducer) AddListener(listener ProduceListener) {
p.listener = listener
}
func (p *mockedProducer) Produce() (string, bool) {

View File

@@ -1,6 +1,11 @@
package rescue
import "github.com/zeromicro/go-zero/core/logx"
import (
"context"
"runtime/debug"
"github.com/zeromicro/go-zero/core/logx"
)
// Recover is used with defer to do cleanup on panics.
// Use it like:
@@ -15,3 +20,14 @@ func Recover(cleanups ...func()) {
logx.ErrorStack(p)
}
}
// RecoverCtx is used with defer to do cleanup on panics.
func RecoverCtx(ctx context.Context, cleanups ...func()) {
for _, cleanup := range cleanups {
cleanup()
}
if p := recover(); p != nil {
logx.WithContext(ctx).Errorf("%+v\n%s", p, debug.Stack())
}
}

View File

@@ -1,6 +1,7 @@
package rescue
import (
"context"
"sync/atomic"
"testing"
@@ -25,3 +26,17 @@ func TestRescue(t *testing.T) {
})
assert.Equal(t, int32(5), atomic.LoadInt32(&count))
}
func TestRescueCtx(t *testing.T) {
var count int32
assert.NotPanics(t, func() {
defer RecoverCtx(context.Background(), func() {
atomic.AddInt32(&count, 2)
}, func() {
atomic.AddInt32(&count, 3)
})
panic("hello")
})
assert.Equal(t, int32(5), atomic.LoadInt32(&count))
}

View File

@@ -171,11 +171,11 @@ func add(nd *node, route string, item any) error {
token := route[:i]
children := nd.getChildren(token)
if child, ok := children[token]; ok {
if child != nil {
return add(child, route[i+1:], item)
if child == nil {
return errInvalidState
}
return errInvalidState
return add(child, route[i+1:], item)
}
child := newNode(nil)

View File

@@ -11,7 +11,7 @@ import (
type mockedRoute struct {
route string
value int
value any
}
func TestSearch(t *testing.T) {
@@ -187,6 +187,12 @@ func TestSearchInvalidItem(t *testing.T) {
assert.Equal(t, errEmptyItem, err)
}
func TestSearchInvalidState(t *testing.T) {
nd := newNode("0")
nd.children[0]["1"] = nil
assert.Error(t, add(nd, "1/2", "2"))
}
func BenchmarkSearchTree(b *testing.B) {
const (
avgLen = 1000

View File

@@ -1,8 +1,6 @@
package service
import (
"log"
"github.com/zeromicro/go-zero/core/load"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/proc"
@@ -39,9 +37,7 @@ type ServiceConf struct {
// MustSetUp sets up the service, exits on error.
func (sc ServiceConf) MustSetUp() {
if err := sc.SetUp(); err != nil {
log.Fatal(err)
}
logx.Must(sc.SetUp())
}
// SetUp sets up the service.

View File

@@ -68,7 +68,7 @@ func (sg *ServiceGroup) doStart() {
for i := range sg.services {
service := sg.services[i]
routineGroup.RunSafe(func() {
routineGroup.Run(func() {
service.Start()
})
}

View File

@@ -14,30 +14,6 @@ var (
done = make(chan struct{})
)
type mockedService struct {
quit chan struct{}
multiplier int
}
func newMockedService(multiplier int) *mockedService {
return &mockedService{
quit: make(chan struct{}),
multiplier: multiplier,
}
}
func (s *mockedService) Start() {
mutex.Lock()
number *= s.multiplier
mutex.Unlock()
done <- struct{}{}
<-s.quit
}
func (s *mockedService) Stop() {
close(s.quit)
}
func TestServiceGroup(t *testing.T) {
multipliers := []int{2, 3, 5, 7}
want := 1
@@ -126,3 +102,27 @@ type mockedStarter struct {
func (s mockedStarter) Start() {
s.fn()
}
type mockedService struct {
quit chan struct{}
multiplier int
}
func newMockedService(multiplier int) *mockedService {
return &mockedService{
quit: make(chan struct{}),
multiplier: multiplier,
}
}
func (s *mockedService) Start() {
mutex.Lock()
number *= s.multiplier
mutex.Unlock()
done <- struct{}{}
<-s.quit
}
func (s *mockedService) Stop() {
close(s.quit)
}

View File

@@ -3,7 +3,6 @@
package stat
import (
"os"
"strconv"
"sync/atomic"
"testing"
@@ -12,8 +11,7 @@ import (
)
func TestReport(t *testing.T) {
os.Setenv(clusterNameKey, "test-cluster")
defer os.Unsetenv(clusterNameKey)
t.Setenv(clusterNameKey, "test-cluster")
var count int32
SetReporter(func(s string) {

View File

@@ -3,6 +3,7 @@ package internal
import (
"bufio"
"fmt"
"math"
"os"
"path"
"strconv"
@@ -218,6 +219,7 @@ func parseUints(val string) ([]uint64, error) {
return nil, nil
}
var sets []uint64
ints := make(map[uint64]lang.PlaceholderType)
cols := strings.Split(val, ",")
for _, r := range cols {
@@ -238,7 +240,10 @@ func parseUints(val string) ([]uint64, error) {
}
for i := min; i <= max; i++ {
ints[i] = lang.Placeholder
if _, ok := ints[i]; !ok {
ints[i] = lang.Placeholder
sets = append(sets, i)
}
}
} else {
v, err := parseUint(r)
@@ -246,19 +251,17 @@ func parseUints(val string) ([]uint64, error) {
return nil, err
}
ints[v] = lang.Placeholder
if _, ok := ints[v]; !ok {
ints[v] = lang.Placeholder
sets = append(sets, v)
}
}
}
var sets []uint64
for k := range ints {
sets = append(sets, k)
}
return sets, nil
}
// runningInUserNS detects whether we are currently running in an user namespace.
// runningInUserNS detects whether we are currently running in a user namespace.
func runningInUserNS() bool {
nsOnce.Do(func() {
file, err := os.Open("/proc/self/uid_map")
@@ -280,9 +283,10 @@ func runningInUserNS() bool {
// We assume we are in the initial user namespace if we have a full
// range - 4294967295 uids starting at uid 0.
if a == 0 && b == 0 && c == 4294967295 {
if a == 0 && b == 0 && c == math.MaxUint32 {
return
}
inUserNS = true
})

View File

@@ -0,0 +1,71 @@
package internal
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRunningInUserNS(t *testing.T) {
// should be false in docker
assert.False(t, runningInUserNS())
}
func TestCgroupV1(t *testing.T) {
if isCgroup2UnifiedMode() {
cg, err := currentCgroupV1()
assert.NoError(t, err)
_, err = cg.cpus()
assert.Error(t, err)
_, err = cg.cpuPeriodUs()
assert.Error(t, err)
_, err = cg.cpuQuotaUs()
assert.Error(t, err)
_, err = cg.usageAllCpus()
assert.Error(t, err)
}
}
func TestParseUint(t *testing.T) {
tests := []struct {
input string
want uint64
err error
}{
{"0", 0, nil},
{"123", 123, nil},
{"-1", 0, nil},
{"-18446744073709551616", 0, nil},
{"foo", 0, fmt.Errorf("cgroup: bad int format: foo")},
}
for _, tt := range tests {
got, err := parseUint(tt.input)
assert.Equal(t, tt.err, err)
assert.Equal(t, tt.want, got)
}
}
func TestParseUints(t *testing.T) {
tests := []struct {
input string
want []uint64
err error
}{
{"", nil, nil},
{"1,2,3", []uint64{1, 2, 3}, nil},
{"1-3", []uint64{1, 2, 3}, nil},
{"1-3,5,7-9", []uint64{1, 2, 3, 5, 7, 8, 9}, nil},
{"foo", nil, fmt.Errorf("cgroup: bad int format: foo")},
{"1-bar", nil, fmt.Errorf("cgroup: bad int list format: 1-bar")},
{"bar-3", nil, fmt.Errorf("cgroup: bad int list format: bar-3")},
{"3-1", nil, fmt.Errorf("cgroup: bad int list format: 3-1")},
}
for _, tt := range tests {
got, err := parseUints(tt.input)
assert.Equal(t, tt.err, err)
assert.Equal(t, tt.want, got)
}
}

View File

@@ -141,7 +141,7 @@ func (c *metricsContainer) Execute(v any) {
report.Median = float32(medianTask.Duration) / float32(time.Millisecond)
tenPercent := fiftyPercent / 5
if tenPercent > 0 {
top10pTasks := topK(tasks, tenPercent)
top10pTasks := topK(top50pTasks, tenPercent)
task90th := top10pTasks[0]
report.Top90th = float32(task90th.Duration) / float32(time.Millisecond)
onePercent := tenPercent / 10
@@ -163,7 +163,7 @@ func (c *metricsContainer) Execute(v any) {
report.Top99p9th = mostDuration
}
} else {
mostDuration := getTopDuration(tasks)
mostDuration := getTopDuration(top50pTasks)
report.Top90th = mostDuration
report.Top99th = mostDuration
report.Top99p9th = mostDuration

View File

@@ -1,12 +1,11 @@
package stat
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
)
func TestBToMb(t *testing.T) {
@@ -41,15 +40,11 @@ func TestBToMb(t *testing.T) {
}
func TestPrintUsage(t *testing.T) {
var buf bytes.Buffer
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
c := logtest.NewCollector(t)
printUsage()
output := buf.String()
output := c.String()
assert.Contains(t, output, "CPU:")
assert.Contains(t, output, "MEMORY:")
assert.Contains(t, output, "Alloc=")

View File

@@ -69,3 +69,62 @@ func TestFieldNamesWithDashTagAndOptions(t *testing.T) {
assert.Equal(t, expected, out)
})
}
func TestPostgreSqlJoin(t *testing.T) {
// Test with empty input array
var input []string
var expectedOutput string
assert.Equal(t, expectedOutput, PostgreSqlJoin(input))
// Test with single element input array
input = []string{"foo"}
expectedOutput = "foo = $2"
assert.Equal(t, expectedOutput, PostgreSqlJoin(input))
// Test with multiple elements input array
input = []string{"foo", "bar", "baz"}
expectedOutput = "foo = $2, bar = $3, baz = $4"
assert.Equal(t, expectedOutput, PostgreSqlJoin(input))
}
type testStruct struct {
Foo string `db:"foo"`
Bar int `db:"bar"`
Baz bool `db:"-"`
}
func TestRawFieldNames(t *testing.T) {
// Test with a struct without tags
in := struct {
Foo string
Bar int
}{}
expectedOutput := []string{"`Foo`", "`Bar`"}
assert.ElementsMatch(t, expectedOutput, RawFieldNames(in))
// Test pg without db tag
expectedOutput = []string{"Foo", "Bar"}
assert.ElementsMatch(t, expectedOutput, RawFieldNames(in, true))
// Test with a struct with tags
input := testStruct{}
expectedOutput = []string{"`foo`", "`bar`"}
assert.ElementsMatch(t, expectedOutput, RawFieldNames(input))
// Test with nil input (pointer)
var nilInput *testStruct
assert.Panics(t, func() {
RawFieldNames(nilInput)
}, "RawFieldNames should panic with nil input")
// Test with non-struct input
inputInt := 42
assert.Panics(t, func() {
RawFieldNames(inputInt)
}, "RawFieldNames should panic with non-struct input")
// Test with PostgreSQL flag
input = testStruct{}
expectedOutput = []string{"foo", "bar"}
assert.ElementsMatch(t, expectedOutput, RawFieldNames(input, true))
}

View File

@@ -1,6 +1,3 @@
//go:build !race
// Disable data race detection is because of the timingWheel in cacheNode.
package cache
import (
@@ -34,8 +31,10 @@ func init() {
func TestCacheNode_DelCache(t *testing.T) {
t.Run("del cache", func(t *testing.T) {
store := redistest.CreateRedis(t)
store.Type = redis.ClusterType
r, err := miniredis.Run()
assert.NoError(t, err)
defer r.Close()
store := redis.New(r.Addr(), redis.Cluster())
cn := cacheNode{
rds: store,
@@ -56,16 +55,16 @@ func TestCacheNode_DelCache(t *testing.T) {
})
t.Run("del cache with errors", func(t *testing.T) {
old := timingWheel
old := timingWheel.Load()
ticker := timex.NewFakeTicker()
var err error
timingWheel, err = collection.NewTimingWheelWithTicker(
tw, err := collection.NewTimingWheelWithTicker(
time.Millisecond, timingWheelSlots, func(key, value any) {
clean(key, value)
}, ticker)
timingWheel.Store(tw)
assert.NoError(t, err)
t.Cleanup(func() {
timingWheel = old
timingWheel.Store(old)
})
r, err := miniredis.Run()
@@ -166,40 +165,99 @@ func TestCacheNode_TakeBadRedis(t *testing.T) {
}
func TestCacheNode_TakeNotFound(t *testing.T) {
store := redistest.CreateRedis(t)
t.Run("not found", func(t *testing.T) {
store := redistest.CreateRedis(t)
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSingleFlight(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewStat("any"),
errNotFound: errTestNotFound,
}
var str string
err := cn.Take(&str, "any", func(v any) error {
return errTestNotFound
})
assert.True(t, cn.IsNotFound(err))
assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `*`, val)
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSingleFlight(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewStat("any"),
errNotFound: errTestNotFound,
}
var str string
err := cn.Take(&str, "any", func(v any) error {
return errTestNotFound
})
assert.True(t, cn.IsNotFound(err))
assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `*`, val)
store.Set("any", "*")
err = cn.Take(&str, "any", func(v any) error {
return nil
})
assert.True(t, cn.IsNotFound(err))
assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
store.Set("any", "*")
err = cn.Take(&str, "any", func(v any) error {
return nil
})
assert.True(t, cn.IsNotFound(err))
assert.True(t, cn.IsNotFound(cn.Get("any", &str)))
store.Del("any")
errDummy := errors.New("dummy")
err = cn.Take(&str, "any", func(v any) error {
return errDummy
store.Del("any")
errDummy := errors.New("dummy")
err = cn.Take(&str, "any", func(v any) error {
return errDummy
})
assert.Equal(t, errDummy, err)
})
t.Run("not found with redis error", func(t *testing.T) {
r, err := miniredis.Run()
assert.NoError(t, err)
defer r.Close()
store, err := redis.NewRedis(redis.RedisConf{
Host: r.Addr(),
Type: redis.NodeType,
})
assert.NoError(t, err)
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSingleFlight(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewStat("any"),
errNotFound: errTestNotFound,
}
var str string
err = cn.Take(&str, "any", func(v any) error {
r.SetError("mock error")
return errTestNotFound
})
assert.True(t, cn.IsNotFound(err))
})
}
func TestCacheNode_TakeCtxWithRedisError(t *testing.T) {
t.Run("not found with redis error", func(t *testing.T) {
r, err := miniredis.Run()
assert.NoError(t, err)
defer r.Close()
store, err := redis.NewRedis(redis.RedisConf{
Host: r.Addr(),
Type: redis.NodeType,
})
assert.NoError(t, err)
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSingleFlight(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewStat("any"),
errNotFound: errTestNotFound,
}
var str string
err = cn.Take(&str, "any", func(v any) error {
str = "foo"
r.SetError("mock error")
return nil
})
assert.NoError(t, err)
})
assert.Equal(t, errDummy, err)
}
func TestCacheNode_TakeNotFoundButChangedByOthers(t *testing.T) {

28
core/stores/cache/cacheopt_test.go vendored Normal file
View File

@@ -0,0 +1,28 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCacheOptions(t *testing.T) {
t.Run("default options", func(t *testing.T) {
o := newOptions()
assert.Equal(t, defaultExpiry, o.Expiry)
assert.Equal(t, defaultNotFoundExpiry, o.NotFoundExpiry)
})
t.Run("with expiry", func(t *testing.T) {
o := newOptions(WithExpiry(time.Second))
assert.Equal(t, time.Second, o.Expiry)
assert.Equal(t, defaultNotFoundExpiry, o.NotFoundExpiry)
})
t.Run("with not found expiry", func(t *testing.T) {
o := newOptions(WithNotFoundExpiry(time.Second))
assert.Equal(t, defaultExpiry, o.Expiry)
assert.Equal(t, time.Second, o.NotFoundExpiry)
})
}

View File

@@ -2,6 +2,7 @@ package cache
import (
"fmt"
"sync/atomic"
"time"
"github.com/zeromicro/go-zero/core/collection"
@@ -19,7 +20,8 @@ const (
)
var (
timingWheel *collection.TimingWheel
// use atomic to avoid data race in unit tests
timingWheel atomic.Value
taskRunner = threading.NewTaskRunner(cleanWorkers)
)
@@ -30,22 +32,27 @@ type delayTask struct {
}
func init() {
var err error
timingWheel, err = collection.NewTimingWheel(time.Second, timingWheelSlots, clean)
tw, err := collection.NewTimingWheel(time.Second, timingWheelSlots, clean)
logx.Must(err)
timingWheel.Store(tw)
proc.AddShutdownListener(func() {
timingWheel.Drain(clean)
if err := tw.Drain(clean); err != nil {
logx.Errorf("failed to drain timing wheel: %v", err)
}
})
}
// AddCleanTask adds a clean task on given keys.
func AddCleanTask(task func() error, keys ...string) {
timingWheel.SetTimer(stringx.Randn(taskKeyLen), delayTask{
tw := timingWheel.Load().(*collection.TimingWheel)
if err := tw.SetTimer(stringx.Randn(taskKeyLen), delayTask{
delay: time.Second,
task: task,
keys: keys,
}, time.Second)
}, time.Second); err != nil {
logx.Errorf("failed to set timer for keys: %q, error: %v", formatKeys(keys), err)
}
}
func clean(key, value any) {
@@ -59,7 +66,10 @@ func clean(key, value any) {
next, ok := nextDelay(dt.delay)
if ok {
dt.delay = next
timingWheel.SetTimer(key, dt, next)
tw := timingWheel.Load().(*collection.TimingWheel)
if err = tw.SetTimer(key, dt, next); err != nil {
logx.Errorf("failed to set timer for key: %s, error: %v", key, err)
}
} else {
msg := fmt.Sprintf("retried but failed to clear cache with keys: %q, error: %v",
formatKeys(dt.keys), err)

View File

@@ -5,7 +5,9 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/collection"
"github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/timex"
)
func TestNextDelay(t *testing.T) {
@@ -49,6 +51,18 @@ func TestNextDelay(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
old := timingWheel.Load()
ticker := timex.NewFakeTicker()
tw, err := collection.NewTimingWheelWithTicker(
time.Millisecond, timingWheelSlots, func(key, value any) {
clean(key, value)
}, ticker)
timingWheel.Store(tw)
assert.NoError(t, err)
t.Cleanup(func() {
timingWheel.Store(old)
})
next, ok := nextDelay(test.input)
assert.Equal(t, test.ok, ok)
assert.Equal(t, test.output, next)

View File

@@ -3,12 +3,11 @@ package mon
import (
"context"
"errors"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
"github.com/zeromicro/go-zero/core/stringx"
"github.com/zeromicro/go-zero/core/timex"
"go.mongodb.org/mongo-driver/bson"
@@ -573,15 +572,7 @@ func TestDecoratedCollection_LogDuration(t *testing.T) {
brk: breaker.NewBreaker(),
}
var buf strings.Builder
w := logx.NewWriter(&buf)
o := logx.Reset()
logx.SetWriter(w)
defer func() {
logx.Reset()
logx.SetWriter(o)
}()
buf := logtest.NewCollector(t)
buf.Reset()
c.logDuration(context.Background(), "foo", timex.Now(), nil, "bar")

View File

@@ -2,10 +2,10 @@ package mon
import (
"context"
"log"
"strings"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/timex"
"go.mongodb.org/mongo-driver/mongo"
mopt "go.mongodb.org/mongo-driver/mongo/options"
@@ -39,10 +39,7 @@ type (
// MustNewModel returns a Model, exits on errors.
func MustNewModel(uri, db, collection string, opts ...Option) *Model {
model, err := NewModel(uri, db, collection, opts...)
if err != nil {
log.Fatal(err)
}
logx.Must(err)
return model
}

View File

@@ -3,12 +3,11 @@ package mon
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
)
func TestFormatAddrs(t *testing.T) {
@@ -40,15 +39,7 @@ func TestFormatAddrs(t *testing.T) {
}
func Test_logDuration(t *testing.T) {
var buf strings.Builder
w := logx.NewWriter(&buf)
o := logx.Reset()
logx.SetWriter(w)
defer func() {
logx.Reset()
logx.SetWriter(o)
}()
buf := logtest.NewCollector(t)
buf.Reset()
logDuration(context.Background(), "foo", "bar", time.Millisecond, nil)

View File

@@ -2,8 +2,8 @@ package monc
import (
"context"
"log"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/mon"
"github.com/zeromicro/go-zero/core/stores/redis"
@@ -30,20 +30,14 @@ type Model struct {
// MustNewModel returns a Model with a cache cluster, exists on errors.
func MustNewModel(uri, db, collection string, c cache.CacheConf, opts ...cache.Option) *Model {
model, err := NewModel(uri, db, collection, c, opts...)
if err != nil {
log.Fatal(err)
}
logx.Must(err)
return model
}
// MustNewNodeModel returns a Model with a cache node, exists on errors.
func MustNewNodeModel(uri, db, collection string, rds *redis.Redis, opts ...cache.Option) *Model {
model, err := NewNodeModel(uri, db, collection, rds, opts...)
if err != nil {
log.Fatal(err)
}
logx.Must(err)
return model
}

View File

@@ -56,7 +56,7 @@ func (h hook) AfterProcess(ctx context.Context, cmd red.Cmder) error {
logDuration(ctx, []red.Cmder{cmd}, duration)
}
metricReqDur.Observe(int64(duration/time.Millisecond), cmd.Name())
metricReqDur.Observe(duration.Milliseconds(), cmd.Name())
if msg := formatError(err); len(msg) > 0 {
metricReqErr.Inc(cmd.Name(), msg)
}
@@ -103,7 +103,7 @@ func (h hook) AfterProcessPipeline(ctx context.Context, cmds []red.Cmder) error
logDuration(ctx, cmds, duration)
}
metricReqDur.Observe(int64(duration/time.Millisecond), "Pipeline")
metricReqDur.Observe(duration.Milliseconds(), "Pipeline")
if msg := formatError(batchError.Err()); len(msg) > 0 {
metricReqErr.Inc("Pipeline", msg)
}

View File

@@ -2,14 +2,18 @@ package redis
import (
"context"
"errors"
"io"
"log"
"net"
"strings"
"testing"
"time"
red "github.com/go-redis/redis/v8"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx/logtest"
ztrace "github.com/zeromicro/go-zero/core/trace"
tracesdk "go.opentelemetry.io/otel/trace"
)
@@ -47,8 +51,7 @@ func TestHookProcessCase2(t *testing.T) {
})
defer ztrace.StopAgent()
w, restore := injectLog()
defer restore()
w := logtest.NewCollector(t)
ctx, err := durationHook.BeforeProcess(context.Background(), red.NewCmd(context.Background()))
if err != nil {
@@ -115,8 +118,7 @@ func TestHookProcessPipelineCase2(t *testing.T) {
})
defer ztrace.StopAgent()
w, restore := injectLog()
defer restore()
w := logtest.NewCollector(t)
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), []red.Cmder{
red.NewCmd(context.Background()),
@@ -135,8 +137,7 @@ func TestHookProcessPipelineCase2(t *testing.T) {
}
func TestHookProcessPipelineCase3(t *testing.T) {
w, restore := injectLog()
defer restore()
w := logtest.NewCollector(t)
assert.Nil(t, durationHook.AfterProcessPipeline(context.Background(), []red.Cmder{
red.NewCmd(context.Background()),
@@ -145,8 +146,7 @@ func TestHookProcessPipelineCase3(t *testing.T) {
}
func TestHookProcessPipelineCase4(t *testing.T) {
w, restore := injectLog()
defer restore()
w := logtest.NewCollector(t)
ctx := context.WithValue(context.Background(), startTimeKey, "foo")
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
@@ -169,8 +169,7 @@ func TestHookProcessPipelineCase5(t *testing.T) {
}
func TestLogDuration(t *testing.T) {
w, restore := injectLog()
defer restore()
w := logtest.NewCollector(t)
logDuration(context.Background(), []red.Cmder{
red.NewCmd(context.Background(), "get", "foo"),
@@ -184,14 +183,39 @@ func TestLogDuration(t *testing.T) {
assert.True(t, strings.Contains(w.String(), `get foo\nset bar 0`))
}
func injectLog() (r *strings.Builder, restore func()) {
var buf strings.Builder
w := logx.NewWriter(&buf)
o := logx.Reset()
logx.SetWriter(w)
return &buf, func() {
logx.Reset()
logx.SetWriter(o)
func TestFormatError(t *testing.T) {
// Test case: err is OpError
err := &net.OpError{
Err: mockOpError{},
}
assert.Equal(t, "timeout", formatError(err))
// Test case: err is nil
assert.Equal(t, "", formatError(nil))
// Test case: err is red.Nil
assert.Equal(t, "", formatError(red.Nil))
// Test case: err is io.EOF
assert.Equal(t, "eof", formatError(io.EOF))
// Test case: err is context.DeadlineExceeded
assert.Equal(t, "context deadline", formatError(context.DeadlineExceeded))
// Test case: err is breaker.ErrServiceUnavailable
assert.Equal(t, "breaker", formatError(breaker.ErrServiceUnavailable))
// Test case: err is unknown
assert.Equal(t, "unexpected error", formatError(errors.New("some error")))
}
type mockOpError struct {
}
func (mockOpError) Error() string {
return "mock error"
}
func (mockOpError) Timeout() bool {
return true
}

View File

@@ -4,13 +4,13 @@ import (
"context"
"errors"
"fmt"
"log"
"strconv"
"time"
red "github.com/go-redis/redis/v8"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/errorx"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mapping"
"github.com/zeromicro/go-zero/core/syncx"
)
@@ -91,22 +91,19 @@ type (
Script = red.Script
)
// MustNewRedis returns a Redis with given options.
func MustNewRedis(conf RedisConf, opts ...Option) *Redis {
rds, err := NewRedis(conf, opts...)
logx.Must(err)
return rds
}
// New returns a Redis with given options.
// Deprecated: use MustNewRedis or NewRedis instead.
func New(addr string, opts ...Option) *Redis {
return newRedis(addr, opts...)
}
// MustNewRedis returns a Redis with given options.
func MustNewRedis(conf RedisConf, opts ...Option) *Redis {
rds, err := NewRedis(conf, opts...)
if err != nil {
log.Fatal(err)
}
return rds
}
// NewRedis returns a Redis with given options.
func NewRedis(conf RedisConf, opts ...Option) (*Redis, error) {
if err := conf.Validate(); err != nil {

View File

@@ -97,6 +97,9 @@ func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
}
// ExecCtx runs given exec on given keys, and returns execution result.
// If DB operation succeeds, it will delete cache with given keys,
// if DB operation fails, it will return nil result and non-nil error,
// if DB operation succeeds but cache deletion fails, it will return result and non-nil error.
func (cc CachedConn) ExecCtx(ctx context.Context, exec ExecCtxFn, keys ...string) (
sql.Result, error) {
res, err := exec(ctx, cc.db)
@@ -104,11 +107,7 @@ func (cc CachedConn) ExecCtx(ctx context.Context, exec ExecCtxFn, keys ...string
return nil, err
}
if err := cc.DelCacheCtx(ctx, keys...); err != nil {
return nil, err
}
return res, nil
return res, cc.DelCacheCtx(ctx, keys...)
}
// ExecNoCache runs exec with given sql statement, without affecting cache.
@@ -214,6 +213,17 @@ func (cc CachedConn) SetCacheCtx(ctx context.Context, key string, val any) error
return cc.cache.SetCtx(ctx, key, val)
}
// SetCacheWithExpire sets v into cache with given key with given expire.
func (cc CachedConn) SetCacheWithExpire(key string, val any, expire time.Duration) error {
return cc.SetCacheWithExpireCtx(context.Background(), key, val, expire)
}
// SetCacheWithExpireCtx sets v into cache with given key with given expire.
func (cc CachedConn) SetCacheWithExpireCtx(ctx context.Context, key string, val any,
expire time.Duration) error {
return cc.cache.SetWithExpireCtx(ctx, key, val, expire)
}
// Transact runs given fn in transaction mode.
func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
fnCtx := func(_ context.Context, session sqlx.Session) error {
@@ -226,3 +236,15 @@ func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
func (cc CachedConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
return cc.db.TransactCtx(ctx, fn)
}
// WithSession returns a new CachedConn with given session.
// If query from session, the uncommitted data might be returned.
// Don't query for the uncommitted data, you should just use it,
// and don't use the cache for the uncommitted data.
// Not recommend to use cache within transactions due to consistency problem.
func (cc CachedConn) WithSession(session sqlx.Session) CachedConn {
return CachedConn{
db: sqlx.NewSqlConnFromSession(session),
cache: cc.cache,
}
}

View File

@@ -15,6 +15,7 @@ import (
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/fx"
@@ -24,6 +25,8 @@ import (
"github.com/zeromicro/go-zero/core/stores/redis"
"github.com/zeromicro/go-zero/core/stores/redis/redistest"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/syncx"
"github.com/zeromicro/go-zero/internal/dbtest"
)
func init() {
@@ -39,7 +42,7 @@ func TestCachedConn_GetCache(t *testing.T) {
var value string
err := c.GetCache("any", &value)
assert.Equal(t, ErrNotFound, err)
r.Set("any", `"value"`)
_ = r.Set("any", `"value"`)
err = c.GetCache("any", &value)
assert.Nil(t, err)
assert.Equal(t, "value", value)
@@ -368,6 +371,24 @@ func TestStatFromMemory(t *testing.T) {
assert.Equal(t, uint64(9), atomic.LoadUint64(&stats.Hit))
}
func TestCachedConn_DelCache(t *testing.T) {
r := redistest.CreateRedis(t)
const (
key = "user"
value = "any"
)
assert.NoError(t, r.Set(key, value))
c := NewNodeConn(&trackedConn{}, r, cache.WithExpiry(time.Second*30))
err := c.DelCache(key)
assert.Nil(t, err)
val, err := r.Get(key)
assert.Nil(t, err)
assert.Empty(t, val)
}
func TestCachedConnQueryRow(t *testing.T) {
r := redistest.CreateRedis(t)
@@ -450,6 +471,36 @@ func TestCachedConnExec(t *testing.T) {
}
func TestCachedConnExecDropCache(t *testing.T) {
t.Run("drop cache", func(t *testing.T) {
r, err := miniredis.Run()
assert.Nil(t, err)
defer fx.DoWithTimeout(func() error {
r.Close()
return nil
}, time.Second)
const (
key = "user"
value = "any"
)
var conn trackedConn
c := NewNodeConn(&conn, redis.New(r.Addr()), cache.WithExpiry(time.Second*30))
assert.Nil(t, c.SetCache(key, value))
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return conn.Exec("delete from user_table where id='kevin'")
}, key)
assert.Nil(t, err)
assert.True(t, conn.execValue)
_, err = r.Get(key)
assert.Exactly(t, miniredis.ErrKeyNotFound, err)
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return nil, errors.New("foo")
}, key)
assert.NotNil(t, err)
})
}
func TestCachedConn_SetCacheWithExpire(t *testing.T) {
r, err := miniredis.Run()
assert.Nil(t, err)
defer fx.DoWithTimeout(func() error {
@@ -463,18 +514,13 @@ func TestCachedConnExecDropCache(t *testing.T) {
)
var conn trackedConn
c := NewNodeConn(&conn, redis.New(r.Addr()), cache.WithExpiry(time.Second*30))
assert.Nil(t, c.SetCache(key, value))
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return conn.Exec("delete from user_table where id='kevin'")
}, key)
assert.Nil(t, err)
assert.True(t, conn.execValue)
_, err = r.Get(key)
assert.Exactly(t, miniredis.ErrKeyNotFound, err)
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
return nil, errors.New("foo")
}, key)
assert.NotNil(t, err)
assert.Nil(t, c.SetCacheWithExpire(key, value, time.Minute))
val, err := r.Get(key)
if assert.NoError(t, err) {
ttl := r.TTL(key)
assert.True(t, ttl > 0 && ttl <= time.Minute)
assert.Equal(t, fmt.Sprintf("%q", value), val)
}
}
func TestCachedConnExecDropCacheFailed(t *testing.T) {
@@ -543,6 +589,125 @@ func TestNewConnWithCache(t *testing.T) {
assert.True(t, conn.execValue)
}
func TestCachedConn_WithSession(t *testing.T) {
dbtest.RunTxTest(t, func(tx *sql.Tx, mock sqlmock.Sqlmock) {
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
r := redistest.CreateRedis(t)
conn := CachedConn{
cache: cache.NewNode(r, syncx.NewSingleFlight(), stats, sql.ErrNoRows),
}
conn = conn.WithSession(sqlx.NewSessionFromTx(tx))
res, err := conn.Exec(func(conn sqlx.SqlConn) (sql.Result, error) {
return conn.Exec("any")
}, "foo")
assert.NoError(t, err)
last, err := res.LastInsertId()
assert.NoError(t, err)
assert.Equal(t, int64(2), last)
affected, err := res.RowsAffected()
assert.NoError(t, err)
assert.Equal(t, int64(3), affected)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
mock.ExpectCommit()
r := redistest.CreateRedis(t)
conn := CachedConn{
db: sqlx.NewSqlConnFromDB(db),
cache: cache.NewNode(r, syncx.NewSingleFlight(), stats, sql.ErrNoRows),
}
assert.NoError(t, conn.Transact(func(session sqlx.Session) error {
conn = conn.WithSession(session)
res, err := conn.Exec(func(conn sqlx.SqlConn) (sql.Result, error) {
return conn.Exec("any")
}, "foo")
assert.NoError(t, err)
last, err := res.LastInsertId()
assert.NoError(t, err)
assert.Equal(t, int64(2), last)
affected, err := res.RowsAffected()
assert.NoError(t, err)
assert.Equal(t, int64(3), affected)
return nil
}))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("any").WillReturnError(errors.New("foo"))
mock.ExpectRollback()
r := redistest.CreateRedis(t)
conn := CachedConn{
db: sqlx.NewSqlConnFromDB(db),
cache: cache.NewNode(r, syncx.NewSingleFlight(), stats, sql.ErrNoRows),
}
assert.Error(t, conn.Transact(func(session sqlx.Session) error {
conn = conn.WithSession(session)
_, err := conn.Exec(func(conn sqlx.SqlConn) (sql.Result, error) {
return conn.Exec("any")
}, "bar")
return err
}))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectQuery("any").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(2))
mock.ExpectCommit()
r := redistest.CreateRedis(t)
conn := CachedConn{
db: sqlx.NewSqlConnFromDB(db),
cache: cache.NewNode(r, syncx.NewSingleFlight(), stats, sql.ErrNoRows),
}
assert.NoError(t, conn.Transact(func(session sqlx.Session) error {
var val string
conn = conn.WithSession(session)
err := conn.QueryRow(&val, "foo", func(conn sqlx.SqlConn, v interface{}) error {
return conn.QueryRow(v, "any")
})
assert.Equal(t, "2", val)
return err
}))
val, err := r.Get("foo")
assert.NoError(t, err)
assert.Equal(t, `"2"`, val)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectQuery("any").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(2))
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
mock.ExpectCommit()
r := redistest.CreateRedis(t)
conn := CachedConn{
db: sqlx.NewSqlConnFromDB(db),
cache: cache.NewNode(r, syncx.NewSingleFlight(), stats, sql.ErrNoRows),
}
assert.NoError(t, conn.Transact(func(session sqlx.Session) error {
var val string
conn = conn.WithSession(session)
assert.NoError(t, conn.QueryRow(&val, "foo", func(conn sqlx.SqlConn, v interface{}) error {
return conn.QueryRow(v, "any")
}))
assert.Equal(t, "2", val)
_, err := conn.Exec(func(conn sqlx.SqlConn) (sql.Result, error) {
return conn.Exec("any")
}, "foo")
return err
}))
val, err := r.Get("foo")
assert.NoError(t, err)
assert.Empty(t, val)
})
}
func resetStats() {
atomic.StoreUint64(&stats.Total, 0)
atomic.StoreUint64(&stats.Hit, 0)
@@ -554,35 +719,35 @@ type dummySqlConn struct {
queryRow func(any, string, ...any) error
}
func (d dummySqlConn) ExecCtx(ctx context.Context, query string, args ...any) (sql.Result, error) {
func (d dummySqlConn) ExecCtx(_ context.Context, _ string, _ ...any) (sql.Result, error) {
return nil, nil
}
func (d dummySqlConn) PrepareCtx(ctx context.Context, query string) (sqlx.StmtSession, error) {
func (d dummySqlConn) PrepareCtx(_ context.Context, _ string) (sqlx.StmtSession, error) {
return nil, nil
}
func (d dummySqlConn) QueryRowPartialCtx(ctx context.Context, v any, query string, args ...any) error {
func (d dummySqlConn) QueryRowPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (d dummySqlConn) QueryRowsCtx(ctx context.Context, v any, query string, args ...any) error {
func (d dummySqlConn) QueryRowsCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (d dummySqlConn) QueryRowsPartialCtx(ctx context.Context, v any, query string, args ...any) error {
func (d dummySqlConn) QueryRowsPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (d dummySqlConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
func (d dummySqlConn) TransactCtx(_ context.Context, _ func(context.Context, sqlx.Session) error) error {
return nil
}
func (d dummySqlConn) Exec(query string, args ...any) (sql.Result, error) {
func (d dummySqlConn) Exec(_ string, _ ...any) (sql.Result, error) {
return nil, nil
}
func (d dummySqlConn) Prepare(query string) (sqlx.StmtSession, error) {
func (d dummySqlConn) Prepare(_ string) (sqlx.StmtSession, error) {
return nil, nil
}
@@ -597,15 +762,15 @@ func (d dummySqlConn) QueryRowCtx(_ context.Context, v any, query string, args .
return nil
}
func (d dummySqlConn) QueryRowPartial(v any, query string, args ...any) error {
func (d dummySqlConn) QueryRowPartial(_ any, _ string, _ ...any) error {
return nil
}
func (d dummySqlConn) QueryRows(v any, query string, args ...any) error {
func (d dummySqlConn) QueryRows(_ any, _ string, _ ...any) error {
return nil
}
func (d dummySqlConn) QueryRowsPartial(v any, query string, args ...any) error {
func (d dummySqlConn) QueryRowsPartial(_ any, _ string, _ ...any) error {
return nil
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/internal/dbtest"
)
type mockedConn struct {
@@ -81,7 +81,7 @@ func (c *mockedConn) Transact(func(session Session) error) error {
}
func TestBulkInserter(t *testing.T) {
runSqlTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var conn mockedConn
inserter, err := NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES(?, ?, ?)`)
assert.Nil(t, err)
@@ -98,7 +98,7 @@ func TestBulkInserter(t *testing.T) {
}
func TestBulkInserterSuffix(t *testing.T) {
runSqlTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var conn mockedConn
inserter, err := NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES`+
`(?, ?, ?) ON DUPLICATE KEY UPDATE is_overtime=VALUES(is_overtime)`)
@@ -119,7 +119,7 @@ func TestBulkInserterSuffix(t *testing.T) {
}
func TestBulkInserterBadStatement(t *testing.T) {
runSqlTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var conn mockedConn
_, err := NewBulkInserter(&conn, "foo")
assert.NotNil(t, err)
@@ -144,19 +144,3 @@ func TestBulkInserter_Update(t *testing.T) {
assert.NotNil(t, inserter.UpdateStmt("foo"))
assert.NotNil(t, inserter.Insert("foo", "bar"))
}
func runSqlTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
logx.Disable()
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
fn(db, mock)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}

View File

@@ -0,0 +1,14 @@
package sqlx
import (
"database/sql"
"errors"
)
var (
// ErrNotFound is an alias of sql.ErrNoRows
ErrNotFound = sql.ErrNoRows
errCantNestTx = errors.New("cannot nest transactions")
errNoRawDBFromTx = errors.New("cannot get raw db from transaction")
)

View File

@@ -32,7 +32,5 @@ func mysqlAcceptable(err error) bool {
}
func withMysqlAcceptable() SqlOption {
return func(conn *commonSqlConn) {
conn.accept = mysqlAcceptable
}
return WithAcceptable(mysqlAcceptable)
}

View File

@@ -54,27 +54,39 @@ func getTaggedFieldValueMap(v reflect.Value) (map[string]any, error) {
}
valueField := reflect.Indirect(v).Field(i)
switch valueField.Kind() {
case reflect.Ptr:
if !valueField.CanInterface() {
return nil, ErrNotReadableValue
}
if valueField.IsNil() {
baseValueType := mapping.Deref(valueField.Type())
valueField.Set(reflect.New(baseValueType))
}
result[key] = valueField.Interface()
default:
if !valueField.CanAddr() || !valueField.Addr().CanInterface() {
return nil, ErrNotReadableValue
}
result[key] = valueField.Addr().Interface()
valueData, err := getValueInterface(valueField)
if err != nil {
return nil, err
}
result[key] = valueData
}
return result, nil
}
func getValueInterface(value reflect.Value) (any, error) {
switch value.Kind() {
case reflect.Ptr:
if !value.CanInterface() {
return nil, ErrNotReadableValue
}
if value.IsNil() {
baseValueType := mapping.Deref(value.Type())
value.Set(reflect.New(baseValueType))
}
return value.Interface(), nil
default:
if !value.CanAddr() || !value.Addr().CanInterface() {
return nil, ErrNotReadableValue
}
return value.Addr().Interface(), nil
}
}
func mapStructFieldsIntoSlice(v reflect.Value, columns []string, strict bool) ([]any, error) {
fields := unwrapFields(v)
if strict && len(columns) < len(fields) {
@@ -88,24 +100,18 @@ func mapStructFieldsIntoSlice(v reflect.Value, columns []string, strict bool) ([
values := make([]any, len(columns))
if len(taggedMap) == 0 {
if len(fields) < len(values) {
return nil, ErrNotMatchDestination
}
for i := 0; i < len(values); i++ {
valueField := fields[i]
switch valueField.Kind() {
case reflect.Ptr:
if !valueField.CanInterface() {
return nil, ErrNotReadableValue
}
if valueField.IsNil() {
baseValueType := mapping.Deref(valueField.Type())
valueField.Set(reflect.New(baseValueType))
}
values[i] = valueField.Interface()
default:
if !valueField.CanAddr() || !valueField.Addr().CanInterface() {
return nil, ErrNotReadableValue
}
values[i] = valueField.Addr().Interface()
valueData, err := getValueInterface(valueField)
if err != nil {
return nil, err
}
values[i] = valueData
}
} else {
for i, column := range columns {
@@ -152,11 +158,11 @@ func unmarshalRow(v any, scanner rowsScanner, strict bool) error {
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
if rve.CanSet() {
return scanner.Scan(v)
if !rve.CanSet() {
return ErrNotSettable
}
return ErrNotSettable
return scanner.Scan(v)
case reflect.Struct:
columns, err := scanner.Columns()
if err != nil {
@@ -183,69 +189,66 @@ func unmarshalRows(v any, scanner rowsScanner, strict bool) error {
rt := reflect.TypeOf(v)
rte := rt.Elem()
rve := rv.Elem()
if !rve.CanSet() {
return ErrNotSettable
}
switch rte.Kind() {
case reflect.Slice:
if rve.CanSet() {
ptr := rte.Elem().Kind() == reflect.Ptr
appendFn := func(item reflect.Value) {
if ptr {
rve.Set(reflect.Append(rve, item))
} else {
rve.Set(reflect.Append(rve, reflect.Indirect(item)))
}
ptr := rte.Elem().Kind() == reflect.Ptr
appendFn := func(item reflect.Value) {
if ptr {
rve.Set(reflect.Append(rve, item))
} else {
rve.Set(reflect.Append(rve, reflect.Indirect(item)))
}
fillFn := func(value any) error {
if rve.CanSet() {
if err := scanner.Scan(value); err != nil {
return err
}
appendFn(reflect.ValueOf(value))
return nil
}
return ErrNotSettable
}
fillFn := func(value any) error {
if err := scanner.Scan(value); err != nil {
return err
}
base := mapping.Deref(rte.Elem())
switch base.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
for scanner.Next() {
value := reflect.New(base)
if err := fillFn(value.Interface()); err != nil {
return err
}
appendFn(reflect.ValueOf(value))
return nil
}
base := mapping.Deref(rte.Elem())
switch base.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
for scanner.Next() {
value := reflect.New(base)
if err := fillFn(value.Interface()); err != nil {
return err
}
case reflect.Struct:
columns, err := scanner.Columns()
}
case reflect.Struct:
columns, err := scanner.Columns()
if err != nil {
return err
}
for scanner.Next() {
value := reflect.New(base)
values, err := mapStructFieldsIntoSlice(value, columns, strict)
if err != nil {
return err
}
for scanner.Next() {
value := reflect.New(base)
values, err := mapStructFieldsIntoSlice(value, columns, strict)
if err != nil {
return err
}
if err := scanner.Scan(values...); err != nil {
return err
}
appendFn(value)
if err := scanner.Scan(values...); err != nil {
return err
}
default:
return ErrUnsupportedValueType
}
return nil
appendFn(value)
}
default:
return ErrUnsupportedValueType
}
return ErrNotSettable
return nil
default:
return ErrUnsupportedValueType
}
@@ -257,6 +260,10 @@ func unwrapFields(v reflect.Value) []reflect.Value {
for i := 0; i < indirect.NumField(); i++ {
child := indirect.Field(i)
if !child.CanSet() {
continue
}
if child.Kind() == reflect.Ptr && child.IsNil() {
baseValueType := mapping.Deref(child.Type())
child.Set(reflect.New(baseValueType))

View File

@@ -8,11 +8,11 @@ import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/internal/dbtest"
)
func TestUnmarshalRowBool(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -22,10 +22,22 @@ func TestUnmarshalRowBool(t *testing.T) {
}, "select value from users where user=?", "anyone"))
assert.True(t, value)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
var value struct {
Value bool `db:"value"`
}
assert.Error(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select value from users where user=?", "anyone"))
})
}
func TestUnmarshalRowBoolNotSettable(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -37,7 +49,7 @@ func TestUnmarshalRowBoolNotSettable(t *testing.T) {
}
func TestUnmarshalRowInt(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -50,7 +62,7 @@ func TestUnmarshalRowInt(t *testing.T) {
}
func TestUnmarshalRowInt8(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -63,7 +75,7 @@ func TestUnmarshalRowInt8(t *testing.T) {
}
func TestUnmarshalRowInt16(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("4")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -76,7 +88,7 @@ func TestUnmarshalRowInt16(t *testing.T) {
}
func TestUnmarshalRowInt32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -89,7 +101,7 @@ func TestUnmarshalRowInt32(t *testing.T) {
}
func TestUnmarshalRowInt64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("6")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -102,7 +114,7 @@ func TestUnmarshalRowInt64(t *testing.T) {
}
func TestUnmarshalRowUint(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -115,7 +127,7 @@ func TestUnmarshalRowUint(t *testing.T) {
}
func TestUnmarshalRowUint8(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -128,7 +140,7 @@ func TestUnmarshalRowUint8(t *testing.T) {
}
func TestUnmarshalRowUint16(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("4")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -141,7 +153,7 @@ func TestUnmarshalRowUint16(t *testing.T) {
}
func TestUnmarshalRowUint32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -154,7 +166,7 @@ func TestUnmarshalRowUint32(t *testing.T) {
}
func TestUnmarshalRowUint64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("6")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -167,7 +179,7 @@ func TestUnmarshalRowUint64(t *testing.T) {
}
func TestUnmarshalRowFloat32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("7")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -180,7 +192,7 @@ func TestUnmarshalRowFloat32(t *testing.T) {
}
func TestUnmarshalRowFloat64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("8")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -193,7 +205,7 @@ func TestUnmarshalRowFloat64(t *testing.T) {
}
func TestUnmarshalRowString(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
const expect = "hello"
rs := sqlmock.NewRows([]string{"value"}).FromCSVString(expect)
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -207,12 +219,12 @@ func TestUnmarshalRowString(t *testing.T) {
}
func TestUnmarshalRowStruct(t *testing.T) {
value := new(struct {
Name string
Age int
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
Name string
Age int
})
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -222,15 +234,58 @@ func TestUnmarshalRowStruct(t *testing.T) {
assert.Equal(t, "liao", value.Name)
assert.Equal(t, 5, value.Age)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
Name string
Age int
})
errAny := errors.New("any error")
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(value, &mockedScanner{
colErr: errAny,
next: 1,
}, true)
}, "select name, age from users where user=?", "anyone"), errAny)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
Name string
age *int
})
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select name, age from users where user=?", "anyone"), ErrNotMatchDestination)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("8")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
type myString chan int
var value myString
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(&value, rows, true)
}, "select value from users where user=?", "anyone"), ErrUnsupportedValueType)
})
}
func TestUnmarshalRowStructWithTags(t *testing.T) {
value := new(struct {
Age int `db:"age"`
Name string `db:"name"`
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
Age int `db:"age"`
Name string `db:"name"`
})
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -240,6 +295,51 @@ func TestUnmarshalRowStructWithTags(t *testing.T) {
assert.Equal(t, "liao", value.Name)
assert.Equal(t, 5, value.Age)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
age *int `db:"age"`
Name string `db:"name"`
})
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select name, age from users where user=?", "anyone"), ErrNotReadableValue)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var value struct {
Age *int `db:"age"`
Name *string `db:"name"`
}
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(&value, rows, true)
}, "select name, age from users where user=?", "anyone"))
assert.Equal(t, "liao", *value.Name)
assert.Equal(t, 5, *value.Age)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
value := new(struct {
Age int `db:"age"`
Name string
})
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select name, age from users where user=?", "anyone"))
assert.Equal(t, 5, value.Age)
})
}
func TestUnmarshalRowStructWithTagsWrongColumns(t *testing.T) {
@@ -248,7 +348,7 @@ func TestUnmarshalRowStructWithTagsWrongColumns(t *testing.T) {
Name string `db:"name"`
})
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name"}).FromCSVString("liao")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -259,7 +359,7 @@ func TestUnmarshalRowStructWithTagsWrongColumns(t *testing.T) {
}
func TestUnmarshalRowsBool(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []bool{true, false}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1\n0")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -270,10 +370,46 @@ func TestUnmarshalRowsBool(t *testing.T) {
}, "select value from users where user=?", "anyone"))
assert.EqualValues(t, expect, value)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1\n0")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
var value []bool
assert.Error(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(value, rows, true)
}, "select value from users where user=?", "anyone"))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1\n0")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
var value struct {
value []bool `db:"value"`
}
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, rows, true)
}, "select value from users where user=?", "anyone"), ErrUnsupportedValueType)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1\n0")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
var value []bool
errAny := errors.New("any")
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, &mockedScanner{
scanErr: errAny,
next: 1,
}, true)
}, "select value from users where user=?", "anyone"), errAny)
})
}
func TestUnmarshalRowsInt(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []int{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -287,7 +423,7 @@ func TestUnmarshalRowsInt(t *testing.T) {
}
func TestUnmarshalRowsInt8(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []int8{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -301,7 +437,7 @@ func TestUnmarshalRowsInt8(t *testing.T) {
}
func TestUnmarshalRowsInt16(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []int16{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -315,7 +451,7 @@ func TestUnmarshalRowsInt16(t *testing.T) {
}
func TestUnmarshalRowsInt32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []int32{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -329,7 +465,7 @@ func TestUnmarshalRowsInt32(t *testing.T) {
}
func TestUnmarshalRowsInt64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []int64{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -343,7 +479,7 @@ func TestUnmarshalRowsInt64(t *testing.T) {
}
func TestUnmarshalRowsUint(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []uint{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -357,7 +493,7 @@ func TestUnmarshalRowsUint(t *testing.T) {
}
func TestUnmarshalRowsUint8(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []uint8{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -371,7 +507,7 @@ func TestUnmarshalRowsUint8(t *testing.T) {
}
func TestUnmarshalRowsUint16(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []uint16{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -385,7 +521,7 @@ func TestUnmarshalRowsUint16(t *testing.T) {
}
func TestUnmarshalRowsUint32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []uint32{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -399,7 +535,7 @@ func TestUnmarshalRowsUint32(t *testing.T) {
}
func TestUnmarshalRowsUint64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []uint64{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -413,7 +549,7 @@ func TestUnmarshalRowsUint64(t *testing.T) {
}
func TestUnmarshalRowsFloat32(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []float32{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -427,7 +563,7 @@ func TestUnmarshalRowsFloat32(t *testing.T) {
}
func TestUnmarshalRowsFloat64(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []float64{2, 3}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -441,7 +577,7 @@ func TestUnmarshalRowsFloat64(t *testing.T) {
}
func TestUnmarshalRowsString(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []string{"hello", "world"}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("hello\nworld")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -457,7 +593,7 @@ func TestUnmarshalRowsString(t *testing.T) {
func TestUnmarshalRowsBoolPtr(t *testing.T) {
yes := true
no := false
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*bool{&yes, &no}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1\n0")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -473,7 +609,7 @@ func TestUnmarshalRowsBoolPtr(t *testing.T) {
func TestUnmarshalRowsIntPtr(t *testing.T) {
two := 2
three := 3
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*int{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -489,7 +625,7 @@ func TestUnmarshalRowsIntPtr(t *testing.T) {
func TestUnmarshalRowsInt8Ptr(t *testing.T) {
two := int8(2)
three := int8(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*int8{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -505,7 +641,7 @@ func TestUnmarshalRowsInt8Ptr(t *testing.T) {
func TestUnmarshalRowsInt16Ptr(t *testing.T) {
two := int16(2)
three := int16(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*int16{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -521,7 +657,7 @@ func TestUnmarshalRowsInt16Ptr(t *testing.T) {
func TestUnmarshalRowsInt32Ptr(t *testing.T) {
two := int32(2)
three := int32(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*int32{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -537,7 +673,7 @@ func TestUnmarshalRowsInt32Ptr(t *testing.T) {
func TestUnmarshalRowsInt64Ptr(t *testing.T) {
two := int64(2)
three := int64(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*int64{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -553,7 +689,7 @@ func TestUnmarshalRowsInt64Ptr(t *testing.T) {
func TestUnmarshalRowsUintPtr(t *testing.T) {
two := uint(2)
three := uint(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*uint{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -569,7 +705,7 @@ func TestUnmarshalRowsUintPtr(t *testing.T) {
func TestUnmarshalRowsUint8Ptr(t *testing.T) {
two := uint8(2)
three := uint8(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*uint8{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -585,7 +721,7 @@ func TestUnmarshalRowsUint8Ptr(t *testing.T) {
func TestUnmarshalRowsUint16Ptr(t *testing.T) {
two := uint16(2)
three := uint16(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*uint16{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -601,7 +737,7 @@ func TestUnmarshalRowsUint16Ptr(t *testing.T) {
func TestUnmarshalRowsUint32Ptr(t *testing.T) {
two := uint32(2)
three := uint32(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*uint32{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -617,7 +753,7 @@ func TestUnmarshalRowsUint32Ptr(t *testing.T) {
func TestUnmarshalRowsUint64Ptr(t *testing.T) {
two := uint64(2)
three := uint64(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*uint64{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -633,7 +769,7 @@ func TestUnmarshalRowsUint64Ptr(t *testing.T) {
func TestUnmarshalRowsFloat32Ptr(t *testing.T) {
two := float32(2)
three := float32(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*float32{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -649,7 +785,7 @@ func TestUnmarshalRowsFloat32Ptr(t *testing.T) {
func TestUnmarshalRowsFloat64Ptr(t *testing.T) {
two := float64(2)
three := float64(3)
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*float64{&two, &three}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2\n3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -665,7 +801,7 @@ func TestUnmarshalRowsFloat64Ptr(t *testing.T) {
func TestUnmarshalRowsStringPtr(t *testing.T) {
hello := "hello"
world := "world"
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []*string{&hello, &world}
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("hello\nworld")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -679,25 +815,25 @@ func TestUnmarshalRowsStringPtr(t *testing.T) {
}
func TestUnmarshalRowsStruct(t *testing.T) {
expect := []struct {
Name string
Age int64
}{
{
Name: "first",
Age: 2,
},
{
Name: "second",
Age: 3,
},
}
var value []struct {
Name string
Age int64
}
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
expect := []struct {
Name string
Age int64
}{
{
Name: "first",
Age: 2,
},
{
Name: "second",
Age: 3,
},
}
var value []struct {
Name string
Age int64
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -709,6 +845,56 @@ func TestUnmarshalRowsStruct(t *testing.T) {
assert.Equal(t, each.Age, value[i].Age)
}
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var value []struct {
Name string
Age int64
}
errAny := errors.New("any error")
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, &mockedScanner{
colErr: errAny,
next: 1,
}, true)
}, "select name, age from users where user=?", "anyone"), errAny)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var value []struct {
Name string
Age int64
}
errAny := errors.New("any error")
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, &mockedScanner{
cols: []string{"name", "age"},
scanErr: errAny,
next: 1,
}, true)
}, "select name, age from users where user=?", "anyone"), errAny)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var value []chan int
errAny := errors.New("any error")
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.ErrorIs(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, &mockedScanner{
cols: []string{"name", "age"},
scanErr: errAny,
next: 1,
}, true)
}, "select name, age from users where user=?", "anyone"), ErrUnsupportedValueType)
})
}
func TestUnmarshalRowsStructWithNullStringType(t *testing.T) {
@@ -736,7 +922,7 @@ func TestUnmarshalRowsStructWithNullStringType(t *testing.T) {
NullString sql.NullString `db:"value"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "value"}).AddRow(
"first", "firstnullstring").AddRow("second", nil)
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -771,7 +957,7 @@ func TestUnmarshalRowsStructWithTags(t *testing.T) {
Name string `db:"name"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -812,7 +998,7 @@ func TestUnmarshalRowsStructAndEmbeddedAnonymousStructWithTags(t *testing.T) {
Embed
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -854,7 +1040,7 @@ func TestUnmarshalRowsStructAndEmbeddedStructPtrAnonymousWithTags(t *testing.T)
*Embed
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -888,7 +1074,7 @@ func TestUnmarshalRowsStructPtr(t *testing.T) {
Age int64
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -921,7 +1107,7 @@ func TestUnmarshalRowsStructWithTagsPtr(t *testing.T) {
Name string `db:"name"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -954,7 +1140,7 @@ func TestUnmarshalRowsStructWithTagsPtrWithInnerPtr(t *testing.T) {
Name string `db:"name"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
@@ -969,7 +1155,7 @@ func TestUnmarshalRowsStructWithTagsPtrWithInnerPtr(t *testing.T) {
}
func TestCommonSqlConn_QueryRowOptional(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"age"}).FromCSVString("5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
@@ -1019,7 +1205,7 @@ func TestUnmarshalRowError(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"age"}).FromCSVString("5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs(
"anyone").WillReturnRows(rs)
@@ -1091,7 +1277,7 @@ func TestAnonymousStructPr(t *testing.T) {
Name string `db:"name"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{
"name",
"age",
@@ -1139,7 +1325,7 @@ func TestAnonymousStructPrError(t *testing.T) {
Name string `db:"name"`
}
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{
"name",
"age",
@@ -1154,7 +1340,7 @@ func TestAnonymousStructPrError(t *testing.T) {
WithArgs("anyone").WillReturnRows(rs)
assert.Error(t, query(context.Background(), db, func(rows *sql.Rows) error {
return unmarshalRows(&value, rows, true)
}, "select name, age,grade,discipline,class_name,score from users where user=?",
}, "select name, age, grade, discipline, class_name, score from users where user=?",
"anyone"))
if len(value) > 0 {
assert.Equal(t, value[0].score, 0)
@@ -1162,23 +1348,8 @@ func TestAnonymousStructPrError(t *testing.T) {
})
}
func runOrmTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
logx.Disable()
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
fn(db, mock)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
type mockedScanner struct {
cols []string
colErr error
scanErr error
err error
@@ -1186,7 +1357,7 @@ type mockedScanner struct {
}
func (m *mockedScanner) Columns() ([]string, error) {
return nil, m.colErr
return m.cols, m.colErr
}
func (m *mockedScanner) Err() error {

View File

@@ -11,9 +11,6 @@ import (
// spanName is used to identify the span name for the SQL execution.
const spanName = "sql"
// ErrNotFound is an alias of sql.ErrNoRows
var ErrNotFound = sql.ErrNoRows
type (
// Session stands for raw connections or transaction sessions
Session interface {
@@ -131,6 +128,13 @@ func NewSqlConnFromDB(db *sql.DB, opts ...SqlOption) SqlConn {
return conn
}
// NewSqlConnFromSession returns a SqlConn with the given session.
func NewSqlConnFromSession(session Session) SqlConn {
return txConn{
Session: session,
}
}
func (db *commonSqlConn) Exec(q string, args ...any) (result sql.Result, err error) {
return db.ExecCtx(context.Background(), q, args...)
}
@@ -395,3 +399,11 @@ func (s statement) QueryRowsPartialCtx(ctx context.Context, v any, args ...any)
return unmarshalRows(v, rows, false)
}, s.query, args...)
}
// WithAcceptable returns a SqlOption that setting the acceptable function.
// acceptable is the func to check if the error can be accepted.
func WithAcceptable(acceptable func(err error) bool) SqlOption {
return func(conn *commonSqlConn) {
conn.accept = acceptable
}
}

View File

@@ -2,13 +2,16 @@ package sqlx
import (
"database/sql"
"errors"
"io"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/trace/tracetest"
"github.com/zeromicro/go-zero/internal/dbtest"
)
const mockedDatasource = "sqlmock"
@@ -54,8 +57,187 @@ func TestSqlConn(t *testing.T) {
assert.Equal(t, 14, len(me.GetSpans()))
}
func TestSqlConn_RawDB(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"foo"}).AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
var val string
assert.NoError(t, conn.QueryRow(&val, "any"))
assert.Equal(t, "bar", val)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"foo"}).AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
var val string
assert.NoError(t, conn.QueryRowPartial(&val, "any"))
assert.Equal(t, "bar", val)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"any"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
var vals []string
assert.NoError(t, conn.QueryRows(&vals, "any"))
assert.ElementsMatch(t, []string{"foo", "bar"}, vals)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"any"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
var vals []string
assert.NoError(t, conn.QueryRowsPartial(&vals, "any"))
assert.ElementsMatch(t, []string{"foo", "bar"}, vals)
})
}
func TestSqlConn_Errors(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
conn := NewSqlConnFromDB(db)
conn.(*commonSqlConn).connProv = func() (*sql.DB, error) {
return nil, errors.New("error")
}
_, err := conn.Prepare("any")
assert.Error(t, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectExec("any").WillReturnError(breaker.ErrServiceUnavailable)
conn := NewSqlConnFromDB(db)
_, err := conn.Exec("any")
assert.Error(t, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any").WillReturnError(breaker.ErrServiceUnavailable)
conn := NewSqlConnFromDB(db)
_, err := conn.Prepare("any")
assert.Error(t, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectRollback()
conn := NewSqlConnFromDB(db)
err := conn.Transact(func(session Session) error {
return breaker.ErrServiceUnavailable
})
assert.Equal(t, breaker.ErrServiceUnavailable, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectQuery("any").WillReturnError(breaker.ErrServiceUnavailable)
conn := NewSqlConnFromDB(db)
var vals []string
err := conn.QueryRows(&vals, "any")
assert.Equal(t, breaker.ErrServiceUnavailable, err)
})
}
func TestStatement(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any").WillBeClosed()
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
assert.NoError(t, stmt.Close())
})
dbtest.RunTxTest(t, func(tx *sql.Tx, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any").WillBeClosed()
stmt, err := tx.Prepare("any")
assert.NoError(t, err)
st := statement{
query: "foo",
stmt: stmt,
}
assert.NoError(t, st.Close())
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
res, err := stmt.Exec()
assert.NoError(t, err)
lastInsertID, err := res.LastInsertId()
assert.NoError(t, err)
assert.Equal(t, int64(2), lastInsertID)
rowsAffected, err := res.RowsAffected()
assert.NoError(t, err)
assert.Equal(t, int64(3), rowsAffected)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
row := sqlmock.NewRows([]string{"foo"}).AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(row)
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
var val string
err = stmt.QueryRow(&val)
assert.NoError(t, err)
assert.Equal(t, "bar", val)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
row := sqlmock.NewRows([]string{"foo"}).AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(row)
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
var val string
err = stmt.QueryRowPartial(&val)
assert.NoError(t, err)
assert.Equal(t, "bar", val)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
rows := sqlmock.NewRows([]string{"any"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
var vals []string
assert.NoError(t, stmt.QueryRows(&vals))
assert.ElementsMatch(t, []string{"foo", "bar"}, vals)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
rows := sqlmock.NewRows([]string{"any"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
conn := NewSqlConnFromDB(db)
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
var vals []string
assert.NoError(t, stmt.QueryRowsPartial(&vals))
assert.ElementsMatch(t, []string{"foo", "bar"}, vals)
})
}
func buildConn() (mock sqlmock.Sqlmock, err error) {
connManager.GetResource(mockedDatasource, func() (io.Closer, error) {
_, err = connManager.GetResource(mockedDatasource, func() (io.Closer, error) {
var db *sql.DB
var err error
db, mock, err = sqlmock.New()

View File

@@ -136,7 +136,7 @@ func (e *realSqlGuard) finish(ctx context.Context, err error) {
logSqlError(ctx, e.stmt, err)
}
metricReqDur.Observe(int64(duration/time.Millisecond), e.command)
metricReqDur.Observe(duration.Milliseconds(), e.command)
}
func (e *realSqlGuard) start(q string, args ...any) error {

View File

@@ -15,11 +15,27 @@ type (
Rollback() error
}
txConn struct {
Session
}
txSession struct {
*sql.Tx
}
)
func (s txConn) RawDB() (*sql.DB, error) {
return nil, errNoRawDBFromTx
}
func (s txConn) Transact(_ func(Session) error) error {
return errCantNestTx
}
func (s txConn) TransactCtx(_ context.Context, _ func(context.Context, Session) error) error {
return errCantNestTx
}
// NewSessionFromTx returns a Session with the given sql.Tx.
// Use it with caution, it's provided for other ORM to interact with.
func NewSessionFromTx(tx *sql.Tx) Session {

View File

@@ -6,7 +6,10 @@ import (
"errors"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/breaker"
"github.com/zeromicro/go-zero/internal/dbtest"
)
const (
@@ -23,51 +26,51 @@ func (mt *mockTx) Commit() error {
return nil
}
func (mt *mockTx) Exec(q string, args ...any) (sql.Result, error) {
func (mt *mockTx) Exec(_ string, _ ...any) (sql.Result, error) {
return nil, nil
}
func (mt *mockTx) ExecCtx(ctx context.Context, query string, args ...any) (sql.Result, error) {
func (mt *mockTx) ExecCtx(_ context.Context, _ string, _ ...any) (sql.Result, error) {
return nil, nil
}
func (mt *mockTx) Prepare(query string) (StmtSession, error) {
func (mt *mockTx) Prepare(_ string) (StmtSession, error) {
return nil, nil
}
func (mt *mockTx) PrepareCtx(ctx context.Context, query string) (StmtSession, error) {
func (mt *mockTx) PrepareCtx(_ context.Context, _ string) (StmtSession, error) {
return nil, nil
}
func (mt *mockTx) QueryRow(v any, q string, args ...any) error {
func (mt *mockTx) QueryRow(_ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowCtx(ctx context.Context, v any, query string, args ...any) error {
func (mt *mockTx) QueryRowCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowPartial(v any, q string, args ...any) error {
func (mt *mockTx) QueryRowPartial(_ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowPartialCtx(ctx context.Context, v any, query string, args ...any) error {
func (mt *mockTx) QueryRowPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRows(v any, q string, args ...any) error {
func (mt *mockTx) QueryRows(_ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowsCtx(ctx context.Context, v any, query string, args ...any) error {
func (mt *mockTx) QueryRowsCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowsPartial(v any, q string, args ...any) error {
func (mt *mockTx) QueryRowsPartial(_ any, _ string, _ ...any) error {
return nil
}
func (mt *mockTx) QueryRowsPartialCtx(ctx context.Context, v any, query string, args ...any) error {
func (mt *mockTx) QueryRowsPartialCtx(_ context.Context, _ any, _ string, _ ...any) error {
return nil
}
@@ -101,3 +104,209 @@ func TestTransactRollback(t *testing.T) {
assert.Equal(t, mockRollback, mock.status)
assert.NotNil(t, err)
}
func TestTxExceptions(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectCommit()
conn := NewSqlConnFromDB(db)
assert.NoError(t, conn.Transact(func(session Session) error {
return nil
}))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
conn := &commonSqlConn{
connProv: func() (*sql.DB, error) {
return nil, errors.New("foo")
},
beginTx: begin,
onError: func(ctx context.Context, err error) {},
brk: breaker.NewBreaker(),
}
assert.Error(t, conn.Transact(func(session Session) error {
return nil
}))
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
_, err := conn.RawDB()
assert.Equal(t, errNoRawDBFromTx, err)
assert.Equal(t, errCantNestTx, conn.Transact(nil))
assert.Equal(t, errCantNestTx, conn.TransactCtx(context.Background(), nil))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
conn := NewSqlConnFromDB(db)
assert.Error(t, conn.Transact(func(session Session) error {
return errors.New("foo")
}))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectRollback().WillReturnError(errors.New("foo"))
conn := NewSqlConnFromDB(db)
assert.Error(t, conn.Transact(func(session Session) error {
panic("foo")
}))
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectRollback()
conn := NewSqlConnFromDB(db)
assert.Error(t, conn.Transact(func(session Session) error {
panic(errors.New("foo"))
}))
})
}
func TestTxSession(t *testing.T) {
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
res, err := conn.Exec("any")
assert.NoError(t, err)
last, err := res.LastInsertId()
assert.NoError(t, err)
assert.Equal(t, int64(2), last)
affected, err := res.RowsAffected()
assert.NoError(t, err)
assert.Equal(t, int64(3), affected)
mock.ExpectExec("any").WillReturnError(errors.New("foo"))
_, err = conn.Exec("any")
assert.Equal(t, "foo", err.Error())
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
mock.ExpectPrepare("any")
stmt, err := conn.Prepare("any")
assert.NoError(t, err)
assert.NotNil(t, stmt)
mock.ExpectPrepare("any").WillReturnError(errors.New("foo"))
_, err = conn.Prepare("any")
assert.Equal(t, "foo", err.Error())
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"col"}).AddRow("foo")
mock.ExpectQuery("any").WillReturnRows(rows)
var val string
err := conn.QueryRow(&val, "any")
assert.NoError(t, err)
assert.Equal(t, "foo", val)
mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
err = conn.QueryRow(&val, "any")
assert.Equal(t, "foo", err.Error())
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"col"}).AddRow("foo")
mock.ExpectQuery("any").WillReturnRows(rows)
var val string
err := conn.QueryRowPartial(&val, "any")
assert.NoError(t, err)
assert.Equal(t, "foo", val)
mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
err = conn.QueryRowPartial(&val, "any")
assert.Equal(t, "foo", err.Error())
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"col"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
var val []string
err := conn.QueryRows(&val, "any")
assert.NoError(t, err)
assert.Equal(t, []string{"foo", "bar"}, val)
mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
err = conn.QueryRows(&val, "any")
assert.Equal(t, "foo", err.Error())
})
runTxTest(t, func(conn SqlConn, mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{"col"}).AddRow("foo").AddRow("bar")
mock.ExpectQuery("any").WillReturnRows(rows)
var val []string
err := conn.QueryRowsPartial(&val, "any")
assert.NoError(t, err)
assert.Equal(t, []string{"foo", "bar"}, val)
mock.ExpectQuery("any").WillReturnError(errors.New("foo"))
err = conn.QueryRowsPartial(&val, "any")
assert.Equal(t, "foo", err.Error())
})
}
func TestTxRollback(t *testing.T) {
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
mock.ExpectQuery("foo").WillReturnError(errors.New("foo"))
mock.ExpectRollback()
conn := NewSqlConnFromDB(db)
err := conn.Transact(func(session Session) error {
c := NewSqlConnFromSession(session)
_, err := c.Exec("any")
assert.NoError(t, err)
var val string
return c.QueryRow(&val, "foo")
})
assert.Error(t, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("any").WillReturnError(errors.New("foo"))
mock.ExpectRollback()
conn := NewSqlConnFromDB(db)
err := conn.Transact(func(session Session) error {
c := NewSqlConnFromSession(session)
if _, err := c.Exec("any"); err != nil {
return err
}
var val string
assert.NoError(t, c.QueryRow(&val, "foo"))
return nil
})
assert.Error(t, err)
})
dbtest.RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
mock.ExpectExec("any").WillReturnResult(sqlmock.NewResult(2, 3))
mock.ExpectQuery("foo").WillReturnRows(sqlmock.NewRows([]string{"col"}).AddRow("bar"))
mock.ExpectCommit()
conn := NewSqlConnFromDB(db)
err := conn.Transact(func(session Session) error {
c := NewSqlConnFromSession(session)
_, err := c.Exec("any")
assert.NoError(t, err)
var val string
assert.NoError(t, c.QueryRow(&val, "foo"))
assert.Equal(t, "bar", val)
return nil
})
assert.NoError(t, err)
})
}
func runTxTest(t *testing.T, f func(conn SqlConn, mock sqlmock.Sqlmock)) {
dbtest.RunTxTest(t, func(tx *sql.Tx, mock sqlmock.Sqlmock) {
sess := NewSessionFromTx(tx)
conn := NewSqlConnFromSession(sess)
f(conn, mock)
})
}

View File

@@ -20,12 +20,14 @@ func ForAtomicBool(val bool) *AtomicBool {
// CompareAndSwap compares current value with given old, if equals, set to given val.
func (b *AtomicBool) CompareAndSwap(old, val bool) bool {
var ov, nv uint32
if old {
ov = 1
}
if val {
nv = 1
}
return atomic.CompareAndSwapUint32((*uint32)(b), ov, nv)
}

View File

@@ -42,7 +42,8 @@ func (manager *ResourceManager) Close() error {
}
// GetResource returns the resource associated with given key.
func (manager *ResourceManager) GetResource(key string, create func() (io.Closer, error)) (io.Closer, error) {
func (manager *ResourceManager) GetResource(key string, create func() (io.Closer, error)) (
io.Closer, error) {
val, err := manager.singleFlight.Do(key, func() (any, error) {
manager.lock.RLock()
resource, ok := manager.resources[key]

View File

@@ -9,25 +9,44 @@ import (
)
func TestTimeoutLimit(t *testing.T) {
limit := NewTimeoutLimit(2)
assert.Nil(t, limit.Borrow(time.Millisecond*200))
assert.Nil(t, limit.Borrow(time.Millisecond*200))
var wait1, wait2, wait3 sync.WaitGroup
wait1.Add(1)
wait2.Add(1)
wait3.Add(1)
go func() {
wait1.Wait()
wait2.Done()
assert.Nil(t, limit.Return())
wait3.Done()
}()
wait1.Done()
wait2.Wait()
assert.Nil(t, limit.Borrow(time.Second))
wait3.Wait()
assert.Equal(t, ErrTimeout, limit.Borrow(time.Millisecond*100))
assert.Nil(t, limit.Return())
assert.Nil(t, limit.Return())
assert.Equal(t, ErrLimitReturn, limit.Return())
tests := []struct {
name string
interval time.Duration
}{
{
name: "no wait",
},
{
name: "wait",
interval: time.Millisecond * 100,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
limit := NewTimeoutLimit(2)
assert.Nil(t, limit.Borrow(time.Millisecond*200))
assert.Nil(t, limit.Borrow(time.Millisecond*200))
var wait1, wait2, wait3 sync.WaitGroup
wait1.Add(1)
wait2.Add(1)
wait3.Add(1)
go func() {
wait1.Wait()
wait2.Done()
time.Sleep(test.interval)
assert.Nil(t, limit.Return())
wait3.Done()
}()
wait1.Done()
wait2.Wait()
assert.Nil(t, limit.Borrow(time.Second))
wait3.Wait()
assert.Equal(t, ErrTimeout, limit.Borrow(time.Millisecond*100))
assert.Nil(t, limit.Return())
assert.Nil(t, limit.Return())
assert.Equal(t, ErrLimitReturn, limit.Return())
})
}
}

View File

@@ -2,6 +2,7 @@ package threading
import (
"bytes"
"context"
"runtime"
"strconv"
@@ -13,6 +14,11 @@ func GoSafe(fn func()) {
go RunSafe(fn)
}
// GoSafeCtx runs the given fn using another goroutine, recovers if fn panics with ctx.
func GoSafeCtx(ctx context.Context, fn func()) {
go RunSafeCtx(ctx, fn)
}
// RoutineId is only for debug, never use it in production.
func RoutineId() uint64 {
b := make([]byte, 64)
@@ -31,3 +37,10 @@ func RunSafe(fn func()) {
fn()
}
// RunSafeCtx runs the given fn, recovers if fn panics with ctx.
func RunSafeCtx(ctx context.Context, fn func()) {
defer rescue.RecoverCtx(ctx)
fn()
}

View File

@@ -1,12 +1,15 @@
package threading
import (
"bytes"
"context"
"io"
"log"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx"
)
func TestRoutineId(t *testing.T) {
@@ -34,3 +37,51 @@ func TestRunSafe(t *testing.T) {
<-ch
i++
}
func TestRunSafeCtx(t *testing.T) {
var buf bytes.Buffer
logx.SetWriter(logx.NewWriter(&buf))
ctx := context.Background()
ch := make(chan lang.PlaceholderType)
i := 0
defer func() {
assert.Equal(t, 1, i)
}()
go RunSafeCtx(ctx, func() {
defer func() {
ch <- lang.Placeholder
}()
panic("panic")
})
<-ch
i++
}
func TestGoSafeCtx(t *testing.T) {
var buf bytes.Buffer
logx.SetWriter(logx.NewWriter(&buf))
ctx := context.Background()
ch := make(chan lang.PlaceholderType)
i := 0
defer func() {
assert.Equal(t, 1, i)
}()
GoSafeCtx(ctx, func() {
defer func() {
ch <- lang.Placeholder
}()
panic("panic")
})
<-ch
i++
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/url"
"os"
"sync"
"github.com/zeromicro/go-zero/core/lang"
@@ -12,6 +13,7 @@ import (
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/exporters/zipkin"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
@@ -23,6 +25,7 @@ const (
kindZipkin = "zipkin"
kindOtlpGrpc = "otlpgrpc"
kindOtlpHttp = "otlphttp"
kindFile = "file"
)
var (
@@ -33,6 +36,10 @@ var (
// StartAgent starts an opentelemetry agent.
func StartAgent(c Config) {
if c.Disabled {
return
}
lock.Lock()
defer lock.Unlock()
@@ -71,18 +78,33 @@ func createExporter(c Config) (sdktrace.SpanExporter, error) {
// endpoint can not reach.
// If the connection not dial success, the global otel ErrorHandler will catch error
// when reporting data like other exporters.
return otlptracegrpc.New(
context.Background(),
opts := []otlptracegrpc.Option{
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint(c.Endpoint),
)
}
if len(c.OtlpHeaders) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(c.OtlpHeaders))
}
return otlptracegrpc.New(context.Background(), opts...)
case kindOtlpHttp:
// Not support flexible configuration now.
return otlptracehttp.New(
context.Background(),
opts := []otlptracehttp.Option{
otlptracehttp.WithInsecure(),
otlptracehttp.WithEndpoint(c.Endpoint),
)
}
if len(c.OtlpHeaders) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(c.OtlpHeaders))
}
if len(c.OtlpHttpPath) > 0 {
opts = append(opts, otlptracehttp.WithURLPath(c.OtlpHttpPath))
}
return otlptracehttp.New(context.Background(), opts...)
case kindFile:
f, err := os.OpenFile(c.Endpoint, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("file exporter endpoint error: %s", err.Error())
}
return stdouttrace.New(stdouttrace.WithWriter(f))
default:
return nil, fmt.Errorf("unknown exporter: %s", c.Batcher)
}

View File

@@ -11,11 +11,14 @@ func TestStartAgent(t *testing.T) {
logx.Disable()
const (
endpoint1 = "localhost:1234"
endpoint2 = "remotehost:1234"
endpoint3 = "localhost:1235"
endpoint4 = "localhost:1236"
endpoint5 = "udp://localhost:6831"
endpoint1 = "localhost:1234"
endpoint2 = "remotehost:1234"
endpoint3 = "localhost:1235"
endpoint4 = "localhost:1236"
endpoint5 = "udp://localhost:6831"
endpoint6 = "localhost:1237"
endpoint71 = "/tmp/trace.log"
endpoint72 = "/not-exist-fs/trace.log"
)
c1 := Config{
Name: "foo",
@@ -36,20 +39,42 @@ func TestStartAgent(t *testing.T) {
Batcher: "otlp",
}
c5 := Config{
Name: "grpc",
Name: "otlpgrpc",
Endpoint: endpoint3,
Batcher: kindOtlpGrpc,
OtlpHeaders: map[string]string{
"uptrace-dsn": "http://project2_secret_token@localhost:14317/2",
},
}
c6 := Config{
Name: "otlphttp",
Endpoint: endpoint4,
Batcher: kindOtlpHttp,
OtlpHeaders: map[string]string{
"uptrace-dsn": "http://project2_secret_token@localhost:14318/2",
},
OtlpHttpPath: "/v1/traces",
}
c7 := Config{
Name: "UDP",
Endpoint: endpoint5,
Batcher: kindJaeger,
}
c8 := Config{
Disabled: true,
Endpoint: endpoint6,
Batcher: kindJaeger,
}
c9 := Config{
Name: "file",
Endpoint: endpoint71,
Batcher: kindFile,
}
c10 := Config{
Name: "file",
Endpoint: endpoint72,
Batcher: kindFile,
}
StartAgent(c1)
StartAgent(c1)
@@ -59,12 +84,16 @@ func TestStartAgent(t *testing.T) {
StartAgent(c5)
StartAgent(c6)
StartAgent(c7)
StartAgent(c8)
StartAgent(c9)
StartAgent(c10)
defer StopAgent()
lock.Lock()
defer lock.Unlock()
// because remotehost cannot be resolved
assert.Equal(t, 5, len(agents))
assert.Equal(t, 6, len(agents))
_, ok := agents[""]
assert.True(t, ok)
_, ok = agents[endpoint1]
@@ -73,4 +102,10 @@ func TestStartAgent(t *testing.T) {
assert.False(t, ok)
_, ok = agents[endpoint5]
assert.True(t, ok)
_, ok = agents[endpoint6]
assert.False(t, ok)
_, ok = agents[endpoint71]
assert.True(t, ok)
_, ok = agents[endpoint72]
assert.False(t, ok)
}

View File

@@ -8,5 +8,15 @@ type Config struct {
Name string `json:",optional"`
Endpoint string `json:",optional"`
Sampler float64 `json:",default=1.0"`
Batcher string `json:",default=jaeger,options=jaeger|zipkin|otlpgrpc|otlphttp"`
Batcher string `json:",default=jaeger,options=jaeger|zipkin|otlpgrpc|otlphttp|file"`
// OtlpHeaders represents the headers for OTLP gRPC or HTTP transport.
// For example:
// uptrace-dsn: 'http://project2_secret_token@localhost:14317/2'
OtlpHeaders map[string]string `json:",optional"`
// OtlpHttpPath represents the path for OTLP HTTP transport.
// For example
// /v1/traces
OtlpHttpPath string `json:",optional"`
// Disabled indicates whether StartAgent starts the agent.
Disabled bool `json:",optional"`
}

View File

@@ -16,5 +16,6 @@ func NewInMemoryExporter(t *testing.T) *tracetest.InMemoryExporter {
me.Reset()
})
otel.SetTracerProvider(trace.NewTracerProvider(trace.WithSyncer(me)))
return me
}

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"strings"
"time"
"github.com/fullstorydev/grpcurl"
"github.com/golang/protobuf/jsonpb"
@@ -17,7 +16,6 @@ import (
"github.com/zeromicro/go-zero/rest/httpx"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
)
type (
@@ -25,8 +23,8 @@ type (
Server struct {
*rest.Server
upstreams []Upstream
timeout time.Duration
processHeader func(http.Header) []string
dialer func(conf zrpc.RpcClientConf) zrpc.Client
}
// Option defines the method to customize Server.
@@ -36,8 +34,8 @@ type (
// MustNewServer creates a new gateway server.
func MustNewServer(c GatewayConf, opts ...Option) *Server {
svr := &Server{
Server: rest.MustNewServer(c.RestConf),
upstreams: c.Upstreams,
Server: rest.MustNewServer(c.RestConf),
}
for _, opt := range opts {
opt(svr)
@@ -67,7 +65,13 @@ func (s *Server) build() error {
source <- up
}
}, func(up Upstream, writer mr.Writer[rest.Route], cancel func(error)) {
cli := zrpc.MustNewClient(up.Grpc)
var cli zrpc.Client
if s.dialer != nil {
cli = s.dialer(up.Grpc)
} else {
cli = zrpc.MustNewClient(up.Grpc)
}
source, err := s.createDescriptorSource(cli, up)
if err != nil {
cancel(fmt.Errorf("%s: %w", up.Name, err))
@@ -123,13 +127,9 @@ func (s *Server) buildHandler(source grpcurl.DescriptorSource, resolver jsonpb.A
return
}
timeout := internal.GetTimeout(r.Header, s.timeout)
ctx, can := context.WithTimeout(r.Context(), timeout)
defer can()
w.Header().Set(httpx.ContentType, httpx.JsonContentType)
handler := internal.NewEventHandler(w, resolver)
if err := grpcurl.InvokeRPC(ctx, source, cli.Conn(), rpcPath, s.prepareMetadata(r.Header),
if err := grpcurl.InvokeRPC(r.Context(), source, cli.Conn(), rpcPath, s.prepareMetadata(r.Header),
handler, parser.Next); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
}
@@ -151,8 +151,7 @@ func (s *Server) createDescriptorSource(cli zrpc.Client, up Upstream) (grpcurl.D
return nil, err
}
} else {
refCli := grpc_reflection_v1alpha.NewServerReflectionClient(cli.Conn())
client := grpcreflect.NewClient(context.Background(), refCli)
client := grpcreflect.NewClientAuto(context.Background(), cli.Conn())
source = grpcurl.DescriptorSourceFromServer(context.Background(), client)
}
@@ -160,13 +159,13 @@ func (s *Server) createDescriptorSource(cli zrpc.Client, up Upstream) (grpcurl.D
}
func (s *Server) ensureUpstreamNames() error {
for _, up := range s.upstreams {
target, err := up.Grpc.BuildTarget()
for i := 0; i < len(s.upstreams); i++ {
target, err := s.upstreams[i].Grpc.BuildTarget()
if err != nil {
return err
}
up.Name = target
s.upstreams[i].Name = target
}
return nil
@@ -188,3 +187,10 @@ func WithHeaderProcessor(processHeader func(http.Header) []string) func(*Server)
s.processHeader = processHeader
}
}
// withDialer sets a dialer to create a gRPC client.
func withDialer(dialer func(conf zrpc.RpcClientConf) zrpc.Client) func(*Server) {
return func(s *Server) {
s.dialer = dialer
}
}

127
gateway/server_test.go Normal file
View File

@@ -0,0 +1,127 @@
package gateway
import (
"context"
"log"
"net"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/discov"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/logx/logtest"
"github.com/zeromicro/go-zero/internal/mock"
"github.com/zeromicro/go-zero/rest/httpc"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"google.golang.org/grpc/test/bufconn"
)
func init() {
logx.Disable()
}
func dialer() func(context.Context, string) (net.Conn, error) {
listener := bufconn.Listen(1024 * 1024)
server := grpc.NewServer()
mock.RegisterDepositServiceServer(server, &mock.DepositServer{})
reflection.Register(server)
go func() {
if err := server.Serve(listener); err != nil {
log.Fatal(err)
}
}()
return func(context.Context, string) (net.Conn, error) {
return listener.Dial()
}
}
func TestMustNewServer(t *testing.T) {
var c GatewayConf
assert.NoError(t, conf.FillDefault(&c))
// avoid popup alert on macos for asking permissions
c.DevServer.Host = "localhost"
c.Host = "localhost"
c.Port = 18881
s := MustNewServer(c, withDialer(func(conf zrpc.RpcClientConf) zrpc.Client {
return zrpc.MustNewClient(conf, zrpc.WithDialOption(grpc.WithContextDialer(dialer())))
}), WithHeaderProcessor(func(header http.Header) []string {
return []string{"foo"}
}))
s.upstreams = []Upstream{
{
Mappings: []RouteMapping{
{
Method: "get",
Path: "/deposit/:amount",
RpcPath: "mock.DepositService/Deposit",
},
},
Grpc: zrpc.RpcClientConf{
Endpoints: []string{"foo"},
Timeout: 1000,
Middlewares: zrpc.ClientMiddlewaresConf{
Trace: true,
Duration: true,
Prometheus: true,
Breaker: true,
Timeout: true,
},
},
},
}
assert.NoError(t, s.build())
go s.Server.Start()
defer s.Stop()
time.Sleep(time.Millisecond * 200)
resp, err := httpc.Do(context.Background(), http.MethodGet, "http://localhost:18881/deposit/100", nil)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
resp, err = httpc.Do(context.Background(), http.MethodGet, "http://localhost:18881/deposit_fail/100", nil)
assert.NoError(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
}
func TestServer_ensureUpstreamNames(t *testing.T) {
var s = Server{
upstreams: []Upstream{
{
Grpc: zrpc.RpcClientConf{
Target: "target",
},
},
},
}
assert.NoError(t, s.ensureUpstreamNames())
assert.Equal(t, "target", s.upstreams[0].Name)
}
func TestServer_ensureUpstreamNames_badEtcd(t *testing.T) {
var s = Server{
upstreams: []Upstream{
{
Grpc: zrpc.RpcClientConf{
Etcd: discov.EtcdConf{},
},
},
},
}
logtest.PanicOnFatal(t)
assert.Panics(t, func() {
s.Start()
})
}

72
go.mod
View File

@@ -4,48 +4,48 @@ go 1.18
require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/alicebob/miniredis/v2 v2.30.1
github.com/alicebob/miniredis/v2 v2.30.4
github.com/fatih/color v1.15.0
github.com/felixge/fgprof v0.9.3
github.com/fullstorydev/grpcurl v1.8.7
github.com/go-redis/redis/v8 v8.11.5
github.com/go-sql-driver/mysql v1.7.0
github.com/go-sql-driver/mysql v1.7.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.3
github.com/google/uuid v1.3.0
github.com/jackc/pgx/v5 v5.3.1
github.com/jackc/pgx/v5 v5.4.2
github.com/jhump/protoreflect v1.15.1
github.com/olekukonko/tablewriter v0.0.5
github.com/pelletier/go-toml/v2 v2.0.7
github.com/prometheus/client_golang v1.14.0
github.com/pelletier/go-toml/v2 v2.0.9
github.com/prometheus/client_golang v1.16.0
github.com/spaolacci/murmur3 v1.1.0
github.com/stretchr/testify v1.8.2
go.etcd.io/etcd/api/v3 v3.5.7
go.etcd.io/etcd/client/v3 v3.5.7
go.mongodb.org/mongo-driver v1.11.4
github.com/stretchr/testify v1.8.4
go.etcd.io/etcd/api/v3 v3.5.9
go.etcd.io/etcd/client/v3 v3.5.9
go.mongodb.org/mongo-driver v1.12.0
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/exporters/jaeger v1.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0
go.opentelemetry.io/otel/exporters/zipkin v1.14.0
go.opentelemetry.io/otel/sdk v1.14.0
go.opentelemetry.io/otel/trace v1.14.0
go.uber.org/automaxprocs v1.5.2
go.uber.org/goleak v1.2.1
golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0
golang.org/x/net v0.12.0
golang.org/x/sys v0.10.0
golang.org/x/time v0.3.0
google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197
google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
google.golang.org/grpc v1.56.2
google.golang.org/protobuf v1.31.0
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/h2non/gock.v1 v1.1.2
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.26.3
k8s.io/apimachinery v0.26.3
k8s.io/apimachinery v0.27.0-alpha.3
k8s.io/client-go v0.26.3
k8s.io/utils v0.0.0-20230115233650-391b47cb4029
k8s.io/utils v0.0.0-20230209194617-a36077c30491
)
require (
@@ -61,15 +61,14 @@ require (
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -77,45 +76,44 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/yuin/gopher-lua v1.1.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.6.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/term v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

256
go.sum
View File

@@ -36,19 +36,12 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.30.1 h1:HM1rlQjq1bm9yQcsawJqSZBJ9AYgxvjkMsNtddh90+g=
github.com/alicebob/miniredis/v2 v2.30.1/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo=
github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
@@ -58,7 +51,6 @@ github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -97,8 +89,6 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fullstorydev/grpcurl v1.8.7 h1:xJWosq3BQovQ4QrdPO72OrPiWuGgEsxY8ldYsJbPrqI=
github.com/fullstorydev/grpcurl v1.8.7/go.mod h1:pVtM4qe3CMoLaIzYS8uvTuDj2jVYmXqMUkZeijnXp/E=
@@ -106,42 +96,30 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
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-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -187,7 +165,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@@ -204,8 +181,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -221,13 +196,12 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplb
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jackc/pgx/v5 v5.4.2 h1:u1gmGDwbdRUZiwisBm/Ky2M14uQyUP65bG8+20nnyrg=
github.com/jackc/pgx/v5 v5.4.2/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY=
github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
@@ -237,35 +211,25 @@ github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgf
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -274,105 +238,71 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
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.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A=
github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -380,16 +310,17 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=
go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=
go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=
go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=
go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs=
go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k=
go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE=
go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4=
go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E=
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -407,6 +338,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0/go.mod h1:5w41DY6S9gZrbjuq6Y+753e96WfPha5IcsOSZTtullM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 h1:3jAYbRHQAqzLjd9I4tzxwJ8Pk/N6AqBcF6m1ZHrxG94=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0/go.mod h1:+N7zNjIJv4K+DeX67XXET0P+eIciESgaFDBqh+ZJFS4=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0 h1:sEL90JjOO/4yhquXl5zTAkLLsZ5+MycAgX99SDsxGc8=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.14.0/go.mod h1:oCslUcizYdpKYyS9e8srZEqM6BB8fq41VJBjLAE6z1w=
go.opentelemetry.io/otel/exporters/zipkin v1.14.0 h1:reEVE1upBF9tcujgvSqLJS0SrI7JQPaTKP4s4rymnSs=
go.opentelemetry.io/otel/exporters/zipkin v1.14.0/go.mod h1:RcjvOAcvhzcufQP8aHmzRw1gE9g/VEZufDdo2w+s4sk=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
@@ -426,15 +359,15 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -466,9 +399,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -476,7 +409,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -495,23 +427,20 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -521,18 +450,15 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -541,7 +467,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -554,28 +479,23 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -584,8 +504,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -634,6 +556,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
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=
@@ -694,8 +617,8 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197 h1:BwjeHhu4HS48EZmu1nS7flldBIDPC3qn+HqaSQ1K4x8=
google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -714,8 +637,8 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
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=
@@ -730,14 +653,13 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -746,13 +668,9 @@ gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaD
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/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=
@@ -768,21 +686,21 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU=
k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE=
k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k=
k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=
k8s.io/apimachinery v0.27.0-alpha.3 h1:uujqsdFrbqF+cEbqFHrkLKp+s3XxRgphTpc6Yg84qLo=
k8s.io/apimachinery v0.27.0-alpha.3/go.mod h1:TO4higCGNMwebVSdb1XPJdXMU4kk+nmMY/cTMVCGa6M=
k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s=
k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ=
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
k8s.io/utils v0.0.0-20230115233650-391b47cb4029 h1:L8zDtT4jrxj+TaQYD0k8KNlr556WaVQylDXswKmX+dE=
k8s.io/utils v0.0.0-20230115233650-391b47cb4029/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840 h1:1Q4XWtrQQh04ZweCpL7aMNYafFMoPEiST4dl5b4PmYw=
k8s.io/kube-openapi v0.0.0-20230307230338-69ee2d25a840/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY=
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=

37
internal/dbtest/sql.go Normal file
View File

@@ -0,0 +1,37 @@
package dbtest
import (
"database/sql"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)
// RunTest runs a test function with a mock database.
func RunTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer func() {
_ = db.Close()
}()
fn(db, mock)
if err = mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expectations: %s", err)
}
}
// RunTxTest runs a test function with a mock database in a transaction.
func RunTxTest(t *testing.T, f func(tx *sql.Tx, mock sqlmock.Sqlmock)) {
RunTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectBegin()
tx, err := db.Begin()
if assert.NoError(t, err) {
f(tx, mock)
}
})
}

View File

@@ -4,7 +4,7 @@ package devserver
type Config struct {
Enabled bool `json:",default=true"`
Host string `json:",optional"`
Port int `json:",default=6470"`
Port int `json:",default=6060"`
MetricsPath string `json:",default=/metrics"`
HealthPath string `json:",default=/healthz"`
EnableMetrics bool `json:",default=true"`

View File

@@ -7,9 +7,9 @@ import (
"net/http/pprof"
"sync"
"github.com/felixge/fgprof"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/prometheus"
"github.com/zeromicro/go-zero/core/threading"
"github.com/zeromicro/go-zero/internal/health"
)
@@ -42,11 +42,12 @@ func (s *Server) addRoutes() {
// metrics
if s.config.EnableMetrics {
// enable prometheus global switch
prometheus.Enable()
s.handleFunc(s.config.MetricsPath, promhttp.Handler().ServeHTTP)
}
// pprof
if s.config.EnablePprof {
s.handleFunc("/debug/fgprof", fgprof.Handler().(http.HandlerFunc))
s.handleFunc("/debug/pprof/", pprof.Index)
s.handleFunc("/debug/pprof/cmdline", pprof.Cmdline)
s.handleFunc("/debug/pprof/profile", pprof.Profile)

View File

@@ -53,6 +53,19 @@ func TestComboHealthManager(t *testing.T) {
assert.True(t, chm.IsReady())
})
t.Run("is ready verbose", func(t *testing.T) {
chm := newComboHealthManager()
hm := NewHealthManager(probeName)
assert.True(t, chm.IsReady())
chm.addProbe(hm)
assert.False(t, chm.IsReady())
hm.MarkReady()
assert.True(t, chm.IsReady())
assert.Contains(t, chm.verboseInfo(), probeName)
assert.Contains(t, chm.verboseInfo(), "is ready")
})
t.Run("concurrent add probes", func(t *testing.T) {
chm := newComboHealthManager()

Some files were not shown because too many files have changed in this diff Show More