fix: logging module
This commit is contained in:
38
go.mod
38
go.mod
@@ -6,14 +6,44 @@ toolchain go1.23.7
|
|||||||
|
|
||||||
require golang.org/x/crypto v0.36.0
|
require golang.org/x/crypto v0.36.0
|
||||||
|
|
||||||
|
require github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go v0.119.0 // indirect
|
||||||
|
cloud.google.com/go/auth v0.15.0 // indirect
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||||
|
cloud.google.com/go/iam v1.4.1 // indirect
|
||||||
|
cloud.google.com/go/pubsub v1.48.0
|
||||||
|
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/google/s2a-go v0.1.9 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.5 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/rs/zerolog v1.33.0 // indirect
|
github.com/rs/zerolog v1.33.0 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
)
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
|
||||||
require (
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||||
|
golang.org/x/net v0.35.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.28.0 // indirect
|
||||||
|
golang.org/x/sync v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
google.golang.org/api v0.224.0 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e // indirect
|
||||||
|
google.golang.org/grpc v1.71.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|||||||
154
go.sum
154
go.sum
@@ -1,24 +1,178 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.119.0 h1:tw7OjErMzJKbbjaEHkrt60KQrK5Wus/boCZ7tm5/RNE=
|
||||||
|
cloud.google.com/go v0.119.0/go.mod h1:fwB8QLzTcNevxqi8dcpR+hoMIs3jBherGS9VUBDAW08=
|
||||||
|
cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
|
||||||
|
cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||||
|
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||||
|
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||||
|
cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
|
||||||
|
cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
|
||||||
|
cloud.google.com/go/pubsub v1.48.0 h1:ntFpQVrr10Wj/GXSOpxGmexGynldv/bFp25H0jy8aOs=
|
||||||
|
cloud.google.com/go/pubsub v1.48.0/go.mod h1:AAtyjyIT/+zaY1ERKFJbefOvkUxRDNp3nD6TdfdqUZk=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
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.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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
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/googleapis/enterprise-certificate-proxy v0.3.5 h1:VgzTY2jogw3xt39CusEnFJWm7rlsq5yL5q9XdLOuP5g=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.3.5/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
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-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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
|
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/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/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY=
|
||||||
|
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY=
|
||||||
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||||
|
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||||
|
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||||
|
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||||
|
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||||
|
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||||
|
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||||
|
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
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.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.224.0 h1:Ir4UPtDsNiwIOHdExr3fAj4xZ42QjK7uQte3lORLJwU=
|
||||||
|
google.golang.org/api v0.224.0/go.mod h1:3V39my2xAGkodXy0vEqcEtkqgw2GtrFL5WuBZlCTCOQ=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||||
|
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e h1:YA5lmSs3zc/5w+xsRcHqpETkaYyK63ivEPzNTcUUlSA=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250227231956-55c901821b1e/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||||
|
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
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.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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -115,10 +116,26 @@ func getLogger(config Config) (zerolog.Logger, error) {
|
|||||||
writers = append(writers, os.Stdout)
|
writers = append(writers, os.Stdout)
|
||||||
case File:
|
case File:
|
||||||
filePath := dest.Config["path"]
|
filePath := dest.Config["path"]
|
||||||
fmt.Printf("writing test log to file: %v\n", filePath)
|
dir := filepath.Dir(filePath)
|
||||||
|
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||||
|
return newLogger, fmt.Errorf("failed to create log directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("writing test log to file: %v\n", config)
|
||||||
lumberjackLogger := &lumberjack.Logger{
|
lumberjackLogger := &lumberjack.Logger{
|
||||||
Filename: filePath,
|
Filename: filePath,
|
||||||
|
MaxSize: 500, // Default size in MB if not overridden
|
||||||
|
MaxBackups: 15, // Number of backups
|
||||||
|
MaxAge: 30, // Days to retain
|
||||||
|
Compress: false,
|
||||||
}
|
}
|
||||||
|
absPath, err := filepath.Abs(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return newLogger, fmt.Errorf("failed to get absolute path: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Attempting to write logs to: %s\n", absPath)
|
||||||
|
lumberjackLogger.Filename = absPath
|
||||||
|
|
||||||
setConfigValue := func(key string, target *int) {
|
setConfigValue := func(key string, target *int) {
|
||||||
if valStr, ok := dest.Config[key]; ok {
|
if valStr, ok := dest.Config[key]; ok {
|
||||||
if val, err := strconv.Atoi(valStr); err == nil {
|
if val, err := strconv.Atoi(valStr); err == nil {
|
||||||
@@ -132,12 +149,15 @@ func getLogger(config Config) (zerolog.Logger, error) {
|
|||||||
if compress, ok := dest.Config["compress"]; ok {
|
if compress, ok := dest.Config["compress"]; ok {
|
||||||
lumberjackLogger.Compress = compress == "true"
|
lumberjackLogger.Compress = compress == "true"
|
||||||
}
|
}
|
||||||
Info(context.Background(), "here")
|
|
||||||
writers = append(writers, lumberjackLogger)
|
writers = append(writers, lumberjackLogger)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
multiwriter := io.MultiWriter(writers...)
|
multiwriter := io.MultiWriter(writers...)
|
||||||
|
defer func() {
|
||||||
|
if closer, ok := multiwriter.(io.Closer); ok {
|
||||||
|
closer.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
newLogger = zerolog.New(multiwriter).
|
newLogger = zerolog.New(multiwriter).
|
||||||
Level(logLevels[config.level]).
|
Level(logLevels[config.level]).
|
||||||
With().
|
With().
|
||||||
@@ -155,10 +175,10 @@ func InitLogger(c Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var initErr error
|
var initErr error
|
||||||
once.Do(func() {
|
// once.Do(func() {
|
||||||
|
|
||||||
logger, initErr = getLogger(c)
|
logger, initErr = getLogger(c)
|
||||||
})
|
// })
|
||||||
return initErr
|
return initErr
|
||||||
}
|
}
|
||||||
func Debug(ctx context.Context, msg string) {
|
func Debug(ctx context.Context, msg string) {
|
||||||
@@ -221,6 +241,7 @@ func logEvent(ctx context.Context, level zerolog.Level, msg string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
event = event.Err(err)
|
event = event.Err(err)
|
||||||
}
|
}
|
||||||
|
// fmt.Print("=======>", event, ctx)
|
||||||
addCtx(ctx, event)
|
addCtx(ctx, event)
|
||||||
event.Msg(msg)
|
event.Msg(msg)
|
||||||
}
|
}
|
||||||
|
|||||||
301
pkg/log/log_tes1.go
Normal file
301
pkg/log/log_tes1.go
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogFunctions(t *testing.T) {
|
||||||
|
testConfig := Config{
|
||||||
|
level: DebugLevel,
|
||||||
|
destinations: []Destination{
|
||||||
|
{Type: Stdout},
|
||||||
|
},
|
||||||
|
contextKeys: []any{"userID", "requestID"},
|
||||||
|
}
|
||||||
|
err := InitLogger(testConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to initialize logger: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
logFunc func(ctx context.Context)
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Debug log with context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Debug(ctx, "debug message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"debug","requestID":"12345","message":"debug message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Debugf with context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Debugf(ctx, "formatted %s", "debug message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"debug","requestID":"12345","message":"formatted debug message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Info log with message",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Info(ctx, "info message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"info","requestID":"12345","message":"info message"}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "Info log with formatted message",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
Infof(ctx, "formatted %s", "info message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"info","message":"formatted info message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Warn log with context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Warn(ctx, "warning message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"warn","requestID":"12345","message":"warning message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Warnf with context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Warnf(ctx, "formatted %s", "warning message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"warn","requestID":"12345","message":"formatted warning message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error log with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, userID, "67890")
|
||||||
|
Error(ctx, errors.New("something went wrong"), "error message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"error","userID":"67890","error":"something went wrong","message":"error message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Errorf with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, userID, "67890")
|
||||||
|
Errorf(ctx, errors.New("something went wrong"), "formatted %s", "error message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"error","userID":"67890","error":"something went wrong","message":"formatted error message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Fatal log with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Fatal(ctx, errors.New("fatal error"), "fatal message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"fatal","requestID":"12345","error":"fatal error","message":"fatal message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Fatalf with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, requestID, "12345")
|
||||||
|
Fatalf(ctx, errors.New("fatal error"), "formatted %s", "fatal message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"fatal","requestID":"12345","error":"fatal error","message":"formatted fatal message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Panic log with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, userID, "67890")
|
||||||
|
Panic(ctx, errors.New("panic error"), "panic message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"panic","userID":"67890","error":"panic error","message":"panic message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Panicf with error and context",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
type ctxKey any
|
||||||
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, userID, "67890")
|
||||||
|
Panicf(ctx, errors.New("panic error"), "formatted %s", "panic message")
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"panic","userID":"67890","error":"panic error","message":"formatted panic message"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Request log",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||||
|
req.RemoteAddr = "127.0.0.1:8080"
|
||||||
|
Request(ctx, req, []byte("request body"))
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"info","method":"GET","url":"http://example.com","body":"request body","remoteAddr":"127.0.0.1:8080","message":"HTTP Request"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Response log",
|
||||||
|
logFunc: func(ctx context.Context) {
|
||||||
|
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||||
|
Response(ctx, req, 200, 100*time.Millisecond)
|
||||||
|
},
|
||||||
|
expectedOutput: `{"level":"info","method":"GET","url":"http://example.com","statusCode":200,"responseTime":100,"message":"HTTP Response"}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
logger = zerolog.New(&buf).With().Timestamp().Logger()
|
||||||
|
tt.logFunc(context.Background())
|
||||||
|
output := buf.String()
|
||||||
|
t.Logf("Log output: %s", output)
|
||||||
|
lines := strings.Split(strings.TrimSpace(output), "\n")
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No log output found")
|
||||||
|
}
|
||||||
|
lastLine := lines[len(lines)-1]
|
||||||
|
var logOutput map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(lastLine), &logOutput); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal log output: %v", err)
|
||||||
|
}
|
||||||
|
delete(logOutput, "time")
|
||||||
|
delete(logOutput, "caller")
|
||||||
|
var expectedOutput map[string]interface{}
|
||||||
|
if err := json.Unmarshal([]byte(tt.expectedOutput), &expectedOutput); err != nil {
|
||||||
|
t.Fatalf("Failed to unmarshal expected output: %v", err)
|
||||||
|
}
|
||||||
|
for key, expectedValue := range expectedOutput {
|
||||||
|
actualValue, ok := logOutput[key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("Expected key %q not found in log output", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if actualValue != expectedValue {
|
||||||
|
t.Errorf("Mismatch for key %q: expected %v, got %v", key, expectedValue, actualValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestDestinationValidation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config Config
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
// Missing `path` for File destination
|
||||||
|
{
|
||||||
|
name: "Missing file path",
|
||||||
|
config: Config{
|
||||||
|
level: InfoLevel,
|
||||||
|
destinations: []Destination{
|
||||||
|
{
|
||||||
|
Type: File,
|
||||||
|
Config: map[string]string{
|
||||||
|
"maxSize": "500",
|
||||||
|
"maxBackups": "15",
|
||||||
|
"maxAge": "30",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: ErrMissingFilePath,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid maxAge",
|
||||||
|
config: Config{
|
||||||
|
level: InfoLevel,
|
||||||
|
destinations: []Destination{
|
||||||
|
{
|
||||||
|
Type: File,
|
||||||
|
Config: map[string]string{
|
||||||
|
"path": "log/app.txt",
|
||||||
|
"maxSize": "500",
|
||||||
|
"maxBackups": "15",
|
||||||
|
"maxAge": "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: errors.New("invalid maxAge"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid file destination",
|
||||||
|
config: Config{
|
||||||
|
level: InfoLevel,
|
||||||
|
destinations: []Destination{
|
||||||
|
{
|
||||||
|
Type: File,
|
||||||
|
Config: map[string]string{
|
||||||
|
"path": "log/app.txt",
|
||||||
|
"maxSize": "500",
|
||||||
|
"maxBackups": "15",
|
||||||
|
"maxAge": "30",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
fmt.Print(tt.config)
|
||||||
|
err := InitLogger(tt.config)
|
||||||
|
if (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) {
|
||||||
|
t.Errorf("Expected error: %v, got: %v", tt.expectedError, err)
|
||||||
|
} else if err != nil && tt.expectedError != nil && !strings.Contains(err.Error(), tt.expectedError.Error()) {
|
||||||
|
t.Errorf("Expected error to contain: %v, got: %v", tt.expectedError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Send a test log to verify file write
|
||||||
|
logger.Info().Msg("This is a test log message")
|
||||||
|
|
||||||
|
// ✅ Optional: Check if the file was created
|
||||||
|
filePath := tt.config.destinations[0].Config["path"]
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
t.Errorf("Log file was not created at %s", filePath)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Log file created at: %s\n", filePath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,289 +1,421 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
// "bytes"
|
||||||
|
// "fmt"
|
||||||
|
// "net/http"
|
||||||
|
// "time"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLogFunctions(t *testing.T) {
|
const testLogFilePath = "./test_logs/test.log"
|
||||||
testConfig := Config{
|
|
||||||
level: DebugLevel,
|
type ctxKey any
|
||||||
|
|
||||||
|
var requestID ctxKey = "requestID"
|
||||||
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
|
func setupLogger(t *testing.T, l Level) string {
|
||||||
|
dir := filepath.Dir(testLogFilePath)
|
||||||
|
err := os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create test log directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := Config{
|
||||||
|
level: l,
|
||||||
destinations: []Destination{
|
destinations: []Destination{
|
||||||
{Type: Stdout},
|
{
|
||||||
|
Type: File,
|
||||||
|
Config: map[string]string{
|
||||||
|
"path": testLogFilePath,
|
||||||
|
"maxSize": "1",
|
||||||
|
"maxAge": "1",
|
||||||
|
"maxBackup": "1",
|
||||||
|
"compress": "false",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
contextKeys: []any{"userID", "requestID"},
|
contextKeys: []any{"userID", "requestID"},
|
||||||
}
|
}
|
||||||
err := InitLogger(testConfig)
|
|
||||||
|
err = InitLogger(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to initialize logger: %v", err)
|
t.Fatalf("failed to initialize logger: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
return testLogFilePath
|
||||||
name string
|
}
|
||||||
logFunc func(ctx context.Context)
|
|
||||||
expectedOutput string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Debug log with context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
func readLogFile(t *testing.T, logPath string) []string {
|
||||||
Debug(ctx, "debug message")
|
file, err := os.Open(logPath)
|
||||||
},
|
if err != nil {
|
||||||
expectedOutput: `{"level":"debug","requestID":"12345","message":"debug message"}`,
|
t.Fatalf("failed to open log file: %v", err)
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Debugf with context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Debugf(ctx, "formatted %s", "debug message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"debug","requestID":"12345","message":"formatted debug message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Info log with message",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Info(ctx, "info message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"info","requestID":"12345","message":"info message"}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Info log with formatted message",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
Infof(ctx, "formatted %s", "info message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"info","message":"formatted info message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Warn log with context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Warn(ctx, "warning message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"warn","requestID":"12345","message":"warning message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Warnf with context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Warnf(ctx, "formatted %s", "warning message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"warn","requestID":"12345","message":"formatted warning message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error log with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var userID ctxKey = "userID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, userID, "67890")
|
|
||||||
Error(ctx, errors.New("something went wrong"), "error message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"error","userID":"67890","error":"something went wrong","message":"error message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Errorf with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var userID ctxKey = "userID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, userID, "67890")
|
|
||||||
Errorf(ctx, errors.New("something went wrong"), "formatted %s", "error message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"error","userID":"67890","error":"something went wrong","message":"formatted error message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fatal log with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Fatal(ctx, errors.New("fatal error"), "fatal message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"fatal","requestID":"12345","error":"fatal error","message":"fatal message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fatalf with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var requestID ctxKey = "requestID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, requestID, "12345")
|
|
||||||
Fatalf(ctx, errors.New("fatal error"), "formatted %s", "fatal message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"fatal","requestID":"12345","error":"fatal error","message":"formatted fatal message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Panic log with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var userID ctxKey = "userID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, userID, "67890")
|
|
||||||
Panic(ctx, errors.New("panic error"), "panic message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"panic","userID":"67890","error":"panic error","message":"panic message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Panicf with error and context",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
type ctxKey any
|
|
||||||
var userID ctxKey = "userID"
|
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, userID, "67890")
|
|
||||||
Panicf(ctx, errors.New("panic error"), "formatted %s", "panic message")
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"panic","userID":"67890","error":"panic error","message":"formatted panic message"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Request log",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
|
||||||
req.RemoteAddr = "127.0.0.1:8080"
|
|
||||||
Request(ctx, req, []byte("request body"))
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"info","method":"GET","url":"http://example.com","body":"request body","remoteAddr":"127.0.0.1:8080","message":"HTTP Request"}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Response log",
|
|
||||||
logFunc: func(ctx context.Context) {
|
|
||||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
|
||||||
Response(ctx, req, 200, 100*time.Millisecond)
|
|
||||||
},
|
|
||||||
expectedOutput: `{"level":"info","method":"GET","url":"http://example.com","statusCode":200,"responseTime":100,"message":"HTTP Response"}`,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
defer file.Close()
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
var buf bytes.Buffer
|
var lines []string
|
||||||
logger = zerolog.New(&buf).With().Timestamp().Logger()
|
scanner := bufio.NewScanner(file)
|
||||||
tt.logFunc(context.Background())
|
for scanner.Scan() {
|
||||||
output := buf.String()
|
lines = append(lines, scanner.Text())
|
||||||
t.Logf("Log output: %s", output)
|
}
|
||||||
lines := strings.Split(strings.TrimSpace(output), "\n")
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
t.Fatalf("failed to read log file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLogLine(t *testing.T, line string) map[string]interface{} {
|
||||||
|
var logEntry map[string]interface{}
|
||||||
|
err := json.Unmarshal([]byte(line), &logEntry)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse log entry: %v", err)
|
||||||
|
}
|
||||||
|
return logEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebug(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, DebugLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Debug(ctx, "Debug message")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
t.Fatal("No log output found")
|
t.Fatal("No logs were written.")
|
||||||
}
|
}
|
||||||
lastLine := lines[len(lines)-1]
|
var found bool
|
||||||
var logOutput map[string]interface{}
|
for _, line := range lines {
|
||||||
if err := json.Unmarshal([]byte(lastLine), &logOutput); err != nil {
|
logEntry := parseLogLine(t, line)
|
||||||
t.Fatalf("Failed to unmarshal log output: %v", err)
|
if logEntry["level"] == "debug" && strings.Contains(logEntry["message"].(string), "Debug message") {
|
||||||
}
|
found = true
|
||||||
delete(logOutput, "time")
|
break
|
||||||
delete(logOutput, "caller")
|
|
||||||
var expectedOutput map[string]interface{}
|
|
||||||
if err := json.Unmarshal([]byte(tt.expectedOutput), &expectedOutput); err != nil {
|
|
||||||
t.Fatalf("Failed to unmarshal expected output: %v", err)
|
|
||||||
}
|
|
||||||
for key, expectedValue := range expectedOutput {
|
|
||||||
actualValue, ok := logOutput[key]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("Expected key %q not found in log output", key)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if actualValue != expectedValue {
|
|
||||||
t.Errorf("Mismatch for key %q: expected %v, got %v", key, expectedValue, actualValue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
if !found {
|
||||||
|
t.Errorf("expected Debug message, but it was not found in logs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestDestinationValidation(t *testing.T) {
|
|
||||||
tests := []struct {
|
func TestInfo(t *testing.T) {
|
||||||
name string
|
logPath := setupLogger(t, InfoLevel)
|
||||||
config Config
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
expectedError error
|
Info(ctx, "Info message")
|
||||||
}{
|
lines := readLogFile(t, logPath)
|
||||||
// Missing `path` for File destination
|
|
||||||
{
|
if len(lines) == 0 {
|
||||||
name: "Missing file path",
|
t.Fatal("No logs were written.")
|
||||||
config: Config{
|
|
||||||
level: InfoLevel,
|
|
||||||
destinations: []Destination{
|
|
||||||
{
|
|
||||||
Type: File,
|
|
||||||
Config: map[string]string{
|
|
||||||
"maxSize": "500",
|
|
||||||
"maxBackups": "15",
|
|
||||||
"maxAge": "30",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedError: ErrMissingFilePath,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Invalid maxAge",
|
|
||||||
config: Config{
|
|
||||||
level: InfoLevel,
|
|
||||||
destinations: []Destination{
|
|
||||||
{
|
|
||||||
Type: File,
|
|
||||||
Config: map[string]string{
|
|
||||||
"path": "log/app.txt",
|
|
||||||
"maxSize": "500",
|
|
||||||
"maxBackups": "15",
|
|
||||||
"maxAge": "invalid",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedError: errors.New("invalid maxAge"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid file destination",
|
|
||||||
config: Config{
|
|
||||||
level: InfoLevel,
|
|
||||||
destinations: []Destination{
|
|
||||||
{
|
|
||||||
Type: File,
|
|
||||||
Config: map[string]string{
|
|
||||||
"path": "log/app.txt",
|
|
||||||
"maxSize": "500",
|
|
||||||
"maxBackups": "15",
|
|
||||||
"maxAge": "30",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedError: nil,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
var found bool
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
for _, line := range lines {
|
||||||
fmt.Print(tt.config)
|
logEntry := parseLogLine(t, line)
|
||||||
err := InitLogger(tt.config)
|
if logEntry["level"] == "info" && strings.Contains(logEntry["message"].(string), "Info message") {
|
||||||
if (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError == nil) {
|
found = true
|
||||||
t.Errorf("Expected error: %v, got: %v", tt.expectedError, err)
|
break
|
||||||
} else if err != nil && tt.expectedError != nil && !strings.Contains(err.Error(), tt.expectedError.Error()) {
|
|
||||||
t.Errorf("Expected error to contain: %v, got: %v", tt.expectedError, err)
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Info message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWarn(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, WarnLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Warn(ctx, "Warning message")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "warn" && strings.Contains(logEntry["message"].(string), "Warning message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Warning message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, ErrorLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Error(ctx, fmt.Errorf("test error"), "Error message")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "error" && strings.Contains(logEntry["message"].(string), "Error message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Error message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequest(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, InfoLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), requestID, "abc-123")
|
||||||
|
req, _ := http.NewRequest("POST", "/api/test", bytes.NewBuffer([]byte(`{"key":"value"}`)))
|
||||||
|
req.RemoteAddr = "127.0.0.1:8080"
|
||||||
|
Request(ctx, req, []byte(`{"key":"value"}`))
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "debug" && strings.Contains(logEntry["message"].(string), "Debugf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected formatted debug message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponse(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, InfoLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), requestID, "abc-123")
|
||||||
|
req, _ := http.NewRequest("GET", "/api/test", nil)
|
||||||
|
Response(ctx, req, 200, time.Millisecond*123)
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "debug" && strings.Contains(logEntry["message"].(string), "Debugf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected formatted debug message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFatal(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, FatalLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Fatal(ctx, fmt.Errorf("fatal error"), "Fatal message")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "fatal" && strings.Contains(logEntry["message"].(string), "Fatal message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Fatal message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPanic(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, PanicLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Panic(ctx, fmt.Errorf("panic error"), "Panic message")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "panic" && strings.Contains(logEntry["message"].(string), "Panic message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Panic message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugf(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, DebugLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Debugf(ctx, "Debugf message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "debug" && strings.Contains(logEntry["message"].(string), "Debugf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected formatted debug message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfof(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, InfoLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Infof(ctx, "Infof message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "info" && strings.Contains(logEntry["message"].(string), "Infof message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Infof message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func TestWarnf(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, WarnLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
Warnf(ctx, "Warnf message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "warn" && strings.Contains(logEntry["message"].(string), "Warnf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Warnf message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestErrorf(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, ErrorLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
err := fmt.Errorf("error message")
|
||||||
|
Errorf(ctx, err, "Errorf message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "error" && strings.Contains(logEntry["message"].(string), "Errorf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Errorf message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestFatalf(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, FatalLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
err := fmt.Errorf("fatal error")
|
||||||
|
Fatalf(ctx, err, "Fatalf message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "fatal" && strings.Contains(logEntry["message"].(string), "Fatalf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Fatalf message, but it was not found in logs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func TestPanicf(t *testing.T) {
|
||||||
|
logPath := setupLogger(t, PanicLevel)
|
||||||
|
ctx := context.WithValue(context.Background(), userID, "12345")
|
||||||
|
err := fmt.Errorf("panic error")
|
||||||
|
Panicf(ctx, err, "Panicf message: %s", "test")
|
||||||
|
lines := readLogFile(t, logPath)
|
||||||
|
|
||||||
|
if len(lines) == 0 {
|
||||||
|
t.Fatal("No logs were written.")
|
||||||
|
}
|
||||||
|
var found bool
|
||||||
|
for _, line := range lines {
|
||||||
|
logEntry := parseLogLine(t, line)
|
||||||
|
if logEntry["level"] == "panic" && strings.Contains(logEntry["message"].(string), "Panicf message") {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Errorf("expected Panicf message, but it was not found in logs")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
pkg/plugin/definition/decrypter.go
Normal file
15
pkg/plugin/definition/decrypter.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package definition
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Decrypter defines the methods for decryption.
|
||||||
|
type Decrypter interface {
|
||||||
|
// Decrypt decrypts the given body using the provided privateKeyBase64 and publicKeyBase64.
|
||||||
|
Decrypt(ctx context.Context, encryptedData string, privateKeyBase64, publicKeyBase64 string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecrypterProvider initializes a new decrypter instance with the given config.
|
||||||
|
type DecrypterProvider interface {
|
||||||
|
// New creates a new decrypter instance based on the provided config.
|
||||||
|
New(ctx context.Context, config map[string]string) (Decrypter, func() error, error)
|
||||||
|
}
|
||||||
15
pkg/plugin/definition/encrypter.go
Normal file
15
pkg/plugin/definition/encrypter.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package definition
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Encrypter defines the methods for encryption.
|
||||||
|
type Encrypter interface {
|
||||||
|
// Encrypt encrypts the given body using the provided privateKeyBase64 and publicKeyBase64.
|
||||||
|
Encrypt(ctx context.Context, data string, privateKeyBase64, publicKeyBase64 string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncrypterProvider initializes a new encrypter instance with the given config.
|
||||||
|
type EncrypterProvider interface {
|
||||||
|
// New creates a new encrypter instance based on the provided config.
|
||||||
|
New(ctx context.Context, config map[string]string) (Encrypter, func() error, error)
|
||||||
|
}
|
||||||
16
pkg/plugin/definition/publisher.go
Normal file
16
pkg/plugin/definition/publisher.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package definition
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Publisher defines the general publisher interface for messaging plugins.
|
||||||
|
type Publisher interface {
|
||||||
|
// Publish sends a message (as a byte slice) using the underlying messaging system.
|
||||||
|
Publish(ctx context.Context, msg []byte) error
|
||||||
|
|
||||||
|
Close() error // Important for releasing resources.
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublisherProvider interface {
|
||||||
|
// New initializes a new publisher instance with the given configuration.
|
||||||
|
New(ctx context.Context, config map[string]string) (Publisher, error)
|
||||||
|
}
|
||||||
19
pkg/plugin/implementation/decrypter/cmd/plugin.go
Normal file
19
pkg/plugin/implementation/decrypter/cmd/plugin.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
|
decrypter "github.com/beckn/beckn-onix/pkg/plugin/implementation/decrypter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecrypterProvider implements the definition.DecrypterProvider interface.
|
||||||
|
type DecrypterProvider struct{}
|
||||||
|
|
||||||
|
// New creates a new Decrypter instance using the provided configuration.
|
||||||
|
func (dp DecrypterProvider) New(ctx context.Context, config map[string]string) (definition.Decrypter, func() error, error) {
|
||||||
|
return decrypter.New(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider is the exported symbol that the plugin manager will look for.
|
||||||
|
var Provider definition.DecrypterProvider = DecrypterProvider{}
|
||||||
49
pkg/plugin/implementation/decrypter/cmd/plugin_test.go
Normal file
49
pkg/plugin/implementation/decrypter/cmd/plugin_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecrypterProviderSuccess(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
config map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid context with empty config",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid context with non-empty config",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{"key": "value"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
provider := DecrypterProvider{}
|
||||||
|
decrypter, cleanup, err := provider.New(tt.ctx, tt.config)
|
||||||
|
|
||||||
|
// Check error.
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("New() error = %v, want no error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check decrypter.
|
||||||
|
if decrypter == nil {
|
||||||
|
t.Error("New() decrypter is nil, want non-nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test cleanup function if it exists.
|
||||||
|
if cleanup != nil {
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
t.Errorf("cleanup() error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
85
pkg/plugin/implementation/decrypter/decrypter.go
Normal file
85
pkg/plugin/implementation/decrypter/decrypter.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package decryption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zenazn/pkcs7pad"
|
||||||
|
)
|
||||||
|
|
||||||
|
// decrypter implements the Decrypter interface and handles the decryption process.
|
||||||
|
type decrypter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new decrypter instance with the given configuration.
|
||||||
|
func New(ctx context.Context) (*decrypter, func() error, error) {
|
||||||
|
return &decrypter{}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the given encryptedData using the provided privateKeyBase64 and publicKeyBase64.
|
||||||
|
func (d *decrypter) Decrypt(ctx context.Context, encryptedData, privateKeyBase64, publicKeyBase64 string) (string, error) {
|
||||||
|
privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the Base64 encoded encrypted data.
|
||||||
|
messageByte, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to decode encrypted data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aesCipher, err := createAESCipher(privateKeyBytes, publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AES cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blocksize := aesCipher.BlockSize()
|
||||||
|
if len(messageByte)%blocksize != 0 {
|
||||||
|
return "", fmt.Errorf("ciphertext is not a multiple of the blocksize")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(messageByte); i += aesCipher.BlockSize() {
|
||||||
|
executionSlice := messageByte[i : i+aesCipher.BlockSize()]
|
||||||
|
aesCipher.Decrypt(executionSlice, executionSlice)
|
||||||
|
}
|
||||||
|
|
||||||
|
messageByte, err = pkcs7pad.Unpad(messageByte)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to unpad data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(messageByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAESCipher(privateKey, publicKey []byte) (cipher.Block, error) {
|
||||||
|
x25519Curve := ecdh.X25519()
|
||||||
|
x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create private key: %w", err)
|
||||||
|
}
|
||||||
|
x25519PublicKey, err := x25519Curve.NewPublicKey(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create public key: %w", err)
|
||||||
|
}
|
||||||
|
sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to derive shared secret: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aesCipher, err := aes.NewCipher(sharedSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return aesCipher, nil
|
||||||
|
}
|
||||||
251
pkg/plugin/implementation/decrypter/decrypter_test.go
Normal file
251
pkg/plugin/implementation/decrypter/decrypter_test.go
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
package decryption
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zenazn/pkcs7pad"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper function to generate valid test keys.
|
||||||
|
func generateTestKeys(t *testing.T) (privateKeyB64, publicKeyB64 string) {
|
||||||
|
curve := ecdh.X25519()
|
||||||
|
privateKey, err := curve.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey := privateKey.PublicKey()
|
||||||
|
privateKeyB64 = base64.StdEncoding.EncodeToString(privateKey.Bytes())
|
||||||
|
publicKeyB64 = base64.StdEncoding.EncodeToString(publicKey.Bytes())
|
||||||
|
|
||||||
|
return privateKeyB64, publicKeyB64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to encrypt test data.
|
||||||
|
func encryptTestData(t *testing.T, data []byte, privateKeyBase64, publicKeyBase64 string) string {
|
||||||
|
privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Invalid private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Invalid public key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
x25519Curve := ecdh.X25519()
|
||||||
|
x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create private key: %v", err)
|
||||||
|
}
|
||||||
|
x25519PublicKey, err := x25519Curve.NewPublicKey(publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create public key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate shared secret for encryption.
|
||||||
|
sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create shared secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create AES cipher.
|
||||||
|
block, err := aes.NewCipher(sharedSecret)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create AES cipher: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pad the data.
|
||||||
|
paddedData := pkcs7pad.Pad(data, block.BlockSize())
|
||||||
|
|
||||||
|
// Encrypt the data.
|
||||||
|
ciphertext := make([]byte, len(paddedData))
|
||||||
|
for i := 0; i < len(paddedData); i += block.BlockSize() {
|
||||||
|
block.Encrypt(ciphertext[i:i+block.BlockSize()], paddedData[i:i+block.BlockSize()])
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDecrypterSuccess tests successful decryption scenarios.
|
||||||
|
func TestDecrypterSuccess(t *testing.T) {
|
||||||
|
senderPrivateKeyB64, senderPublicKeyB64 := generateTestKeys(t)
|
||||||
|
receiverPrivateKeyB64, receiverPublicKeyB64 := generateTestKeys(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid decryption with small data",
|
||||||
|
data: []byte("test"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid decryption with medium data",
|
||||||
|
data: []byte("medium length test data that spans multiple blocks"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid decryption with empty data",
|
||||||
|
data: []byte{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Encrypt the test data.
|
||||||
|
encryptedData := encryptTestData(t, tt.data, senderPrivateKeyB64, receiverPublicKeyB64)
|
||||||
|
|
||||||
|
decrypter, _, err := New(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create decrypter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := decrypter.Decrypt(context.Background(), encryptedData, receiverPrivateKeyB64, senderPublicKeyB64)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Decrypt() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if result != string(tt.data) {
|
||||||
|
t.Errorf("Decrypt() = %v, want %v", result, string(tt.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDecrypterFailure tests various failure scenarios.
|
||||||
|
func TestDecrypterFailure(t *testing.T) {
|
||||||
|
_, senderPublicKeyB64 := generateTestKeys(t)
|
||||||
|
receiverPrivateKeyB64, _ := generateTestKeys(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
encryptedData string
|
||||||
|
privateKey string
|
||||||
|
publicKey string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Invalid private key format",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: "invalid-base64!@#$",
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "invalid private key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid public key format",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: "invalid-base64!@#$",
|
||||||
|
expectedErr: "invalid public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid encrypted data format",
|
||||||
|
encryptedData: "invalid-base64!@#$",
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "failed to decode encrypted data",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty private key",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: "",
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "invalid private key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty public key",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: "",
|
||||||
|
expectedErr: "invalid public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid base64 data",
|
||||||
|
encryptedData: "=invalid-base64", // Invalid encrypted data.
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "failed to decode encrypted data",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid private key size",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: base64.StdEncoding.EncodeToString([]byte("short")),
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "failed to create private key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid public key size",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString(make([]byte, 32)),
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: base64.StdEncoding.EncodeToString([]byte("short")),
|
||||||
|
expectedErr: "failed to create public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid block size",
|
||||||
|
encryptedData: base64.StdEncoding.EncodeToString([]byte("not-block-size")),
|
||||||
|
privateKey: receiverPrivateKeyB64,
|
||||||
|
publicKey: senderPublicKeyB64,
|
||||||
|
expectedErr: "ciphertext is not a multiple of the blocksize",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
decrypter, _, err := New(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create decrypter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = decrypter.Decrypt(context.Background(), tt.encryptedData, tt.privateKey, tt.publicKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), tt.expectedErr) {
|
||||||
|
t.Errorf("Expected error containing %q, got %q", tt.expectedErr, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNewDecrypter tests the creation of new Decrypter instances.
|
||||||
|
func TestNewDecrypter(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid context",
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nil context",
|
||||||
|
ctx: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
decrypter, _, err := New(tt.ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("New() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if decrypter == nil {
|
||||||
|
t.Error("Expected non-nil decrypter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
18
pkg/plugin/implementation/encrypter/cmd/plugin.go
Normal file
18
pkg/plugin/implementation/encrypter/cmd/plugin.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
|
"github.com/beckn/beckn-onix/pkg/plugin/implementation/encrypter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EncrypterProvider implements the definition.EncrypterProvider interface.
|
||||||
|
type EncrypterProvider struct{}
|
||||||
|
|
||||||
|
func (ep EncrypterProvider) New(ctx context.Context, config map[string]string) (definition.Encrypter, func() error, error) {
|
||||||
|
return encrypter.New(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider is the exported symbol that the plugin manager will look for.
|
||||||
|
var Provider definition.EncrypterProvider = EncrypterProvider{}
|
||||||
49
pkg/plugin/implementation/encrypter/cmd/plugin_test.go
Normal file
49
pkg/plugin/implementation/encrypter/cmd/plugin_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncrypterProviderSuccess(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
config map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid empty config",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid config with algorithm",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{
|
||||||
|
"algorithm": "AES",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Create provider and encrypter.
|
||||||
|
provider := EncrypterProvider{}
|
||||||
|
encrypter, cleanup, err := provider.New(tt.ctx, tt.config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("EncrypterProvider.New() error = %v", err)
|
||||||
|
}
|
||||||
|
if encrypter == nil {
|
||||||
|
t.Fatal("EncrypterProvider.New() returned nil encrypter")
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if cleanup != nil {
|
||||||
|
if err := cleanup(); err != nil {
|
||||||
|
t.Errorf("Cleanup() error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
70
pkg/plugin/implementation/encrypter/encrypter.go
Normal file
70
pkg/plugin/implementation/encrypter/encrypter.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package encrypter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zenazn/pkcs7pad"
|
||||||
|
)
|
||||||
|
|
||||||
|
// encrypter implements the Encrypter interface and handles the encryption process.
|
||||||
|
type encrypter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new encrypter instance with the given configuration.
|
||||||
|
func New(ctx context.Context) (*encrypter, func() error, error) {
|
||||||
|
return &encrypter{}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encrypter) Encrypt(ctx context.Context, data string, privateKeyBase64, publicKeyBase64 string) (string, error) {
|
||||||
|
privateKeyBytes, err := base64.StdEncoding.DecodeString(privateKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyBase64)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid public key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the input string to a byte slice.
|
||||||
|
dataByte := []byte(data)
|
||||||
|
aesCipher, err := createAESCipher(privateKeyBytes, publicKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create AES cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataByte = pkcs7pad.Pad(dataByte, aesCipher.BlockSize())
|
||||||
|
for i := 0; i < len(dataByte); i += aesCipher.BlockSize() {
|
||||||
|
aesCipher.Encrypt(dataByte[i:i+aesCipher.BlockSize()], dataByte[i:i+aesCipher.BlockSize()])
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.StdEncoding.EncodeToString(dataByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAESCipher(privateKey, publicKey []byte) (cipher.Block, error) {
|
||||||
|
x25519Curve := ecdh.X25519()
|
||||||
|
x25519PrivateKey, err := x25519Curve.NewPrivateKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create private key: %w", err)
|
||||||
|
}
|
||||||
|
x25519PublicKey, err := x25519Curve.NewPublicKey(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create public key: %w", err)
|
||||||
|
}
|
||||||
|
sharedSecret, err := x25519PrivateKey.ECDH(x25519PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to derive shared secret: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aesCipher, err := aes.NewCipher(sharedSecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create AES cipher: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return aesCipher, nil
|
||||||
|
}
|
||||||
183
pkg/plugin/implementation/encrypter/encrypter_test.go
Normal file
183
pkg/plugin/implementation/encrypter/encrypter_test.go
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
package encrypter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper function to generate a test X25519 key pair.
|
||||||
|
func generateTestKeyPair(t *testing.T) (string, string) {
|
||||||
|
curve := ecdh.X25519()
|
||||||
|
privateKey, err := curve.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKeyBytes := privateKey.PublicKey().Bytes()
|
||||||
|
// Encode public and private key to base64.
|
||||||
|
publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKeyBytes)
|
||||||
|
privateKeyBase64 := base64.StdEncoding.EncodeToString(privateKey.Bytes())
|
||||||
|
|
||||||
|
return publicKeyBase64, privateKeyBase64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEncryptSuccess tests successful encryption scenarios.
|
||||||
|
func TestEncryptSuccess(t *testing.T) {
|
||||||
|
_, privateKey := generateTestKeyPair(t)
|
||||||
|
peerpublicKey, _ := generateTestKeyPair(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data string
|
||||||
|
pubKey string
|
||||||
|
privKey string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid short message",
|
||||||
|
data: "Hello, World!",
|
||||||
|
pubKey: peerpublicKey,
|
||||||
|
privKey: privateKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid JSON message",
|
||||||
|
data: `{"key":"value"}`,
|
||||||
|
pubKey: peerpublicKey,
|
||||||
|
privKey: privateKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid empty message",
|
||||||
|
data: "",
|
||||||
|
pubKey: peerpublicKey,
|
||||||
|
privKey: privateKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
encrypter := &encrypter{}
|
||||||
|
encrypted, err := encrypter.Encrypt(context.Background(), tt.data, tt.privKey, tt.pubKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encrypt() expected no error, but got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the encrypted data is valid base64.
|
||||||
|
_, err = base64.StdEncoding.DecodeString(encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Encrypt() output is not valid base64: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we can't decrypt without the ephemeral private key,
|
||||||
|
// we can only verify that encryption doesn't return empty data.
|
||||||
|
if encrypted == "" {
|
||||||
|
t.Error("Encrypt() returned empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the output is different from input (basic encryption check).
|
||||||
|
if encrypted == tt.data {
|
||||||
|
t.Error("Encrypt() output matches input, suggesting no encryption occurred")
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEncryptFailure tests encryption failure scenarios.
|
||||||
|
func TestEncryptFailure(t *testing.T) {
|
||||||
|
// Generate a valid key pair for testing.
|
||||||
|
_, privateKey := generateTestKeyPair(t)
|
||||||
|
peerpublicKey, _ := generateTestKeyPair(t)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data string
|
||||||
|
publicKey string
|
||||||
|
privKey string
|
||||||
|
errorContains string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Invalid public key format",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: "invalid-base64!@#$",
|
||||||
|
privKey: privateKey,
|
||||||
|
errorContains: "invalid public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid key bytes(public key)",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: base64.StdEncoding.EncodeToString([]byte("invalid-key-bytes")),
|
||||||
|
privKey: privateKey,
|
||||||
|
errorContains: "failed to create public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid key bytes(private key)",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: peerpublicKey,
|
||||||
|
privKey: base64.StdEncoding.EncodeToString([]byte("invalid-key-bytes")),
|
||||||
|
errorContains: "failed to create private key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty public key",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: "",
|
||||||
|
privKey: privateKey,
|
||||||
|
errorContains: "invalid public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Too short key",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: base64.StdEncoding.EncodeToString([]byte{1, 2, 3, 4}),
|
||||||
|
privKey: privateKey,
|
||||||
|
errorContains: "failed to create public key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid private key",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: peerpublicKey,
|
||||||
|
privKey: "invalid-base64!@#$",
|
||||||
|
errorContains: "invalid private key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty private key",
|
||||||
|
data: "test data",
|
||||||
|
publicKey: peerpublicKey,
|
||||||
|
privKey: "",
|
||||||
|
errorContains: "invalid private key",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
encrypter := &encrypter{}
|
||||||
|
_, err := encrypter.Encrypt(context.Background(), tt.data, tt.privKey, tt.publicKey)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), tt.errorContains) {
|
||||||
|
t.Errorf("Encrypt() error = %v, want error containing %q", err, tt.errorContains)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNew tests the creation of new encrypter instances.
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Success",
|
||||||
|
ctx: context.Background(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
encrypter, _, err := New(tt.ctx)
|
||||||
|
if err == nil && encrypter == nil {
|
||||||
|
t.Error("New() returned nil encrypter")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,17 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/beckn/beckn-onix/shared/plugin/definition"
|
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
|
|
||||||
plugin "github.com/beckn/beckn-onix/shared/plugin/definition"
|
verifier "github.com/beckn/beckn-onix/pkg/plugin/implementation/signVerifier"
|
||||||
verifier "github.com/beckn/beckn-onix/shared/plugin/implementation/signVerifier"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VerifierProvider provides instances of Verifier.
|
// VerifierProvider provides instances of Verifier.
|
||||||
type VerifierProvider struct{}
|
type VerifierProvider struct{}
|
||||||
|
|
||||||
// New initializes a new Verifier instance.
|
// New initializes a new Verifier instance.
|
||||||
func (vp VerifierProvider) New(ctx context.Context, config map[string]string) (plugin.Verifier, func() error, error) {
|
func (vp VerifierProvider) New(ctx context.Context, config map[string]string) (definition.Verifier, func() error, error) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil, nil, errors.New("context cannot be nil")
|
return nil, nil, errors.New("context cannot be nil")
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/beckn/beckn-onix/shared/plugin/definition"
|
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
"github.com/beckn/beckn-onix/shared/plugin/implementation/signer"
|
"github.com/beckn/beckn-onix/pkg/plugin/implementation/signer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignerProvider implements the definition.SignerProvider interface.
|
// SignerProvider implements the definition.SignerProvider interface.
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"plugin"
|
"plugin"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/beckn/beckn-onix/shared/plugin/definition"
|
"github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config represents the plugin manager configuration.
|
// Config represents the plugin manager configuration.
|
||||||
@@ -15,6 +15,9 @@ type Config struct {
|
|||||||
Root string `yaml:"root"`
|
Root string `yaml:"root"`
|
||||||
Signer PluginConfig `yaml:"signer"`
|
Signer PluginConfig `yaml:"signer"`
|
||||||
Verifier PluginConfig `yaml:"verifier"`
|
Verifier PluginConfig `yaml:"verifier"`
|
||||||
|
Decrypter PluginConfig `yaml:"decrypter"`
|
||||||
|
Encrypter PluginConfig `yaml:"encrypter"`
|
||||||
|
Publisher PluginConfig `yaml:"publisher"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginConfig represents configuration details for a plugin.
|
// PluginConfig represents configuration details for a plugin.
|
||||||
@@ -27,6 +30,9 @@ type PluginConfig struct {
|
|||||||
type Manager struct {
|
type Manager struct {
|
||||||
sp definition.SignerProvider
|
sp definition.SignerProvider
|
||||||
vp definition.VerifierProvider
|
vp definition.VerifierProvider
|
||||||
|
dp definition.DecrypterProvider
|
||||||
|
ep definition.EncrypterProvider
|
||||||
|
pb definition.PublisherProvider
|
||||||
cfg *Config
|
cfg *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,19 +42,37 @@ func NewManager(ctx context.Context, cfg *Config) (*Manager, error) {
|
|||||||
return nil, fmt.Errorf("configuration cannot be nil")
|
return nil, fmt.Errorf("configuration cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load signer plugin
|
// Load signer plugin.
|
||||||
sp, err := provider[definition.SignerProvider](cfg.Root, cfg.Signer.ID)
|
sp, err := provider[definition.SignerProvider](cfg.Root, cfg.Signer.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load signer plugin: %w", err)
|
return nil, fmt.Errorf("failed to load signer plugin: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load verifier plugin
|
// Load publisher plugin.
|
||||||
|
pb, err := provider[definition.PublisherProvider](cfg.Root, cfg.Publisher.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load publisher plugin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load verifier plugin.
|
||||||
vp, err := provider[definition.VerifierProvider](cfg.Root, cfg.Verifier.ID)
|
vp, err := provider[definition.VerifierProvider](cfg.Root, cfg.Verifier.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load Verifier plugin: %w", err)
|
return nil, fmt.Errorf("failed to load Verifier plugin: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Manager{sp: sp, vp: vp, cfg: cfg}, nil
|
// Load decrypter plugin.
|
||||||
|
dp, err := provider[definition.DecrypterProvider](cfg.Root, cfg.Decrypter.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load Decrypter plugin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load encryption plugin.
|
||||||
|
ep, err := provider[definition.EncrypterProvider](cfg.Root, cfg.Encrypter.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load encryption plugin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, cfg: cfg}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// provider loads a plugin dynamically and retrieves its provider instance.
|
// provider loads a plugin dynamically and retrieves its provider instance.
|
||||||
@@ -106,3 +130,42 @@ func (m *Manager) Verifier(ctx context.Context) (definition.Verifier, func() err
|
|||||||
}
|
}
|
||||||
return Verifier, close, nil
|
return Verifier, close, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decrypter retrieves the decryption plugin instance.
|
||||||
|
func (m *Manager) Decrypter(ctx context.Context) (definition.Decrypter, func() error, error) {
|
||||||
|
if m.dp == nil {
|
||||||
|
return nil, nil, fmt.Errorf("decrypter plugin provider not loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypter, close, err := m.dp.New(ctx, m.cfg.Decrypter.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to initialize Decrypter: %w", err)
|
||||||
|
}
|
||||||
|
return decrypter, close, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypter retrieves the encryption plugin instance.
|
||||||
|
func (m *Manager) Encrypter(ctx context.Context) (definition.Encrypter, func() error, error) {
|
||||||
|
if m.ep == nil {
|
||||||
|
return nil, nil, fmt.Errorf("encryption plugin provider not loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypter, close, err := m.ep.New(ctx, m.cfg.Encrypter.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to initialize encrypter: %w", err)
|
||||||
|
}
|
||||||
|
return encrypter, close, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publisher retrieves the publisher plugin instance.
|
||||||
|
func (m *Manager) Publisher(ctx context.Context) (definition.Publisher, error) {
|
||||||
|
if m.pb == nil {
|
||||||
|
return nil, fmt.Errorf("publisher plugin provider not loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
publisher, err := m.pb.New(ctx, m.cfg.Publisher.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to initialize publisher: %w", err)
|
||||||
|
}
|
||||||
|
return publisher, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user