commit 22db9d53ecc923524d62b67ea5c053d0357f0000 Author: 王性驊 Date: Wed Jan 15 13:17:30 2025 +0800 feat: init project diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/app-cloudep-comment-server.iml b/.idea/app-cloudep-comment-server.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/app-cloudep-comment-server.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..8859eba --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3315139 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87fad30 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +# go-zero 生成風格 +GO_ZERO_STYLE=go_zero +GO ?= go +GOFMT ?= gofmt "-s" +GOFILES := $(shell find . -name "*.go") +LDFLAGS := -s -w +VERSION="v1.0.4" +DOCKER_REPO="igs170911/comment" +GIT_COMMIT ?= $(shell git rev-parse --short HEAD) + +.PHONY: test +test: # 進行測試 + go test -v --cover ./... + +.PHONY: fmt +fmt: # 格式優化 + $(GOFMT) -w $(GOFILES) + goimports -w ./ + golangci-lint run + +.PHONY: gen-rpc +gen-rpc: # 建立 rpc code + goctl rpc protoc ./generate/protobuf/comment.proto -m --style=$(GO_ZERO_STYLE) --go_out=./gen_result/pb --go-grpc_out=./gen_result/pb --zrpc_out=. + go mod tidy + @echo "Generate core-api files successfully" + +.PHONY: mock-gen +mock-gen: # 建立 mock 資料 + mockgen -source=./pkg/domain/repository/comment.go -destination=./pkg/mock/repository/comment.go -package=mock + + @echo "Generate mock files successfully" diff --git a/client/comment/comment.go b/client/comment/comment.go new file mode 100644 index 0000000..834107a --- /dev/null +++ b/client/comment/comment.go @@ -0,0 +1,39 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.7.3 +// Source: comment.proto + +package comment + +import ( + "context" + + "app-cloudep-comment-server/gen_result/pb/comment" + + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" +) + +type ( + NoneReq = comment.NoneReq + OKResp = comment.OKResp + Pager = comment.Pager + + Comment interface { + Empty(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) + } + + defaultComment struct { + cli zrpc.Client + } +) + +func NewComment(cli zrpc.Client) Comment { + return &defaultComment{ + cli: cli, + } +} + +func (m *defaultComment) Empty(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) { + client := comment.NewCommentClient(m.cli.Conn()) + return client.Empty(ctx, in, opts...) +} diff --git a/comment.go b/comment.go new file mode 100644 index 0000000..30c7f02 --- /dev/null +++ b/comment.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + "fmt" + + "app-cloudep-comment-server/gen_result/pb/comment" + "app-cloudep-comment-server/internal/config" + commentServer "app-cloudep-comment-server/internal/server/comment" + "app-cloudep-comment-server/internal/svc" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/core/service" + "github.com/zeromicro/go-zero/zrpc" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var configFile = flag.String("f", "etc/comment.yaml", "the config file") + +func main() { + flag.Parse() + + var c config.Config + conf.MustLoad(*configFile, &c) + ctx := svc.NewServiceContext(c) + + s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { + comment.RegisterCommentServer(grpcServer, commentServer.NewCommentServer(ctx)) + + if c.Mode == service.DevMode || c.Mode == service.TestMode { + reflection.Register(grpcServer) + } + }) + defer s.Stop() + + fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) + s.Start() +} diff --git a/etc/comment.yaml b/etc/comment.yaml new file mode 100644 index 0000000..88d1cf4 --- /dev/null +++ b/etc/comment.yaml @@ -0,0 +1,6 @@ +Name: comment.rpc +ListenOn: 0.0.0.0:8080 +Etcd: + Hosts: + - 127.0.0.1:2379 + Key: comment.rpc diff --git a/gen_result/pb/comment/comment.pb.go b/gen_result/pb/comment/comment.pb.go new file mode 100644 index 0000000..17289c2 --- /dev/null +++ b/gen_result/pb/comment/comment.pb.go @@ -0,0 +1,227 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.1 +// protoc v3.19.4 +// source: generate/protobuf/comment.proto + +package comment + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// OKResp +type OKResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OKResp) Reset() { + *x = OKResp{} + mi := &file_generate_protobuf_comment_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OKResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OKResp) ProtoMessage() {} + +func (x *OKResp) ProtoReflect() protoreflect.Message { + mi := &file_generate_protobuf_comment_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OKResp.ProtoReflect.Descriptor instead. +func (*OKResp) Descriptor() ([]byte, []int) { + return file_generate_protobuf_comment_proto_rawDescGZIP(), []int{0} +} + +// NoneReq +type NoneReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NoneReq) Reset() { + *x = NoneReq{} + mi := &file_generate_protobuf_comment_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NoneReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NoneReq) ProtoMessage() {} + +func (x *NoneReq) ProtoReflect() protoreflect.Message { + mi := &file_generate_protobuf_comment_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NoneReq.ProtoReflect.Descriptor instead. +func (*NoneReq) Descriptor() ([]byte, []int) { + return file_generate_protobuf_comment_proto_rawDescGZIP(), []int{1} +} + +type Pager struct { + state protoimpl.MessageState `protogen:"open.v1"` + Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` + Index int64 `protobuf:"varint,3,opt,name=index,proto3" json:"index,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Pager) Reset() { + *x = Pager{} + mi := &file_generate_protobuf_comment_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Pager) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pager) ProtoMessage() {} + +func (x *Pager) ProtoReflect() protoreflect.Message { + mi := &file_generate_protobuf_comment_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pager.ProtoReflect.Descriptor instead. +func (*Pager) Descriptor() ([]byte, []int) { + return file_generate_protobuf_comment_proto_rawDescGZIP(), []int{2} +} + +func (x *Pager) GetTotal() int64 { + if x != nil { + return x.Total + } + return 0 +} + +func (x *Pager) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Pager) GetIndex() int64 { + if x != nil { + return x.Index + } + return 0 +} + +var File_generate_protobuf_comment_proto protoreflect.FileDescriptor + +var file_generate_protobuf_comment_proto_rawDesc = []byte{ + 0x0a, 0x1f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x08, 0x0a, 0x06, 0x4f, 0x4b, + 0x52, 0x65, 0x73, 0x70, 0x22, 0x09, 0x0a, 0x07, 0x4e, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x22, + 0x47, 0x0a, 0x05, 0x50, 0x61, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x32, 0x35, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x10, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4f, 0x4b, 0x52, 0x65, 0x73, 0x70, 0x42, + 0x0b, 0x5a, 0x09, 0x2e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_generate_protobuf_comment_proto_rawDescOnce sync.Once + file_generate_protobuf_comment_proto_rawDescData = file_generate_protobuf_comment_proto_rawDesc +) + +func file_generate_protobuf_comment_proto_rawDescGZIP() []byte { + file_generate_protobuf_comment_proto_rawDescOnce.Do(func() { + file_generate_protobuf_comment_proto_rawDescData = protoimpl.X.CompressGZIP(file_generate_protobuf_comment_proto_rawDescData) + }) + return file_generate_protobuf_comment_proto_rawDescData +} + +var file_generate_protobuf_comment_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_generate_protobuf_comment_proto_goTypes = []any{ + (*OKResp)(nil), // 0: comment.OKResp + (*NoneReq)(nil), // 1: comment.NoneReq + (*Pager)(nil), // 2: comment.Pager +} +var file_generate_protobuf_comment_proto_depIdxs = []int32{ + 1, // 0: comment.Comment.Empty:input_type -> comment.NoneReq + 0, // 1: comment.Comment.Empty:output_type -> comment.OKResp + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_generate_protobuf_comment_proto_init() } +func file_generate_protobuf_comment_proto_init() { + if File_generate_protobuf_comment_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_generate_protobuf_comment_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_generate_protobuf_comment_proto_goTypes, + DependencyIndexes: file_generate_protobuf_comment_proto_depIdxs, + MessageInfos: file_generate_protobuf_comment_proto_msgTypes, + }.Build() + File_generate_protobuf_comment_proto = out.File + file_generate_protobuf_comment_proto_rawDesc = nil + file_generate_protobuf_comment_proto_goTypes = nil + file_generate_protobuf_comment_proto_depIdxs = nil +} diff --git a/gen_result/pb/comment/comment_grpc.pb.go b/gen_result/pb/comment/comment_grpc.pb.go new file mode 100644 index 0000000..3d2109b --- /dev/null +++ b/gen_result/pb/comment/comment_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.19.4 +// source: generate/protobuf/comment.proto + +package comment + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Comment_Empty_FullMethodName = "/comment.Comment/Empty" +) + +// CommentClient is the client API for Comment service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CommentClient interface { + Empty(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) +} + +type commentClient struct { + cc grpc.ClientConnInterface +} + +func NewCommentClient(cc grpc.ClientConnInterface) CommentClient { + return &commentClient{cc} +} + +func (c *commentClient) Empty(ctx context.Context, in *NoneReq, opts ...grpc.CallOption) (*OKResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(OKResp) + err := c.cc.Invoke(ctx, Comment_Empty_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CommentServer is the server API for Comment service. +// All implementations must embed UnimplementedCommentServer +// for forward compatibility. +type CommentServer interface { + Empty(context.Context, *NoneReq) (*OKResp, error) + mustEmbedUnimplementedCommentServer() +} + +// UnimplementedCommentServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCommentServer struct{} + +func (UnimplementedCommentServer) Empty(context.Context, *NoneReq) (*OKResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Empty not implemented") +} +func (UnimplementedCommentServer) mustEmbedUnimplementedCommentServer() {} +func (UnimplementedCommentServer) testEmbeddedByValue() {} + +// UnsafeCommentServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CommentServer will +// result in compilation errors. +type UnsafeCommentServer interface { + mustEmbedUnimplementedCommentServer() +} + +func RegisterCommentServer(s grpc.ServiceRegistrar, srv CommentServer) { + // If the following call pancis, it indicates UnimplementedCommentServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Comment_ServiceDesc, srv) +} + +func _Comment_Empty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NoneReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CommentServer).Empty(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Comment_Empty_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CommentServer).Empty(ctx, req.(*NoneReq)) + } + return interceptor(ctx, in, info, handler) +} + +// Comment_ServiceDesc is the grpc.ServiceDesc for Comment service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Comment_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "comment.Comment", + HandlerType: (*CommentServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Empty", + Handler: _Comment_Empty_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "generate/protobuf/comment.proto", +} diff --git a/generate/protobuf/comment.proto b/generate/protobuf/comment.proto new file mode 100644 index 0000000..92cdff2 --- /dev/null +++ b/generate/protobuf/comment.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package comment; +option go_package="./comment"; +// OKResp +message OKResp {} +// NoneReq +message NoneReq {} + +message Pager { + int64 total =1; + int64 size=2; + int64 index=3; +} + + +service Comment { + rpc Empty(NoneReq) returns(OKResp); +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..078f568 --- /dev/null +++ b/go.mod @@ -0,0 +1,140 @@ +module app-cloudep-comment-server + +go 1.23.4 + +require ( + code.30cm.net/digimon/library-go/errs v1.2.13 + code.30cm.net/digimon/library-go/mongo v0.0.9 + github.com/alicebob/miniredis/v2 v2.34.0 + github.com/golang/snappy v0.0.4 + github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.34.0 + github.com/zeromicro/go-zero v1.7.6 + go.mongodb.org/mongo-driver v1.17.1 + go.uber.org/mock v0.5.0 + google.golang.org/grpc v1.69.2 + google.golang.org/protobuf v1.36.2 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v27.1.1+incompatible // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // 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.20 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect + github.com/moby/term v0.5.0 // 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.7.1 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/go-redis/v9 v9.7.0 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // 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-20240726163527-a2c0da244d78 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.etcd.io/etcd/api/v3 v3.5.15 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect + go.etcd.io/etcd/client/v3 v3.5.15 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.8.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.29.3 // indirect + k8s.io/apimachinery v0.29.4 // indirect + k8s.io/client-go v0.29.3 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..de9578e --- /dev/null +++ b/go.sum @@ -0,0 +1,397 @@ +code.30cm.net/digimon/library-go/errs v1.2.13 h1:0HC13nwYvmGTXbF8+cjdCmFww1yHcDChJfbsWN9ZKCk= +code.30cm.net/digimon/library-go/errs v1.2.13/go.mod h1:Hs4v7SbXNggDVBGXSYsFMjkii1qLF+rugrIpWePN4/o= +code.30cm.net/digimon/library-go/mongo v0.0.9 h1:fPciIE5B85tXpLg8aeVQqKVbLnfpVAk9xbMu7pE2tVw= +code.30cm.net/digimon/library-go/mongo v0.0.9/go.mod h1:KBVKz/Ci5IheI77BgZxPUeKkaGvDy8fV8EDHSCOLIO4= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE= +github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis/v2 v2.34.0 h1:mBFWMaJSNL9RwdGRyEDoAAv8OQc5UlEhLDQggTglU/0= +github.com/alicebob/miniredis/v2 v2.34.0/go.mod h1:kWShP4b58T1CW0Y5dViCd5ztzrDqRWqM3nksiyXk5s8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-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-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.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +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.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +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/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +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/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/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/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +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.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-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.1.27/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.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zeromicro/go-zero v1.7.6 h1:SArK4xecdrpVY3ZFJcbc0IZCx+NuWyHNjCv9f1+Gwrc= +github.com/zeromicro/go-zero v1.7.6/go.mod h1:SmGykRm5e0Z4CGNj+GaSKDffaHzQV56fel0FkymTLlE= +go.etcd.io/etcd/api/v3 v3.5.15 h1:3KpLJir1ZEBrYuV2v+Twaa/e2MdDCEZ/70H+lzEiwsk= +go.etcd.io/etcd/api/v3 v3.5.15/go.mod h1:N9EhGzXq58WuMllgH9ZvnEr7SI9pS0k0+DHZezGp7jM= +go.etcd.io/etcd/client/pkg/v3 v3.5.15 h1:fo0HpWz/KlHGMCC+YejpiCmyWDEuIpnTDzpJLB5fWlA= +go.etcd.io/etcd/client/pkg/v3 v3.5.15/go.mod h1:mXDI4NAOwEiszrHCb0aqfAYNCrZP4e9hRca3d1YK8EU= +go.etcd.io/etcd/client/v3 v3.5.15 h1:23M0eY4Fd/inNv1ZfU3AxrbbOdW79r9V9Rl62Nm6ip4= +go.etcd.io/etcd/client/v3 v3.5.15/go.mod h1:CLSJxrYjvLtHsrPKsy7LmZEE+DK2ktfd2bN4RhBMwlU= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0 h1:3evrL5poBuh1KF51D9gO/S+N/1msnm4DaBqs/rpXUqY= +go.opentelemetry.io/otel/exporters/zipkin v1.24.0/go.mod h1:0EHgD8R0+8yRhUYJOGR8Hfg2dpiJQxDOszd5smVO9wM= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20220715151400-c0bba94af5f8/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +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/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= +google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q= +k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +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.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100755 index 0000000..c1f85b9 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,7 @@ +package config + +import "github.com/zeromicro/go-zero/zrpc" + +type Config struct { + zrpc.RpcServerConf +} diff --git a/internal/logic/comment/empty_logic.go b/internal/logic/comment/empty_logic.go new file mode 100644 index 0000000..176a319 --- /dev/null +++ b/internal/logic/comment/empty_logic.go @@ -0,0 +1,30 @@ +package commentlogic + +import ( + "context" + + "app-cloudep-comment-server/gen_result/pb/comment" + "app-cloudep-comment-server/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" +) + +type EmptyLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewEmptyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *EmptyLogic { + return &EmptyLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +func (l *EmptyLogic) Empty(in *comment.NoneReq) (*comment.OKResp, error) { + // todo: add your logic here and delete this line + + return &comment.OKResp{}, nil +} diff --git a/internal/server/comment/comment_server.go b/internal/server/comment/comment_server.go new file mode 100644 index 0000000..61ff119 --- /dev/null +++ b/internal/server/comment/comment_server.go @@ -0,0 +1,29 @@ +// Code generated by goctl. DO NOT EDIT. +// goctl 1.7.3 +// Source: comment.proto + +package server + +import ( + "context" + + "app-cloudep-comment-server/gen_result/pb/comment" + "app-cloudep-comment-server/internal/logic/comment" + "app-cloudep-comment-server/internal/svc" +) + +type CommentServer struct { + svcCtx *svc.ServiceContext + comment.UnimplementedCommentServer +} + +func NewCommentServer(svcCtx *svc.ServiceContext) *CommentServer { + return &CommentServer{ + svcCtx: svcCtx, + } +} + +func (s *CommentServer) Empty(ctx context.Context, in *comment.NoneReq) (*comment.OKResp, error) { + l := commentlogic.NewEmptyLogic(ctx, s.svcCtx) + return l.Empty(in) +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go new file mode 100644 index 0000000..10f7b78 --- /dev/null +++ b/internal/svc/service_context.go @@ -0,0 +1,13 @@ +package svc + +import "app-cloudep-comment-server/internal/config" + +type ServiceContext struct { + Config config.Config +} + +func NewServiceContext(c config.Config) *ServiceContext { + return &ServiceContext{ + Config: c, + } +} diff --git a/pkg/domain/comment/level.go b/pkg/domain/comment/level.go new file mode 100644 index 0000000..6728e74 --- /dev/null +++ b/pkg/domain/comment/level.go @@ -0,0 +1,13 @@ +package comment + +type Level int8 + +// ToInt8 將訂單狀態轉為 int64 +func (s Level) ToInt8() int8 { + return int8(s) +} + +const ( + TopLevelComment Level = iota + SubLevelComment +) diff --git a/pkg/domain/entity/comment.go b/pkg/domain/entity/comment.go new file mode 100644 index 0000000..61bb566 --- /dev/null +++ b/pkg/domain/entity/comment.go @@ -0,0 +1,22 @@ +package entity + +import ( + "app-cloudep-comment-server/pkg/domain/comment" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// Comments 只有一層的回應,沒有巢狀回覆 +type Comments struct { + ID primitive.ObjectID `bson:"_id,omitempty"` // MongoDB 的 ObjectID + UID string `bson:"uid"` // 留言的使用者的 ID + ReferenceID string `bson:"reference_id"` // 參考的 id,例如 product_id + ParentCommentID string `bson:"parent_comment_id"` // 父留言 id,沒有就空白 + Message string `bson:"message"` // 留言內容 + Level comment.Level `bson:"level"` // 等級,數字較小的為上層 + UpdatedAt int64 `bson:"updated_at"` // 更新時間 + CreatedAt int64 `bson:"created_at"` // 建立時間 +} + +func (c *Comments) CollectionName() string { + return "comments" +} diff --git a/pkg/domain/error.go b/pkg/domain/error.go new file mode 100644 index 0000000..771224f --- /dev/null +++ b/pkg/domain/error.go @@ -0,0 +1,12 @@ +package domain + +import "code.30cm.net/digimon/library-go/errs" + +const ( + _ = iota + CreateCommentErrorCode errs.ErrorCode = iota + GetCommentErrorCode + DelCommentErrorCode + ListCommentErrorCode + UpdateCommentErrorCode +) diff --git a/pkg/domain/redis.go b/pkg/domain/redis.go new file mode 100755 index 0000000..5bbb3a5 --- /dev/null +++ b/pkg/domain/redis.go @@ -0,0 +1,23 @@ +package domain + +import "strings" + +type RedisKey string + +const ( + CommentRedisKey RedisKey = "comment" +) + +func (key RedisKey) ToString() string { + return "comment:" + string(key) +} + +func (key RedisKey) With(s ...string) RedisKey { + parts := append([]string{string(key)}, s...) + + return RedisKey(strings.Join(parts, ":")) +} + +func GetCommentRedisKey(id string) string { + return CommentRedisKey.With(id).ToString() +} diff --git a/pkg/domain/repository/comment.go b/pkg/domain/repository/comment.go new file mode 100644 index 0000000..971e224 --- /dev/null +++ b/pkg/domain/repository/comment.go @@ -0,0 +1,22 @@ +package repository + +import ( + "app-cloudep-comment-server/pkg/domain/comment" + "app-cloudep-comment-server/pkg/domain/entity" + "context" +) + +type CommentRepository interface { + CreateComment(ctx context.Context, comment *entity.Comments) error // 新增留言 // 根據 ID 獲取單條留言 + DeleteCommentByID(ctx context.Context, id string) error // 根據 ID 刪除留言 // 刪除某留言的所有子留言 + UpdateCommentMessage(ctx context.Context, id string, message string) error // 更新留言內容 + ListComments(ctx context.Context, req ListCommentRequest) ([]*entity.Comments, int64, error) // 分頁列出留言 +} + +type ListCommentRequest struct { + PageSize int64 + PageIndex int64 + ReferenceID *string + ParentID *string + Level comment.Level +} diff --git a/pkg/domain/usecase/comment.go b/pkg/domain/usecase/comment.go new file mode 100644 index 0000000..1d10ca1 --- /dev/null +++ b/pkg/domain/usecase/comment.go @@ -0,0 +1,44 @@ +package usecase + +import ( + "context" +) + +type CommentUseCase interface { + // CreateCommentItem 建立 Comment + CreateCommentItem(ctx context.Context, info CreateCommentDocs) error + // GetCommentItem 取得CommentItem + GetCommentItem(ctx context.Context, id string) (*CommentDocs, error) + // ListComment 取得 CommentItem 列表 + ListComment(ctx context.Context, req ListCommentRequest) ([]*CommentDocs, int64, error) + // RemoveCommentItem 移除 CommentItem 列表 + RemoveCommentItem(ctx context.Context, id string) error + // UpdateCommentMessage 更新 + UpdateCommentMessage(ctx context.Context, id string, message string) error +} + +type CreateCommentDocs struct { + UID string `bson:"uid"` // 留言的使用者的 ID + ReferenceID string `bson:"reference_id"` // 參考的 id ex product_id or product_item_id 之類的 + ParentCommentID string `bson:"parent_comment_id"` // 父留言 id 沒有就空白 + Message string `bson:"message"` // 留言內容 +} + +type CommentDocs struct { + ID string `json:"id"` + UID string `json:"uid"` // 留言的使用者的 ID + ReferenceID string `json:"reference_id"` // 參考的 id ex product_id or product_item_id 之類的 + ParentCommentID string `json:"parent_comment_id"` // 父留言 id 沒有就空白 + Message string `json:"message"` // 留言內容 + Replies []*CommentDocs `json:"replies"` // 子留言 + UpdatedAt int64 `json:"updated_at"` // 更新時間 + CreatedAt int64 `json:"created_at"` // 建立時間 +} + +type ListCommentRequest struct { + PageSize int64 + PageIndex int64 + ReferenceID *string + ParentID *string + IsSub bool +} diff --git a/pkg/mock/repository/comment.go b/pkg/mock/repository/comment.go new file mode 100644 index 0000000..34af9c0 --- /dev/null +++ b/pkg/mock/repository/comment.go @@ -0,0 +1,101 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/domain/repository/comment.go +// +// Generated by this command: +// +// mockgen -source=./pkg/domain/repository/comment.go -destination=./pkg/mock/repository/comment.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + entity "app-cloudep-comment-server/pkg/domain/entity" + repository "app-cloudep-comment-server/pkg/domain/repository" + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" +) + +// MockCommentRepository is a mock of CommentRepository interface. +type MockCommentRepository struct { + ctrl *gomock.Controller + recorder *MockCommentRepositoryMockRecorder + isgomock struct{} +} + +// MockCommentRepositoryMockRecorder is the mock recorder for MockCommentRepository. +type MockCommentRepositoryMockRecorder struct { + mock *MockCommentRepository +} + +// NewMockCommentRepository creates a new mock instance. +func NewMockCommentRepository(ctrl *gomock.Controller) *MockCommentRepository { + mock := &MockCommentRepository{ctrl: ctrl} + mock.recorder = &MockCommentRepositoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCommentRepository) EXPECT() *MockCommentRepositoryMockRecorder { + return m.recorder +} + +// CreateComment mocks base method. +func (m *MockCommentRepository) CreateComment(ctx context.Context, comment *entity.Comments) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateComment", ctx, comment) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateComment indicates an expected call of CreateComment. +func (mr *MockCommentRepositoryMockRecorder) CreateComment(ctx, comment any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateComment", reflect.TypeOf((*MockCommentRepository)(nil).CreateComment), ctx, comment) +} + +// DeleteCommentByID mocks base method. +func (m *MockCommentRepository) DeleteCommentByID(ctx context.Context, id string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCommentByID", ctx, id) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCommentByID indicates an expected call of DeleteCommentByID. +func (mr *MockCommentRepositoryMockRecorder) DeleteCommentByID(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCommentByID", reflect.TypeOf((*MockCommentRepository)(nil).DeleteCommentByID), ctx, id) +} + +// ListComments mocks base method. +func (m *MockCommentRepository) ListComments(ctx context.Context, req repository.ListCommentRequest) ([]*entity.Comments, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListComments", ctx, req) + ret0, _ := ret[0].([]*entity.Comments) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListComments indicates an expected call of ListComments. +func (mr *MockCommentRepositoryMockRecorder) ListComments(ctx, req any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListComments", reflect.TypeOf((*MockCommentRepository)(nil).ListComments), ctx, req) +} + +// UpdateCommentMessage mocks base method. +func (m *MockCommentRepository) UpdateCommentMessage(ctx context.Context, id, message string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateCommentMessage", ctx, id, message) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateCommentMessage indicates an expected call of UpdateCommentMessage. +func (mr *MockCommentRepositoryMockRecorder) UpdateCommentMessage(ctx, id, message any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCommentMessage", reflect.TypeOf((*MockCommentRepository)(nil).UpdateCommentMessage), ctx, id, message) +} diff --git a/pkg/repository/comment.go b/pkg/repository/comment.go new file mode 100644 index 0000000..04d8600 --- /dev/null +++ b/pkg/repository/comment.go @@ -0,0 +1,138 @@ +package repository + +import ( + "app-cloudep-comment-server/pkg/domain" + "app-cloudep-comment-server/pkg/domain/entity" + "app-cloudep-comment-server/pkg/domain/repository" + mgo "code.30cm.net/digimon/library-go/mongo" + "context" + "errors" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/mon" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" + "time" +) + +type CommentRepositoryParam struct { + Conf *mgo.Conf + CacheConf cache.CacheConf + DBOpts []mon.Option + CacheOpts []cache.Option +} + +type CommentRepository struct { + DB mgo.DocumentDBWithCacheUseCase +} + +func MustCommentRepository(param CommentRepositoryParam) repository.CommentRepository { + e := entity.Comments{} + documentDB, err := mgo.MustDocumentDBWithCache( + param.Conf, + e.CollectionName(), + param.CacheConf, + param.DBOpts, + param.CacheOpts, + ) + if err != nil { + panic(err) + } + + return &CommentRepository{ + DB: documentDB, + } +} + +func (repo *CommentRepository) CreateComment(ctx context.Context, comment *entity.Comments) error { + now := time.Now().UTC().UnixNano() + if comment.ID.IsZero() { + comment.ID = primitive.NewObjectID() + comment.CreatedAt = now + comment.UpdatedAt = now + } + + rk := domain.GetCommentRedisKey(comment.ID.Hex()) + _, err := repo.DB.InsertOne(ctx, rk, comment) + + return err +} + +func (repo *CommentRepository) DeleteCommentByID(ctx context.Context, id string) error { + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return ErrInvalidObjectID + } + + rk := domain.GetCommentRedisKey(id) + _, err = repo.DB.DeleteOne(ctx, rk, bson.M{"_id": oid}) + + return err +} + +func (repo *CommentRepository) UpdateCommentMessage(ctx context.Context, id string, message string) error { + // 驗證 ObjectID 是否有效 + oid, err := primitive.ObjectIDFromHex(id) + if err != nil { + return ErrInvalidObjectID + } + + // 初始化更新字段 + updates := bson.M{ + "message": message, // 更新留言內容 + "updated_at": time.Now().UTC().UnixNano(), // 更新時間 + } + + // 查詢條件:根據 _id 查詢 + filter := bson.M{ + "_id": oid, + } + + rk := domain.GetCommentRedisKey(id) + // 執行更新操作 + res, err := repo.DB.UpdateOne(ctx, rk, filter, bson.M{"$set": updates}) + if err != nil { + return err + } + + // 驗證是否有匹配的記錄被更新 + if res.MatchedCount == 0 { + return ErrNotFound + } + + return nil +} + +func (repo *CommentRepository) ListComments(ctx context.Context, req repository.ListCommentRequest) ([]*entity.Comments, int64, error) { + // 構建查詢過濾器 + filter := bson.M{} + if req.ParentID != nil { + filter["parent_comment_id"] = req.ParentID + } + if req.ReferenceID != nil { + filter["reference_id"] = req.ReferenceID + } + + // 設置排序選項 + opts := options.Find().SetSkip((req.PageIndex - 1) * req.PageSize).SetLimit(req.PageSize) + opts.SetSort(bson.D{{Key: "created_at", Value: -1}}) + + // 查詢符合條件的總數 + count, err := repo.DB.GetClient().CountDocuments(ctx, filter) + if err != nil { + return nil, 0, err + } + + // 執行查詢並獲取結果 + var data []*entity.Comments + err = repo.DB.GetClient().Find(ctx, &data, filter, opts) + + switch { + case err == nil: + return data, count, nil + case errors.Is(err, mon.ErrNotFound): + return nil, 0, ErrNotFound + default: + return nil, 0, err + } +} diff --git a/pkg/repository/comment_test.go b/pkg/repository/comment_test.go new file mode 100644 index 0000000..2467985 --- /dev/null +++ b/pkg/repository/comment_test.go @@ -0,0 +1,385 @@ +package repository + +import ( + "app-cloudep-comment-server/pkg/domain/entity" + "app-cloudep-comment-server/pkg/domain/repository" + mgo "code.30cm.net/digimon/library-go/mongo" + "context" + "fmt" + "github.com/alicebob/miniredis/v2" + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/stores/cache" + "github.com/zeromicro/go-zero/core/stores/mon" + "github.com/zeromicro/go-zero/core/stores/redis" + "go.mongodb.org/mongo-driver/bson/primitive" + "google.golang.org/protobuf/proto" + "testing" + "time" +) + +func SetupTestCommentRepository(db string) (repository.CommentRepository, func(), error) { + h, p, tearDown, err := startMongoContainer() + if err != nil { + return nil, nil, err + } + s, _ := miniredis.Run() + + conf := &mgo.Conf{ + Schema: Schema, + Host: fmt.Sprintf("%s:%s", h, p), + Database: db, + MaxStaleness: 300, + MaxPoolSize: 100, + MinPoolSize: 100, + MaxConnIdleTime: 300, + Compressors: []string{}, + EnableStandardReadWriteSplitMode: false, + ConnectTimeoutMs: 3000, + } + + cacheConf := cache.CacheConf{ + cache.NodeConf{ + RedisConf: redis.RedisConf{ + Host: s.Addr(), + Type: redis.NodeType, + }, + Weight: 100, + }, + } + + cacheOpts := []cache.Option{ + cache.WithExpiry(1000 * time.Microsecond), + cache.WithNotFoundExpiry(1000 * time.Microsecond), + } + + dbOpts := []mon.Option{ + mgo.SetCustomDecimalType(), + mgo.InitMongoOptions(*conf), + } + + param := CommentRepositoryParam{ + Conf: conf, + CacheConf: cacheConf, + CacheOpts: cacheOpts, + DBOpts: dbOpts, + } + repo := MustCommentRepository(param) + //_, _ = repo.Index20241230001UP(context.Background()) + + return repo, tearDown, nil +} + +func TestCommentRepository_CreateCommentItem(t *testing.T) { + repo, tearDown, err := SetupTestCommentRepository("testDB") + assert.NoError(t, err) + defer tearDown() + + tests := []struct { + name string + input *entity.Comments + expectError bool + }{ + { + name: "Valid comment creation", + input: &entity.Comments{ + UID: "test-uid", + ReferenceID: "ref-001", + ParentCommentID: "", + Message: "This is a test comment", + }, + expectError: false, + }, + { + name: "Comment with existing ID", + input: &entity.Comments{ + ID: primitive.NewObjectID(), + UID: "test-uid", + ReferenceID: "ref-002", + ParentCommentID: "", + Message: "This is another test comment", + }, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := repo.CreateComment(context.Background(), tt.input) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + + // 驗證插入的數據是否存在於資料庫中 + result, _, err := repo.ListComments(context.Background(), repository.ListCommentRequest{ + PageSize: 10, + PageIndex: 1, + ReferenceID: &tt.input.ReferenceID, + }) + assert.NoError(t, err) + assert.NotNil(t, result, "應該能找到插入的留言") + assert.Equal(t, tt.input.UID, result[0].UID, "UID 應該匹配") + assert.Equal(t, tt.input.ReferenceID, result[0].ReferenceID, "ReferenceID 應該匹配") + assert.Equal(t, tt.input.Message, result[0].Message, "Message 應該匹配") + } + }) + } +} + +func TestCommentRepository_ListComment(t *testing.T) { + repo, tearDown, err := SetupTestCommentRepository("testDB") + assert.NoError(t, err) + defer tearDown() + + // 插入測試留言資料 + testComments := []entity.Comments{ + { + ID: primitive.NewObjectID(), + UID: "user1", + ReferenceID: "ref-001", + ParentCommentID: "", + Message: "This is a parent comment", + CreatedAt: time.Now().Add(-10 * time.Minute).UnixNano(), + }, + { + ID: primitive.NewObjectID(), + UID: "user2", + ReferenceID: "ref-001", + ParentCommentID: "", + Message: "This is another parent comment", + CreatedAt: time.Now().Add(-5 * time.Minute).UnixNano(), + }, + { + ID: primitive.NewObjectID(), + UID: "user3", + ReferenceID: "ref-002", + ParentCommentID: "parent-id", + Message: "This is a child comment", + CreatedAt: time.Now().Add(-3 * time.Minute).UnixNano(), + }, + } + + for _, comment := range testComments { + err := repo.CreateComment(context.Background(), &comment) + assert.NoError(t, err, "插入應成功") + } + + tests := []struct { + name string + req repository.ListCommentRequest + expectedTotal int64 + expectedIDs []primitive.ObjectID + expectError bool + }{ + { + name: "List all comments for ReferenceID", + req: repository.ListCommentRequest{ + ReferenceID: proto.String("ref-001"), + PageIndex: 1, + PageSize: 10, + }, + expectedTotal: 2, + expectedIDs: []primitive.ObjectID{testComments[1].ID, testComments[0].ID}, // 根據 CreatedAt 排序 + expectError: false, + }, + { + name: "List child comments by ParentID", + req: repository.ListCommentRequest{ + ParentID: proto.String("parent-id"), + PageIndex: 1, + PageSize: 10, + }, + expectedTotal: 1, + expectedIDs: []primitive.ObjectID{testComments[2].ID}, + expectError: false, + }, + { + name: "Pagination test", + req: repository.ListCommentRequest{ + ReferenceID: proto.String("ref-001"), + PageIndex: 1, + PageSize: 1, + }, + expectedTotal: 2, + expectedIDs: []primitive.ObjectID{testComments[1].ID}, // 第一頁應只有一條 + expectError: false, + }, + { + name: "No matching comments", + req: repository.ListCommentRequest{ + ReferenceID: proto.String("non-existent"), + PageIndex: 1, + PageSize: 10, + }, + expectedTotal: 0, + expectedIDs: nil, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + comments, total, err := repo.ListComments(context.Background(), tt.req) + + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedTotal, total, "返回的總數應該正確") + assert.Len(t, comments, len(tt.expectedIDs), "返回的留言數量應該正確") + + for i, comment := range comments { + assert.Equal(t, tt.expectedIDs[i], comment.ID, "留言 ID 應匹配") + } + } + }) + } +} + +func TestCommentRepository_DeleteCommentByID(t *testing.T) { + repo, tearDown, err := SetupTestCommentRepository("testDB") + assert.NoError(t, err) + defer tearDown() + + // 插入測試留言 + comment := &entity.Comments{ + UID: "test-uid", + ReferenceID: "ref-001", + ParentCommentID: "", + Message: "This is a test comment", + } + err = repo.CreateComment(context.Background(), comment) + assert.NoError(t, err, "插入應成功") + + invalidID := "invalid-id" // 無效的 ObjectID 格式 + nonExistentID := primitive.NewObjectID().Hex() // 不存在的 ID + + tests := []struct { + name string + id string + expectError bool + expectedErr error + expectedRes int64 + }{ + { + name: "Valid delete", + id: comment.ID.Hex(), + expectError: false, + expectedErr: ErrNotFound, + expectedRes: 1, + }, + { + name: "Invalid ID format", + id: invalidID, + expectError: true, + expectedErr: ErrInvalidObjectID, + expectedRes: 0, + }, + { + name: "Non-existent ID", + id: nonExistentID, + expectError: false, + expectedRes: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 調用刪除方法 + err := repo.DeleteCommentByID(context.Background(), tt.id) + + if tt.expectError { + // 驗證錯誤是否正確 + assert.Error(t, err) + assert.Equal(t, tt.expectedErr, err) + } else { + // 驗證刪除是否成功 + assert.NoError(t, err) + + // 驗證刪除後是否還存在 + // 驗證插入的數據是否存在於資料庫中 + result, _, err := repo.ListComments(context.Background(), repository.ListCommentRequest{ + PageSize: 10, + PageIndex: 1, + ReferenceID: &comment.ReferenceID, + }) + assert.NoError(t, err) + assert.Nil(t, result, "留言應該被刪除或不存在") + } + }) + } +} + +func TestCommentRepository_UpdateCommentMessage(t *testing.T) { + repo, tearDown, err := SetupTestCommentRepository("testDB") + assert.NoError(t, err) + defer tearDown() + + // 插入測試留言 + comment := &entity.Comments{ + ID: primitive.NewObjectID(), + UID: "user123", + Message: "Original message", + ReferenceID: "ref-001", + CreatedAt: time.Now().UTC().UnixNano(), + UpdatedAt: time.Now().UTC().UnixNano(), + } + err = repo.CreateComment(context.Background(), comment) + assert.NoError(t, err, "插入應成功") + + tests := []struct { + name string + id string + newMessage string + expectError bool + expectedError error + }{ + { + name: "Valid update", + id: comment.ID.Hex(), + newMessage: "Updated message", + expectError: false, + }, + { + name: "Invalid ObjectID", + id: "invalid-id", + newMessage: "Should not update", + expectError: true, + expectedError: ErrInvalidObjectID, + }, + { + name: "Comment not found", + id: primitive.NewObjectID().Hex(), + newMessage: "Should not update", + expectError: true, + expectedError: ErrNotFound, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := repo.UpdateCommentMessage(context.Background(), tt.id, tt.newMessage) + + if tt.expectError { + // 驗證錯誤是否正確 + assert.Error(t, err) + assert.Equal(t, tt.expectedError, err) + } else { + // 驗證無錯誤返回 + assert.NoError(t, err) + + // 驗證刪除後是否還存在 + // 驗證插入的數據是否存在於資料庫中 + result, _, err := repo.ListComments(context.Background(), repository.ListCommentRequest{ + PageSize: 10, + PageIndex: 1, + ReferenceID: &comment.ReferenceID, + }) + assert.NoError(t, err) + assert.NotNil(t, result, "更新後的留言應存在") + assert.Equal(t, tt.newMessage, result[0].Message, "留言內容應更新") + } + }) + } +} diff --git a/pkg/repository/error.go b/pkg/repository/error.go new file mode 100755 index 0000000..c98cf79 --- /dev/null +++ b/pkg/repository/error.go @@ -0,0 +1,12 @@ +package repository + +import ( + "errors" + + "github.com/zeromicro/go-zero/core/stores/mon" +) + +var ( + ErrNotFound = mon.ErrNotFound + ErrInvalidObjectID = errors.New("invalid objectId") +) diff --git a/pkg/repository/start_mongo_container_test.go b/pkg/repository/start_mongo_container_test.go new file mode 100644 index 0000000..1bcfe1a --- /dev/null +++ b/pkg/repository/start_mongo_container_test.go @@ -0,0 +1,52 @@ +package repository + +import ( + "context" + "fmt" + + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +const ( + Host = "127.0.0.1" + Port = "27017" + Schema = "mongodb" +) + +func startMongoContainer() (string, string, func(), error) { + ctx := context.Background() + + req := testcontainers.ContainerRequest{ + Image: "mongo:latest", + ExposedPorts: []string{"27017/tcp"}, + WaitingFor: wait.ForListeningPort("27017/tcp"), + } + + mongoC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + return "", "", nil, err + } + + port, err := mongoC.MappedPort(ctx, Port) + if err != nil { + return "", "", nil, err + } + + host, err := mongoC.Host(ctx) + if err != nil { + return "", "", nil, err + } + + uri := fmt.Sprintf("mongodb://%s:%s", host, port.Port()) + tearDown := func() { + mongoC.Terminate(ctx) + } + + fmt.Printf("Connecting to %s\n", uri) + + return host, port.Port(), tearDown, nil +} diff --git a/pkg/usecase/comment.go b/pkg/usecase/comment.go new file mode 100644 index 0000000..1b1ba37 --- /dev/null +++ b/pkg/usecase/comment.go @@ -0,0 +1,236 @@ +package usecase + +import ( + "app-cloudep-comment-server/pkg/domain" + "app-cloudep-comment-server/pkg/domain/comment" + "app-cloudep-comment-server/pkg/domain/entity" + "app-cloudep-comment-server/pkg/domain/repository" + "app-cloudep-comment-server/pkg/domain/usecase" + "context" + "encoding/base64" + "fmt" + "github.com/golang/snappy" + "github.com/zeromicro/go-zero/core/logx" + + "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" +) + +type CommentUseCaseParam struct { + Comment repository.CommentRepository +} + +type CommentUseCase struct { + CommentUseCaseParam +} + +func MustCommentUseCase(param CommentUseCaseParam) usecase.CommentUseCase { + return &CommentUseCase{ + param, + } +} + +func (use *CommentUseCase) CreateCommentItem(ctx context.Context, info usecase.CreateCommentDocs) error { + insert := &entity.Comments{ + UID: info.UID, + ReferenceID: info.ReferenceID, + Level: comment.TopLevelComment, + } + snappyContent := snappy.Encode(nil, []byte(info.Message)) + insert.Message = base64.StdEncoding.EncodeToString(snappyContent) + + if info.ParentCommentID != "" { + insert.ParentCommentID = info.ParentCommentID + insert.Level = comment.SubLevelComment + } + + err := use.Comment.CreateComment(ctx, insert) + if err != nil { + return errs.DatabaseErrorWithScopeL( + code.CloudEPComment, + domain.CreateCommentErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "info", Value: info}, + {Key: "func", Value: "Comment.CreateComment"}, + {Key: "err", Value: err.Error()}, + }, + "failed to create comment").Wrap(err) + } + + return nil +} + +func (use *CommentUseCase) GetCommentItem(ctx context.Context, id string) (*usecase.CommentDocs, error) { + item, _, err := use.Comment.ListComments(ctx, repository.ListCommentRequest{ + ReferenceID: &id, + PageIndex: 1, + PageSize: 20, + }) + if err != nil { + return nil, errs.DatabaseErrorWithScopeL( + code.CloudEPComment, + domain.GetCommentErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "id", Value: id}, + {Key: "func", Value: "Comment.ListComments"}, + {Key: "err", Value: err.Error()}, + }, + "failed to get comment").Wrap(err) + } + + decodeB64Content, _ := base64.StdEncoding.DecodeString(item[0].Message) + snappyContent, _ := snappy.Decode(nil, decodeB64Content) + item[0].Message = string(snappyContent) + + return &usecase.CommentDocs{ + ID: item[0].ID.Hex(), + UID: item[0].UID, + ReferenceID: item[0].ReferenceID, + ParentCommentID: item[0].ParentCommentID, + Message: item[0].Message, + UpdatedAt: item[0].UpdatedAt, + CreatedAt: item[0].CreatedAt, + }, nil +} + +func (use *CommentUseCase) UpdateCommentMessage(ctx context.Context, id string, message string) error { + err := use.Comment.UpdateCommentMessage(ctx, id, message) + if err != nil { + return errs.DatabaseErrorWithScopeL( + code.CloudEPComment, + domain.UpdateCommentErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "id", Value: id}, + {Key: "func", Value: "Comment.UpdateCommentMessage"}, + {Key: "err", Value: err.Error()}, + }, + "failed to update comment").Wrap(err) + } + + return nil +} + +func (use *CommentUseCase) RemoveCommentItem(ctx context.Context, id string) error { + // 也需要把子留言給刪除 + err := use.Comment.DeleteCommentByID(ctx, id) + if err != nil { + return errs.DatabaseErrorWithScopeL( + code.CloudEPComment, + domain.DelCommentErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "id", Value: id}, + {Key: "func", Value: "Comment.DeleteCommentByID"}, + {Key: "err", Value: err.Error()}, + }, + "failed to del comment").Wrap(err) + } + + return nil +} + +func (use *CommentUseCase) ListComment(ctx context.Context, req usecase.ListCommentRequest) ([]*usecase.CommentDocs, int64, error) { + // 查詢主層留言 + comments, total, err := use.Comment.ListComments(ctx, repository.ListCommentRequest{ + ParentID: req.ParentID, + PageSize: req.PageSize, + PageIndex: req.PageIndex, + ReferenceID: req.ReferenceID, + }) + if err != nil { + return nil, 0, errs.DatabaseErrorWithScopeL( + code.CloudEPComment, + domain.ListCommentErrorCode, + logx.WithContext(ctx), + []logx.LogField{ + {Key: "req", Value: req}, + {Key: "func", Value: "Comment.ListComments"}, + {Key: "err", Value: err.Error()}, + }, + "failed to list comment").Wrap(err) + } + + // 批量構建子留言 + replyMap, err := use.buildReplies(ctx, comments) + if err != nil { + return nil, 0, err + } + + // 組裝結果 + results := make([]*usecase.CommentDocs, 0, len(comments)) + for _, item := range comments { + decodedMessage, err := decodeMessage(item.Message) + if err != nil { + logx.WithContext(ctx).Errorf("Failed to decode message: %v", err) + continue + } + + results = append(results, &usecase.CommentDocs{ + ID: item.ID.Hex(), + UID: item.UID, + ReferenceID: item.ReferenceID, + ParentCommentID: item.ParentCommentID, + Message: decodedMessage, + UpdatedAt: item.UpdatedAt, + CreatedAt: item.CreatedAt, + Replies: replyMap[item.ID.Hex()], + }) + } + + return results, total, nil +} + +func (use *CommentUseCase) buildReplies(ctx context.Context, parentComments []*entity.Comments) (map[string][]*usecase.CommentDocs, error) { + // 分組 + replyMap := make(map[string][]*usecase.CommentDocs) + // 查詢子留言 + for _, item := range parentComments { + id := item.ID.Hex() + subComments, _, err := use.Comment.ListComments(ctx, repository.ListCommentRequest{ + ParentID: &id, + PageSize: 20, + PageIndex: 1, // 可調整分頁大小 + }) + if err != nil { + continue + } + + for _, sub := range subComments { + decodedMessage, err := decodeMessage(sub.Message) + if err != nil { + logx.WithContext(ctx).Errorf("Failed to decode message: %v", err) + continue + } + replyMap[sub.ParentCommentID] = append(replyMap[sub.ParentCommentID], &usecase.CommentDocs{ + ID: sub.ID.Hex(), + UID: sub.UID, + ReferenceID: sub.ReferenceID, + ParentCommentID: sub.ParentCommentID, + Message: decodedMessage, + UpdatedAt: sub.UpdatedAt, + CreatedAt: sub.CreatedAt, + }) + } + } + + return replyMap, nil +} + +func decodeMessage(encodedMessage string) (string, error) { + // Base64 解碼 + decodedBase64, err := base64.StdEncoding.DecodeString(encodedMessage) + if err != nil { + return "", fmt.Errorf("failed to decode Base64: %w", err) + } + + // Snappy 解碼 + decodedSnappy, err := snappy.Decode(nil, decodedBase64) + if err != nil { + return "", fmt.Errorf("failed to decode Snappy: %w", err) + } + + return string(decodedSnappy), nil +} diff --git a/pkg/usecase/comment_test.go b/pkg/usecase/comment_test.go new file mode 100644 index 0000000..31d3c72 --- /dev/null +++ b/pkg/usecase/comment_test.go @@ -0,0 +1,479 @@ +package usecase + +import ( + "app-cloudep-comment-server/pkg/domain/comment" + "app-cloudep-comment-server/pkg/domain/entity" + "app-cloudep-comment-server/pkg/domain/repository" + "app-cloudep-comment-server/pkg/domain/usecase" + "context" + "encoding/base64" + "errors" + "github.com/golang/snappy" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.uber.org/mock/gomock" + "testing" + "time" + + mockRepo "app-cloudep-comment-server/pkg/mock/repository" + err "app-cloudep-comment-server/pkg/repository" +) + +func TestCommentUseCase_CreateCommentItem(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockCommentRepository := mockRepo.NewMockCommentRepository(mockCtrl) + + uc := MustCommentUseCase(CommentUseCaseParam{ + Comment: mockCommentRepository, + }) + + tests := []struct { + name string + req usecase.CreateCommentDocs + mockSetup func() + wantErr bool + }{ + { + name: "successfully create top-level comment", + req: usecase.CreateCommentDocs{ + UID: "user-123", + ReferenceID: "ref-001", + Message: "This is a top-level comment", + }, + mockSetup: func() { + mockCommentRepository.EXPECT(). + CreateComment(gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, e *entity.Comments) error { + // 驗證插入內容是否正確 + assert.Equal(t, "user-123", e.UID) + assert.Equal(t, "ref-001", e.ReferenceID) + assert.Equal(t, comment.TopLevelComment, e.Level) + return nil + }) + }, + wantErr: false, + }, + { + name: "successfully create sub-level comment", + req: usecase.CreateCommentDocs{ + UID: "user-456", + ReferenceID: "ref-002", + ParentCommentID: "parent-001", + Message: "This is a sub-level comment", + }, + mockSetup: func() { + mockCommentRepository.EXPECT(). + CreateComment(gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, e *entity.Comments) error { + // 驗證插入內容是否正確 + assert.Equal(t, "user-456", e.UID) + assert.Equal(t, "ref-002", e.ReferenceID) + assert.Equal(t, "parent-001", e.ParentCommentID) + assert.Equal(t, comment.SubLevelComment, e.Level) + return nil + }) + }, + wantErr: false, + }, + { + name: "failed to create comment due to repository error", + req: usecase.CreateCommentDocs{ + UID: "user-789", + ReferenceID: "ref-003", + Message: "This is a failing comment", + }, + mockSetup: func() { + mockCommentRepository.EXPECT(). + CreateComment(gomock.Any(), gomock.Any()). + Return(errors.New("database error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.mockSetup() + err := uc.CreateCommentItem(context.Background(), tt.req) + + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), "failed to create comment") + } + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestCommentUseCase_GetCommentItem(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + mockCommentRepository := mockRepo.NewMockCommentRepository(mockCtrl) + + uc := MustCommentUseCase(CommentUseCaseParam{ + Comment: mockCommentRepository, + }) + + tests := []struct { + name string + id string + mockSetup func() + wantErr bool + expected *usecase.CommentDocs + }{ + { + name: "successfully get comment item", + id: "64a1f2e9347e1a001d345678", + mockSetup: func() { + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, req repository.ListCommentRequest) ([]*entity.Comments, int64, error) { + assert.Equal(t, "64a1f2e9347e1a001d345678", *req.ReferenceID) + return []*entity.Comments{ + { + ID: primitive.NewObjectID(), + UID: "user-123", + ReferenceID: "64a1f2e9347e1a001d345678", + ParentCommentID: "", + Message: base64.StdEncoding.EncodeToString(snappy.Encode(nil, []byte("This is a test comment"))), + UpdatedAt: time.Now().UTC().UnixNano(), + CreatedAt: time.Now().UTC().UnixNano(), + }, + }, 1, nil + }) + }, + wantErr: false, + expected: &usecase.CommentDocs{ + UID: "user-123", + ReferenceID: "64a1f2e9347e1a001d345678", + ParentCommentID: "", + Message: "This is a test comment", + }, + }, + { + name: "comment not found", + id: "64a1f2e9347e1a001d345679", + mockSetup: func() { + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + Return([]*entity.Comments{}, int64(0), err.ErrNotFound) + }, + wantErr: true, + }, + { + name: "database error", + id: "64a1f2e9347e1a001d345680", + mockSetup: func() { + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + Return([]*entity.Comments{}, int64(0), errors.New("database error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.mockSetup() + comment, err := uc.GetCommentItem(context.Background(), tt.id) + + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, comment) + assert.Equal(t, tt.expected.UID, comment.UID) + assert.Equal(t, tt.expected.ReferenceID, comment.ReferenceID) + assert.Equal(t, tt.expected.Message, comment.Message) + assert.Equal(t, tt.expected.ParentCommentID, comment.ParentCommentID) + } + }) + } +} + +func TestCommentUseCase_UpdateCommentMessage(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + // Mock Repository + mockCommentRepository := mockRepo.NewMockCommentRepository(mockCtrl) + + // UseCase Initialization + uc := MustCommentUseCase(CommentUseCaseParam{ + Comment: mockCommentRepository, + }) + + tests := []struct { + name string + id string + message string + mockSetup func() + wantErr bool + }{ + { + name: "successfully update comment message", + id: "64a1f2e9347e1a001d345678", // Valid ObjectID + message: "Updated comment message", + mockSetup: func() { + mockCommentRepository.EXPECT(). + UpdateCommentMessage(gomock.Any(), "64a1f2e9347e1a001d345678", "Updated comment message"). + Return(nil) + }, + wantErr: false, + }, + { + name: "invalid ObjectID format", + id: "invalid-id", // Invalid ObjectID + message: "Invalid ID test", + mockSetup: func() { + mockCommentRepository.EXPECT(). + UpdateCommentMessage(gomock.Any(), "invalid-id", "Invalid ID test"). + Return(err.ErrInvalidObjectID) + }, + wantErr: true, + }, + { + name: "comment not found", + id: "64a1f2e9347e1a001d345679", // Non-existent ObjectID + message: "Non-existent comment", + mockSetup: func() { + mockCommentRepository.EXPECT(). + UpdateCommentMessage(gomock.Any(), "64a1f2e9347e1a001d345679", "Non-existent comment"). + Return(err.ErrNotFound) + }, + wantErr: true, + }, + { + name: "database error while updating", + id: "64a1f2e9347e1a001d345680", + message: "Database error test", + mockSetup: func() { + mockCommentRepository.EXPECT(). + UpdateCommentMessage(gomock.Any(), "64a1f2e9347e1a001d345680", "Database error test"). + Return(errors.New("database error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Mock Setup + tt.mockSetup() + + // Execute UseCase + err := uc.UpdateCommentMessage(context.Background(), tt.id, tt.message) + + // Assert Results + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), "failed to update comment") + } + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestCommentUseCase_RemoveCommentItem(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + // Mock Repository + mockCommentRepository := mockRepo.NewMockCommentRepository(mockCtrl) + + // UseCase Initialization + uc := MustCommentUseCase(CommentUseCaseParam{ + Comment: mockCommentRepository, + }) + + tests := []struct { + name string + id string + mockSetup func() + wantErr bool + }{ + { + name: "successfully remove comment item", + id: "64a1f2e9347e1a001d345678", // Valid ObjectID + mockSetup: func() { + mockCommentRepository.EXPECT(). + DeleteCommentByID(gomock.Any(), "64a1f2e9347e1a001d345678"). + Return(nil) + }, + wantErr: false, + }, + { + name: "invalid ObjectID format", + id: "invalid-id", // Invalid ObjectID + mockSetup: func() { + mockCommentRepository.EXPECT(). + DeleteCommentByID(gomock.Any(), "invalid-id"). + Return(err.ErrInvalidObjectID) + }, + wantErr: true, + }, + { + name: "comment not found", + id: "64a1f2e9347e1a001d345679", // Non-existent ObjectID + mockSetup: func() { + mockCommentRepository.EXPECT(). + DeleteCommentByID(gomock.Any(), "64a1f2e9347e1a001d345679"). + Return(err.ErrNotFound) + }, + wantErr: true, + }, + { + name: "database error while deleting", + id: "64a1f2e9347e1a001d345680", + mockSetup: func() { + mockCommentRepository.EXPECT(). + DeleteCommentByID(gomock.Any(), "64a1f2e9347e1a001d345680"). + Return(errors.New("database error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Mock Setup + tt.mockSetup() + + // Execute UseCase + err := uc.RemoveCommentItem(context.Background(), tt.id) + + // Assert Results + if tt.wantErr { + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), "failed to del comment") + } + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestCommentUseCase_ListComment(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + // Mock Repository + mockCommentRepository := mockRepo.NewMockCommentRepository(mockCtrl) + + // UseCase Initialization + uc := MustCommentUseCase(CommentUseCaseParam{ + Comment: mockCommentRepository, + }) + + tests := []struct { + name string + req usecase.ListCommentRequest + mockSetup func() + wantErr bool + expected []*usecase.CommentDocs + total int64 + }{ + { + name: "successfully list comments with replies", + req: usecase.ListCommentRequest{ + PageSize: 10, + PageIndex: 1, + }, + mockSetup: func() { + // Mock main layer comments + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + Return([]*entity.Comments{ + { + ID: primitive.NewObjectID(), + UID: "user-123", + ReferenceID: "ref-001", + Message: base64.StdEncoding.EncodeToString(snappy.Encode(nil, []byte("Comment 1"))), + UpdatedAt: time.Now().UnixNano(), + CreatedAt: time.Now().UnixNano(), + }, + }, int64(1), nil) + + // Mock replies + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + Return([]*entity.Comments{ + { + ID: primitive.NewObjectID(), + UID: "user-456", + ReferenceID: "ref-001", + ParentCommentID: "parent-001", + Message: base64.StdEncoding.EncodeToString(snappy.Encode(nil, []byte("Reply 1"))), + UpdatedAt: time.Now().UnixNano(), + CreatedAt: time.Now().UnixNano(), + }, + }, int64(1), nil) + }, + wantErr: false, + expected: []*usecase.CommentDocs{ + { + UID: "user-123", + ReferenceID: "ref-001", + Message: "Comment 1", + Replies: []*usecase.CommentDocs{ + { + UID: "user-456", + ReferenceID: "ref-001", + ParentCommentID: "parent-001", + Message: "Reply 1", + }, + }, + }, + }, + total: 1, + }, + { + name: "database error while listing comments", + req: usecase.ListCommentRequest{ + PageSize: 10, + PageIndex: 1, + }, + mockSetup: func() { + mockCommentRepository.EXPECT(). + ListComments(gomock.Any(), gomock.Any()). + Return([]*entity.Comments{}, int64(0), errors.New("database error")) + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Mock Setup + tt.mockSetup() + + // Execute UseCase + results, total, err := uc.ListComment(context.Background(), tt.req) + + // Assert Results + if tt.wantErr { + assert.Error(t, err) + assert.Nil(t, results) + assert.Equal(t, int64(0), total) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.total, total) + assert.Len(t, results, len(tt.expected)) + for i, expectedComment := range tt.expected { + assert.Equal(t, expectedComment.Message, results[i].Message) + } + } + }) + } +}