From 590f0dedd6ca847755c559339d6ec8325855d3e4 Mon Sep 17 00:00:00 2001 From: cjy Date: Wed, 10 Jun 2026 15:20:21 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E4=B8=BA=20taskDao=20=E4=B8=8E=20taskL?= =?UTF-8?q?ogic=20=E6=B7=BB=E5=8A=A0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 internal/testutil/db.go,基于 DATA-DOG/go-sqlmock 提供 SetupTaskBenchDB 帮助函数,把 mock 出来的 *gorm.DB 挂到 app.ModuleClients.TaskBenchDB 上,方便 dao / logic 层单测。 SQL 匹配使用 QueryMatcherRegexp,避免对 gorm 生成的 SQL 字面量 (字段顺序、占位符数量等)过度耦合。 - internal/dao/taskDao_test.go:覆盖 GetPendingTaskLayout 等 DAO 方法的 SQL 行为与错误分支。 - internal/logic/taskLogic_test.go:覆盖 CreateTaskWorkLog 的入参范围校验分支。 go.mod / go.sum: - 新增直接依赖 github.com/DATA-DOG/go-sqlmock v1.5.2、 github.com/stretchr/testify v1.11.1; - 调整 google/uuid、robfig/cron/v3、satori/go.uuid 为直接依赖。 Co-Authored-By: Claude Fable 5 --- go.mod | 11 +- go.sum | 27 +++- internal/dao/taskDao_test.go | 95 +++++++++++++ internal/logic/taskLogic_test.go | 233 +++++++++++++++++++++++++++++++ internal/testutil/db.go | 47 +++++++ 5 files changed, 407 insertions(+), 6 deletions(-) create mode 100644 internal/dao/taskDao_test.go create mode 100644 internal/logic/taskLogic_test.go create mode 100644 internal/testutil/db.go diff --git a/go.mod b/go.mod index cd6c16a..1c41256 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.23.3 require ( dubbo.apache.org/dubbo-go/v3 v3.0.2 + github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 github.com/bwmarrin/snowflake v0.3.0 github.com/dubbogo/grpc-go v1.42.9 @@ -13,18 +14,21 @@ require ( github.com/gin-gonic/gin v1.9.0 github.com/go-redis/redis v6.15.9+incompatible github.com/golang/protobuf v1.5.2 + github.com/google/uuid v1.6.0 github.com/google/wire v0.6.0 github.com/jinzhu/copier v0.3.5 github.com/mwitkow/go-proto-validators v0.3.2 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/opentracing/opentracing-go v1.2.0 + github.com/robfig/cron/v3 v3.0.1 github.com/samber/lo v1.51.0 + github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/shopspring/decimal v1.4.0 github.com/spf13/viper v1.7.1 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 + github.com/stretchr/testify v1.11.1 github.com/uber/jaeger-client-go v2.30.0+incompatible go.uber.org/zap v1.24.0 - google.golang.org/grpc v1.54.0 google.golang.org/protobuf v1.29.1 gorm.io/datatypes v1.2.5 gorm.io/driver/mysql v1.5.6 @@ -81,7 +85,6 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -114,6 +117,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polarismesh/polaris-go v1.1.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.12.2 // indirect @@ -121,8 +125,6 @@ require ( github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/statsd_exporter v0.21.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect github.com/shirou/gopsutil/v3 v3.22.2 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.9.2 // indirect @@ -154,6 +156,7 @@ require ( golang.org/x/text v0.22.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.54.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 84d6b6e..a0e4897 100644 --- a/go.sum +++ b/go.sum @@ -49,7 +49,10 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= @@ -251,6 +254,7 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -294,6 +298,7 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= @@ -329,7 +334,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -396,6 +403,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -522,9 +530,13 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 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-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= @@ -572,6 +584,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/knadh/koanf v1.4.1/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs= @@ -625,10 +638,12 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -719,6 +734,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -813,6 +829,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -852,8 +869,9 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J 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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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= @@ -863,7 +881,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= @@ -1252,6 +1271,7 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1503,9 +1523,12 @@ gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4= gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= +gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g= +gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g= gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/dao/taskDao_test.go b/internal/dao/taskDao_test.go new file mode 100644 index 0000000..f412fa7 --- /dev/null +++ b/internal/dao/taskDao_test.go @@ -0,0 +1,95 @@ +package dao + +import ( + "errors" + "regexp" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gorm.io/gorm" + + "micro-bundle/internal/testutil" +) + +// ---- GetPendingTaskLayout ---- + +// sqlmock + gorm 配合:QueryMatcherEqual 要求 SQL 字面量精确匹配;但 +// gorm 生成的字段顺序与时间字段处理可能因版本略有差异,这里用 Regexp 匹配 +// 关键子串,避免对生成 SQL 的细节过度耦合。 +// +// DAO 测试不用 t.Parallel():SetupTaskBenchDB 会改写全局 app.ModuleClients, +// 并行执行会导致 sqlmock 期望链互相污染。 +var reSelectLayout = regexp.MustCompile(`SELECT \* FROM .task_pending_layout. WHERE id = \?`) +var reInsertLayout = regexp.MustCompile(`INSERT INTO .task_pending_layout. .*\)\s*VALUES \(.*\)\s*ON DUPLICATE KEY UPDATE .data.`) +var reInsertHidden = regexp.MustCompile(`INSERT INTO .task_assignee_hidden.`) + +func TestGetPendingTaskLayout_NotFound(t *testing.T) { + mock := testutil.SetupTaskBenchDB(t) + mock.MatchExpectationsInOrder(false) + + mock.ExpectQuery(reSelectLayout.String()). + WithArgs(1). + WillReturnError(gormNotFound()) + + got, err := GetPendingTaskLayout() + require.Error(t, err) + assert.Empty(t, got) +} + +func TestGetPendingTaskLayout_Success(t *testing.T) { + mock := testutil.SetupTaskBenchDB(t) + mock.MatchExpectationsInOrder(false) + + rows := sqlmock.NewRows([]string{"id", "data"}).AddRow(1, `[{"fieldKey":"subNum"}]`) + // gorm 的 First() 内部会带一个 LIMIT 参数,这里用 .* 兼容。 + mock.ExpectQuery(reSelectLayout.String()). + WillReturnRows(rows) + + got, err := GetPendingTaskLayout() + require.NoError(t, err) + assert.JSONEq(t, `[{"fieldKey":"subNum"}]`, got) +} + +// ---- SetPendingTaskLayout ---- + +func TestSetPendingTaskLayout_Success(t *testing.T) { + mock := testutil.SetupTaskBenchDB(t) + mock.MatchExpectationsInOrder(false) + + // gorm 会在 Create + OnConflict 时开事务,显式声明 Begin/Commit。 + mock.ExpectBegin() + mock.ExpectExec(reInsertLayout.String()). + WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + + err := SetPendingTaskLayout(`[{"fieldKey":"subNum"}]`) + require.NoError(t, err) +} + +// ---- AddHiddenTaskAssignee ---- + +func TestAddHiddenTaskAssignee_Success(t *testing.T) { + mock := testutil.SetupTaskBenchDB(t) + mock.MatchExpectationsInOrder(false) + + mock.ExpectBegin() + mock.ExpectExec(reInsertHidden.String()). + WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + + err := AddHiddenTaskAssignee("员工A", "E001") + require.NoError(t, err) +} + +// ---- helpers ---- + +// gormNotFound 返回 gorm.ErrRecordNotFound。 +// gorm 内部 First()/Find() 命中零行时返回该错误。 +func gormNotFound() error { + return gorm.ErrRecordNotFound +} + +// 保留 errors 包,留给后续事务测试使用 dialError 等 +var _ = errors.New diff --git a/internal/logic/taskLogic_test.go b/internal/logic/taskLogic_test.go new file mode 100644 index 0000000..1aa9e3c --- /dev/null +++ b/internal/logic/taskLogic_test.go @@ -0,0 +1,233 @@ +package logic + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "micro-bundle/internal/dto" +) + +// ---- CreateTaskWorkLog 范围校验 ---- + +func TestCreateTaskWorkLog_Validation(t *testing.T) { + t.Parallel() + + base := func() *dto.CreateTaskWorkLogRequest { + return &dto.CreateTaskWorkLogRequest{ + AssignRecordsUUID: "uuid-1", + WorkUUID: "work-1", + OperationType: 1, // 1-加 + TaskType: 1, // 1-视频 + TaskCount: 1, + } + } + + tests := []struct { + name string + mutate func(r *dto.CreateTaskWorkLogRequest) + wantContain string + }{ + // ReturnError(nil, "参数错误", ...) 实际返回的 msg 统一是 "参数错误", + // 详细说明只在 print 字段(不入错误链),所以断言统一为 "参数错误", + // 区分点放在测试名上。 + {"OperationType为0", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 0 }, "参数错误"}, + {"OperationType为5", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 5 }, "参数错误"}, + {"TaskType为0", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 0 }, "参数错误"}, + {"TaskType为7", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 7 }, "参数错误"}, + {"TaskCount为负", func(r *dto.CreateTaskWorkLogRequest) { r.TaskCount = -1 }, "参数错误"}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + r := base() + tc.mutate(r) + err := CreateTaskWorkLog(r) + require.Error(t, err) + assert.Contains(t, err.Error(), tc.wantContain) + }) + } +} + +// ---- BatchAssignTask 校验 ---- + +func TestBatchAssignTask_EmptyItems(t *testing.T) { + t.Parallel() + + // nil 和 空 slice 都走 `ReturnError(nil, "参数错误", "批量指派项不能为空")`, + // 实际返回的 msg 是 "参数错误"。 + + // nil slice + err := BatchAssignTask(nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "参数错误") + + // 空 slice + err = BatchAssignTask([]*dto.BatchAssignItem{}) + require.Error(t, err) + assert.Contains(t, err.Error(), "参数错误") +} + +func TestBatchAssignTask_NilItem(t *testing.T) { + t.Parallel() + + // 同样 `ReturnError(nil, "参数错误", "存在空的指派项")`。 + err := BatchAssignTask([]*dto.BatchAssignItem{nil}) + require.Error(t, err) + assert.Contains(t, err.Error(), "参数错误") +} + +func TestBatchAssignTask_NegativeCount(t *testing.T) { + t.Parallel() + + make := func(mutate func(*dto.BatchAssignItem)) *dto.BatchAssignItem { + it := &dto.BatchAssignItem{ + SubNum: "S001", + TaskAssignee: "员工A", + TaskAssigneeNum: "E001", + AssignVideoCount: 1, + } + mutate(it) + return it + } + + tests := []struct { + name string + mutate func(*dto.BatchAssignItem) + }{ + {"Video为负", func(it *dto.BatchAssignItem) { it.AssignVideoCount = -1 }}, + {"Post为负", func(it *dto.BatchAssignItem) { it.AssignPostCount = -1 }}, + {"Data为负", func(it *dto.BatchAssignItem) { it.AssignDataCount = -1 }}, + {"VideoScript为负", func(it *dto.BatchAssignItem) { it.AssignVideoScriptCount = -1 }}, + {"Report为负", func(it *dto.BatchAssignItem) { it.AssignReportCount = -1 }}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := BatchAssignTask([]*dto.BatchAssignItem{make(tc.mutate)}) + require.Error(t, err) + assert.Contains(t, err.Error(), "指派数量不能小于0") + }) + } +} + +func TestBatchAssignTask_SingleItemAllZero(t *testing.T) { + t.Parallel() + + it := &dto.BatchAssignItem{SubNum: "S001", TaskAssignee: "员工A", TaskAssigneeNum: "E001"} + err := BatchAssignTask([]*dto.BatchAssignItem{it}) + require.Error(t, err) + assert.Contains(t, err.Error(), "指派数量不能全部为0") +} + +// 注:logic 中"全部指派项数量都为 0"的外层兜底(`if !hasPositive`)实际不可达: +// 循环中每项的"单项全 0"检查会先返回,所以该分支永远走不到。 +// 保留这条注释供后人参考,不写不可达测试。 + +// ---- TerminateTaskByUUID / BatchTerminateTaskByUUIDs ---- + +func TestTerminateTaskByUUID_Empty(t *testing.T) { + t.Parallel() + + err := TerminateTaskByUUID("") + require.Error(t, err) + assert.Contains(t, err.Error(), "参数错误") +} + +func TestBatchTerminateTaskByUUIDs_Empty(t *testing.T) { + t.Parallel() + + success, failed, failedUUIDs, err := BatchTerminateTaskByUUIDs(nil) + require.Error(t, err) + assert.Equal(t, 0, success) + assert.Equal(t, 0, failed) + assert.Nil(t, failedUUIDs) + assert.Contains(t, err.Error(), "参数错误") + + success, failed, failedUUIDs, err = BatchTerminateTaskByUUIDs([]string{}) + require.Error(t, err) + assert.Equal(t, 0, success) + assert.Equal(t, 0, failed) + assert.Nil(t, failedUUIDs) +} + +// ---- RevertTaskCompletionByUUIDItem ---- + +func TestRevertTaskCompletionByUUIDItem_Empty(t *testing.T) { + t.Parallel() + + for _, in := range []string{"", " ", "\t\n"} { + err := RevertTaskCompletionByUUIDItem(in) + require.Error(t, err, "input=%q", in) + assert.Contains(t, err.Error(), "参数错误") + } +} + +// ---- GetTaskActualStatusByUUID ---- + +func TestGetTaskActualStatusByUUID_Empty(t *testing.T) { + t.Parallel() + + for _, in := range []string{"", " "} { + status, err := GetTaskActualStatusByUUID(in) + require.Error(t, err, "input=%q", in) + assert.Equal(t, 0, status) + assert.Contains(t, err.Error(), "查询参数错误") + } +} + +// ---- AddHiddenTaskAssignee ---- + +func TestAddHiddenTaskAssignee_Empty(t *testing.T) { + t.Parallel() + + tests := []struct { + name, assignee, num string + }{ + {"都为空", "", ""}, + {"名称空", "", "E001"}, + {"账号空", "员工A", ""}, + {"全空格", " ", " "}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := AddHiddenTaskAssignee(tc.assignee, tc.num) + require.Error(t, err) + assert.Contains(t, err.Error(), "参数错误") + }) + } +} + +// ---- buildDefaultPendingLayout (私有函数,同包内可直接测) ---- + +func TestBuildDefaultPendingLayout(t *testing.T) { + t.Parallel() + + got := buildDefaultPendingLayout() + require.NotEmpty(t, got) + + var rows []map[string]any + err := json.Unmarshal([]byte(got), &rows) + require.NoError(t, err, "应当是合法 JSON") + + // 33 个字段是当前硬编码的默认值;如未来调整,只改这一个数字。 + assert.Len(t, rows, 33) + + // 每个字段必有 fieldKey / fieldValue / sortOrNot / status + for i, row := range rows { + assert.Contains(t, row, "fieldKey", "第 %d 行缺 fieldKey", i) + assert.Contains(t, row, "fieldValue", "第 %d 行缺 fieldValue", i) + assert.Contains(t, row, "sortOrNot", "第 %d 行缺 sortOrNot", i) + assert.Contains(t, row, "status", "第 %d 行缺 status", i) + } +} + +// GetPendingTaskLayout 的 dao 降级路径(失败时回退默认布局)留作下一阶段, +// 那里需要 sqlmock 配合 dao 验证。纯函数 buildDefaultPendingLayout 已被 +// TestBuildDefaultPendingLayout 覆盖。 \ No newline at end of file diff --git a/internal/testutil/db.go b/internal/testutil/db.go new file mode 100644 index 0000000..e4fbcc3 --- /dev/null +++ b/internal/testutil/db.go @@ -0,0 +1,47 @@ +package testutil + +import ( + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + + "micro-bundle/pkg/app" + "micro-bundle/pkg/db" +) + +// SetupTaskBenchDB 创建一个 sqlmock 驱动的 *gorm.DB,挂到 app.ModuleClients.TaskBenchDB。 +// 返回的 sqlmock 用于在测试中声明期望 SQL,cleanup 自动还原全局单例。 +// +// 使用 QueryMatcherRegexp 匹配,SQL 文本作为正则匹配(支持 .* 等), +// 避免对 gorm 生成 SQL 的细节(字段顺序、占位符数量)过度耦合。 +func SetupTaskBenchDB(t *testing.T) sqlmock.Sqlmock { + t.Helper() + mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherRegexp)) + require.NoError(t, err) + + gormDB, err := gorm.Open(mysql.New(mysql.Config{ + Conn: mockDB, + SkipInitializeWithVersion: true, + }), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, + }, + DisableForeignKeyConstraintWhenMigrating: true, + }) + require.NoError(t, err) + + original := app.ModuleClients + app.ModuleClients = &app.App{ + Lg: zap.NewNop(), + TaskBenchDB: &db.TaskBenchDB{DB: gormDB}, + } + t.Cleanup(func() { app.ModuleClients = original }) + return mock +}