diff --git a/internal/service/app.go b/internal/service/app.go index 7609525..fc81cb0 100644 --- a/internal/service/app.go +++ b/internal/service/app.go @@ -17,7 +17,7 @@ type AppService struct { config *config.Config } -func (app *AppService) Put(ctx context.Context, in *api.AppsRequest) (*api.AddAppResponse, error) { +func (app *AppService) Post(ctx context.Context, in *api.AppsRequest) (*api.AddAppResponse, error) { owner := GetIdentity(ctx) appInstance, err := app.biz.CreateApp(ctx, in, owner) @@ -27,10 +27,11 @@ func (app *AppService) Put(ctx context.Context, in *api.AppsRequest) (*api.AddAp } return &api.AddAppResponse{Appid: appInstance.Appid, AccessKey: appInstance.AccessKey, Secret: appInstance.Secret}, nil } + func (app *AppService) Get(ctx context.Context, in *api.GetAPPRequest) (*api.Apps, error) { apps, err := app.biz.Get(ctx, in.Appid) if err != nil { - app.log.Errorf(ctx,"GetApps failed: %v", err) + app.log.Errorf(ctx, "GetApps failed: %v", err) return nil, err } return apps, nil @@ -40,10 +41,11 @@ func (app *AppService) Desc() *grpc.ServiceDesc { return &api.AppsService_ServiceDesc } -func NewAppService(biz *biz.AppUsecase, log logger.Logger, config *config.Config) *AppService { +func NewAppService(biz *biz.AppUsecase, log logger.Logger, config *config.Config) api.AppsServiceServer { return &AppService{biz: biz, log: log, config: config} } -func (app *AppService) Patch(ctx context.Context, in *api.AppsRequest) (*api.Apps, error) { + +func (app *AppService) Update(ctx context.Context, in *api.AppsRequest) (*api.Apps, error) { owner := GetIdentity(ctx) appInstance, err := app.biz.Patch(ctx, in, owner) if err != nil { @@ -52,6 +54,7 @@ func (app *AppService) Patch(ctx context.Context, in *api.AppsRequest) (*api.App } return appInstance, nil } + func (app *AppService) Delete(ctx context.Context, in *api.DeleteAppRequest) (*api.DeleteAppResponse, error) { err := app.biz.Del(ctx, in.Appid) if err != nil { @@ -59,3 +62,12 @@ func (app *AppService) Delete(ctx context.Context, in *api.DeleteAppRequest) (*a } return &api.DeleteAppResponse{}, nil } + +func (app *AppService) List(ctx context.Context, in *api.AppsListRequest) (*api.AppsListResponse, error) { + apps, err := app.biz.List(ctx, in) + if err != nil { + return nil, err + } + return &api.AppsListResponse{Apps: apps}, nil + // return nil, nil +} diff --git a/internal/service/app_test.go b/internal/service/app_test.go index 1e6e21b..60a5934 100644 --- a/internal/service/app_test.go +++ b/internal/service/app_test.go @@ -6,6 +6,12 @@ import ( "testing" "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/service" api "github.com/begonia-org/go-sdk/api/app/v1" "github.com/begonia-org/go-sdk/client" common "github.com/begonia-org/go-sdk/common/api/v1" @@ -13,6 +19,7 @@ import ( ) var appid = "" +var name2 = "" func addApp(t *testing.T) { c.Convey( @@ -20,12 +27,27 @@ func addApp(t *testing.T) { t, func() { apiClient := client.NewAppAPI(apiAddr, accessKey, secret) - rsp, err := apiClient.PostAppConfig(context.Background(), &api.AppsRequest{Name: fmt.Sprintf("app-%s", time.Now().Format("20060102150405")), Description: "test"}) + name := fmt.Sprintf("app-%s", time.Now().Format("20060102150405")) + rsp, err := apiClient.PostAppConfig(context.Background(), &api.AppsRequest{Name: name, Description: "test", Tags: []string{"test"}}) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) c.So(rsp.Appid, c.ShouldNotBeEmpty) appid = rsp.Appid + rsp2, err := apiClient.GetAPP(context.Background(), appid) + c.So(err, c.ShouldBeNil) + c.So(rsp2.StatusCode, c.ShouldEqual, common.Code_OK) + c.So(rsp2.Name, c.ShouldNotBeEmpty) + _, err = apiClient.PostAppConfig(context.Background(), &api.AppsRequest{Name: name, Description: "test"}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldEqual, "duplicate app name") + // c.So(rsp3.StatusCode, c.ShouldEqual, common.Code_ERR) + + name2 = fmt.Sprintf("app-service-2-%s", time.Now().Format("20060102150405")) + rsp, err = apiClient.PostAppConfig(context.Background(), &api.AppsRequest{Name: name2, Description: "test"}) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + }, ) } @@ -43,6 +65,29 @@ func getApp(t *testing.T) { }, ) } +func testPatchApp(t *testing.T) { + c.Convey( + "test patch app", + t, + func() { + apiClient := client.NewAppAPI(apiAddr, accessKey, secret) + name := fmt.Sprintf("app-%s", time.Now().Format("20060102150405")) + rsp2, err := apiClient.UpdateAPP(context.Background(), appid, name, "test patch", nil) + c.So(err, c.ShouldBeNil) + c.So(rsp2.StatusCode, c.ShouldEqual, common.Code_OK) + rsp2, err = apiClient.GetAPP(context.Background(), appid) + c.So(err, c.ShouldBeNil) + c.So(rsp2.StatusCode, c.ShouldEqual, common.Code_OK) + c.So(rsp2.Name, c.ShouldEqual, name) + + _, err = apiClient.UpdateAPP(context.Background(), appid, name2, "test patch", nil) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldEqual, "duplicate app name") + + }, + ) + +} func delApp(t *testing.T) { c.Convey( "test del app", @@ -56,11 +101,55 @@ func delApp(t *testing.T) { _, err = apiClient.GetAPP(context.Background(), appid) c.So(err, c.ShouldNotBeNil) + + _, err = apiClient.DeleteAPP(context.TODO(), appid) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldEqual, "app not found") + // c.So(rsp3.StatusCode, c.ShouldEqual, common.Code_OK) }) } + +func listAPP(t *testing.T) { + c.Convey( + "test list app", + t, + func() { + apiClient := client.NewAppAPI(apiAddr, accessKey, secret) + rsp, err := apiClient.ListAPP(context.Background(), []string{"test", "test2"}, []api.APPStatus{api.APPStatus_APP_ENABLED}, 1, 10) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + c.So(rsp.Apps, c.ShouldNotBeEmpty) + c.So(rsp.Apps[0].Appid, c.ShouldNotBeEmpty) + }, + ) +} +func testListErr(t *testing.T) { + c.Convey( + "test list app", + t, + func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewAPPSvrForTest(cnf, gateway.Log) + patch := gomonkey.ApplyFuncReturn((*biz.AppUsecase).List, nil, fmt.Errorf("test list app error")) + defer patch.Reset() + _, err := srv.List(context.Background(), nil) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldEqual, "test list app error") + patch.Reset() + }, + ) +} func TestApp(t *testing.T) { t.Run("add app", addApp) t.Run("get app", getApp) + t.Run("list app", listAPP) + t.Run("list app err", testListErr) + t.Run("patch app", testPatchApp) // appid = "442568851213783040" t.Run("del app", delApp) + } diff --git a/internal/service/authz.go b/internal/service/authz.go index 2cc4f51..a692b51 100644 --- a/internal/service/authz.go +++ b/internal/service/authz.go @@ -19,7 +19,7 @@ type AuthzService struct { authCrypto *crypto.UsersAuth } -func NewAuthzService(biz *biz.AuthzUsecase, log logger.Logger, auth *crypto.UsersAuth, config *config.Config) *AuthzService { +func NewAuthzService(biz *biz.AuthzUsecase, log logger.Logger, auth *crypto.UsersAuth, config *config.Config) api.AuthServiceServer { return &AuthzService{biz: biz, log: log, authCrypto: auth, config: config} } diff --git a/internal/service/authz_test.go b/internal/service/authz_test.go index e4cf5c7..cbcc778 100644 --- a/internal/service/authz_test.go +++ b/internal/service/authz_test.go @@ -13,7 +13,12 @@ import ( "runtime" "testing" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/service" sys "github.com/begonia-org/go-sdk/api/sys/v1" + v1 "github.com/begonia-org/go-sdk/api/user/v1" "github.com/begonia-org/go-sdk/client" common "github.com/begonia-org/go-sdk/common/api/v1" c "github.com/smartystreets/goconvey/convey" @@ -112,10 +117,34 @@ func testLogout(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(apiRsp.Code, c.ShouldNotEqual, int32(common.Code_OK)) + + _, err = apiClient.Logout(context.Background(), xtoken) + c.So(err, c.ShouldNotBeNil) + t.Logf("logout error: %v", err) + // c.So(rsp.StatusCode, c.shoun, common.Code_OK) }, ) } +func testAuthSeed(t *testing.T) { + c.Convey( + "test auth seed", + t, + func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + config := config.ReadConfig(env) + srv := service.NewAuthzSvrForTest(config, gateway.Log) + _, err := srv.AuthSeed(context.Background(), &v1.AuthLogAPIRequest{}) + c.So(err, c.ShouldNotBeNil) + + }, + ) +} + func TestAuth(t *testing.T) { t.Run("login", loginTest) t.Run("logout", testLogout) + t.Run("auth seed", testAuthSeed) } diff --git a/internal/service/endpoints.go b/internal/service/endpoints.go index fd1473b..3cfb34a 100644 --- a/internal/service/endpoints.go +++ b/internal/service/endpoints.go @@ -19,7 +19,7 @@ type EndpointsService struct { api.UnimplementedEndpointServiceServer } -func NewEndpointsService(biz *endpoint.EndpointUsecase, log logger.Logger, config *config.Config) *EndpointsService { +func NewEndpointsService(biz *endpoint.EndpointUsecase, log logger.Logger, config *config.Config) api.EndpointServiceServer { return &EndpointsService{biz: biz, log: log, config: config} } @@ -33,7 +33,7 @@ func (e *EndpointsService) Update(ctx context.Context, in *api.EndpointSrvUpdate tm, _ := time.Parse(time.RFC3339, timestamp) return &api.UpdateEndpointResponse{UpdatedAt: timestamppb.New(tm)}, nil } -func (e *EndpointsService) Put(ctx context.Context, in *api.EndpointSrvConfig) (*api.AddEndpointResponse, error) { +func (e *EndpointsService) Post(ctx context.Context, in *api.EndpointSrvConfig) (*api.AddEndpointResponse, error) { id, err := e.biz.AddConfig(ctx, in) if err != nil { return nil, err @@ -62,7 +62,7 @@ func (e *EndpointsService) Delete(ctx context.Context, in *api.DeleteEndpointReq return &api.DeleteEndpointResponse{}, nil } -func (e *EndpointsService) Details(ctx context.Context, in *api.DetailsEndpointRequest) (*api.DetailsEndpointResponse, error) { +func (e *EndpointsService) Get(ctx context.Context, in *api.DetailsEndpointRequest) (*api.DetailsEndpointResponse, error) { endpoint, err := e.biz.Get(ctx, in.UniqueKey) if err != nil { return nil, err diff --git a/internal/service/file.go b/internal/service/file.go index ee2fbd5..843d029 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -30,21 +30,16 @@ type FileService struct { config *config.Config } -func NewFileService(biz *file.FileUsecase, config *config.Config) *FileService { +func NewFileService(biz *file.FileUsecase, config *config.Config) api.FileServiceServer { return &FileService{biz: biz, config: config} } func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*api.UploadFileResponse, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, gosdk.NewError(fmt.Errorf("not found metadata"), int32(common.Code_PARAMS_ERROR), codes.InvalidArgument, "not_found_metadata") - } - identity := md.Get("x-identity") - if len(identity) == 0 { + identity := "" + if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - // in.Key = identity[0] + "/" + in.Key - return f.biz.Upload(ctx, in, identity[0]) + return f.biz.Upload(ctx, in, identity) } func (f *FileService) InitiateMultipartUpload(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { @@ -54,34 +49,27 @@ func (f *FileService) UploadMultipartFile(ctx context.Context, in *api.UploadMul return f.biz.UploadMultipartFileFile(ctx, in) } func (f *FileService) CompleteMultipartUpload(ctx context.Context, in *api.CompleteMultipartUploadRequest) (*api.CompleteMultipartUploadResponse, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_metadata") - } - identity := md.Get("x-identity") - if len(identity) == 0 { + identity := "" + if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - return f.biz.CompleteMultipartUploadFile(ctx, in, identity[0]) + return f.biz.CompleteMultipartUploadFile(ctx, in, identity) } func (f *FileService) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { return f.biz.AbortMultipartUpload(ctx, in) } func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*httpbody.HttpBody, error) { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, gosdk.NewError(fmt.Errorf("not found metadata"), int32(common.Code_PARAMS_ERROR), codes.InvalidArgument, "not_found_metadata") - } - identity := md.Get("x-identity") - if len(identity) == 0 { + identity := "" + if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } + newKey, err := url.PathUnescape(in.Key) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") } in.Key = newKey - buf, err := f.biz.Download(ctx, in, identity[0]) + buf, err := f.biz.Download(ctx, in, identity) if err != nil { return nil, err } @@ -92,10 +80,7 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", len(buf)), gosdk.GetMetadataKey("X-File-Sha256"), hex.EncodeToString(shaer.Sum(nil)), ) - err = grpc.SendHeader(ctx, rspMd) - if err != nil { - return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.Internal, "send_header") - } + _ = grpc.SendHeader(ctx, rspMd) rsp := &httpbody.HttpBody{ ContentType: http.DetectContentType(buf), @@ -116,7 +101,7 @@ func parseRangeHeader(rangeHeader string) (start, end int64, err error) { start = 0 end, err = strconv.ParseInt(strings.TrimPrefix(rangeSpec, "-"), 10, 64) if err != nil { - return 0, 0, fmt.Errorf("invalid start value: %s", parts[0]) + return 0, 0, fmt.Errorf("invalid end value: %s", parts[0]) } return start, end, nil } @@ -124,7 +109,7 @@ func parseRangeHeader(rangeHeader string) (start, end int64, err error) { end = 0 start, err = strconv.ParseInt(strings.TrimSuffix(rangeSpec, "-"), 10, 64) if err != nil { - return 0, 0, fmt.Errorf("invalid end value: %s", parts[1]) + return 0, 0, fmt.Errorf("invalid start value: %s", parts[1]) } return start, end, nil @@ -149,12 +134,17 @@ func parseRangeHeader(rangeHeader string) (start, end int64, err error) { return start, end, nil } func (f *FileService) DownloadForRange(ctx context.Context, in *api.DownloadRequest) (*httpbody.HttpBody, error) { + identity := GetIdentity(ctx) + if identity == "" { + return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") + + } md, ok := metadata.FromIncomingContext(ctx) var rangeStr string var start, end int64 var err error if ok { - if _, ok := md["range"]; !ok { + if v, ok := md["range"]; !ok || len(v) == 0 { return nil, gosdk.NewError(fmt.Errorf("range header not found"), int32(common.Code_PARAMS_ERROR), codes.InvalidArgument, "range_header_not_found") } rangeStr = md.Get("range")[0] @@ -163,11 +153,7 @@ func (f *FileService) DownloadForRange(ctx context.Context, in *api.DownloadRequ return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "parse_range_header") } } - identity := GetIdentity(ctx) - if identity == "" { - return nil, gosdk.NewError(errors.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") - } data, fileSize, err := f.biz.DownloadForRange(ctx, in, start, end, identity) if err != nil { return nil, err @@ -182,10 +168,8 @@ func (f *FileService) DownloadForRange(ctx context.Context, in *api.DownloadRequ gosdk.GetMetadataKey("Accept-Ranges"), "bytes", "X-Http-Code", fmt.Sprintf("%d", http.StatusPartialContent), ) - err = grpc.SendHeader(ctx, rspMd) - if err != nil { - return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.Internal, "send_header") - } + _ = grpc.SendHeader(ctx, rspMd) + return &httpbody.HttpBody{ ContentType: "application/octet-stream", Data: data, @@ -222,13 +206,12 @@ func (f *FileService) Metadata(ctx context.Context, in *api.FileMetadataRequest) gosdk.GetMetadataKey("X-File-Version"), rsp.Version, gosdk.GetMetadataKey("Access-Control-Expose-Headers"), "Content-Length, Content-Range, Accept-Ranges, Last-Modified, ETag, Content-Type, X-File-name, X-File-Sha256", ) - // rspMd.Append(sdk.GetMetadataKey(), "Content-Length", "Content-Range", "Accept-Ranges", "Last-Modified", "ETag", "Content-Type", "x-file-name", "x-file-sha256") - err = grpc.SendHeader(ctx, rspMd) - if err != nil { + _ = grpc.SendHeader(ctx, rspMd) + // if err != nil { - return nil, gosdk.NewError(fmt.Errorf("非法的响应头,%w", err), int32(common.Code_UNKNOWN), codes.Internal, "send_header") + // return nil, gosdk.NewError(fmt.Errorf("非法的响应头,%w", err), int32(common.Code_UNKNOWN), codes.Internal, "send_header") - } + // } if md, ok := metadata.FromIncomingContext(ctx); ok { if httpMethod, ok := md["x-http-method"]; ok { if strings.EqualFold(httpMethod[0], "HEAD") { diff --git a/internal/service/file_test.go b/internal/service/file_test.go index 3371b70..d608e38 100644 --- a/internal/service/file_test.go +++ b/internal/service/file_test.go @@ -8,17 +8,25 @@ import ( "fmt" "io" "net/http" + "net/url" "os" "path/filepath" "runtime" "testing" + "github.com/agiledragon/gomonkey/v2" "github.com/begonia-org/begonia" "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz/file" cfg "github.com/begonia-org/begonia/internal/pkg/config" + "github.com/begonia-org/begonia/internal/pkg/errors" + "github.com/begonia-org/begonia/internal/service" + api "github.com/begonia-org/go-sdk/api/file/v1" "github.com/begonia-org/go-sdk/client" common "github.com/begonia-org/go-sdk/common/api/v1" c "github.com/smartystreets/goconvey/convey" + "google.golang.org/grpc/metadata" ) func sumFileSha256(src string) (string, error) { @@ -121,7 +129,7 @@ func uploadParts(t *testing.T) { c.Convey("test upload file", t, func() { apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) var err error - tmpFile, err = generateRandomFile(1024 * 1024 * 20) + tmpFile, err = generateRandomFile(1024 * 1024 * 2) c.So(err, c.ShouldBeNil) defer os.Remove(tmpFile.path) rsp, err := apiClient.UploadFileWithMuiltParts(context.Background(), tmpFile.path, "test/tmp.bin", true) @@ -221,10 +229,195 @@ func deleteFile(t *testing.T) { }) } +func testRangeDownload(t *testing.T) { + c.Convey("test range download file", t, func() { + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + tmp, err := os.CreateTemp("", "testfile-*.txt") + c.So(err, c.ShouldBeNil) + defer tmp.Close() + defer os.Remove(tmp.Name()) + rsp, err := apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", -1, 128) + c.So(err, c.ShouldBeNil) + c.So(len(rsp), c.ShouldEqual, 129) + + rsp, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1) + c.So(err, c.ShouldBeNil) + c.So(len(rsp), c.ShouldEqual, 1024*1024*2-128) + + }) +} +func testUploadErr(t *testing.T) { + c.Convey("test upload file err", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewFileSvrForTest(cnf, gateway.Log) + _, err := srv.Upload(context.Background(), &api.UploadFileRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("app_id", sdkAPPID)) + _, err = srv.Upload(ctx, &api.UploadFileRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + }) +} +func testDownloadErr(t *testing.T) { + c.Convey("test download file err", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewFileSvrForTest(cnf, gateway.Log) + _, err := srv.Download(context.Background(), &api.DownloadRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("app_id", sdkAPPID)) + _, err = srv.Download(ctx, &api.DownloadRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + patch := gomonkey.ApplyFuncReturn(url.PathUnescape, "", fmt.Errorf("test PathUnescape error")) + defer patch.Reset() + ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-identity", sdkAPPID)) + _, err = srv.Download(ctx, &api.DownloadRequest{Key: "test"}) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test PathUnescape error") + + // patch2 := gomonkey.ApplyFuncReturn(grpc.SendHeader, fmt.Errorf("test SendHeader error")) + // defer patch2.Reset() + // ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-identity", sdkAPPID)) + // _, err = srv.Download(ctx, &api.DownloadRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + // patch2.Reset() + // c.So(err, c.ShouldNotBeNil) + // c.So(err.Error(), c.ShouldContainSubstring, "test SendHeader error") + + }) +} +func testRangeDownloadErr(t *testing.T) { + c.Convey("test range download file err", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewFileSvrForTest(cnf, gateway.Log) + // ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("app_id", sdkAPPID)) + // _, err := srv.DownloadForRange(ctx, &api.DownloadRequest{}) + // c.So(err, c.ShouldNotBeNil) + // c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + cases := []struct { + rangeStr string + err error + }{ + { + rangeStr: "", + err: fmt.Errorf("range header not found"), + }, + { + rangeStr: "0-0", + err: fmt.Errorf("invalid range header"), + }, + { + rangeStr: "bytes=-ffee", + err: fmt.Errorf("invalid end value"), + }, + { + rangeStr: "bytes=ffee-", + err: fmt.Errorf("invalid start value"), + }, + { + rangeStr: "bytes=1024", + err: fmt.Errorf("invalid range specification"), + }, + { + rangeStr: "bytes=tgg-1024", + err: fmt.Errorf("invalid start value"), + }, + { + rangeStr: "bytes=1024-tgg", + err: fmt.Errorf("invalid end value"), + }, + } + for _, cs := range cases { + md := metadata.New(nil) + md.Set("x-identity", sdkAPPID) + if cs.rangeStr != "" { + md.Set("range", cs.rangeStr) + } + ctx := metadata.NewIncomingContext(context.Background(), md) + _, err := srv.DownloadForRange(ctx, &api.DownloadRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, cs.err.Error()) + } + + patch := gomonkey.ApplyFuncReturn((*file.FileUsecase).DownloadForRange, nil, int64(0), fmt.Errorf("test download for range error")) + defer patch.Reset() + md := metadata.New(nil) + md.Set("x-identity", sdkAPPID) + md.Set("range", "bytes=0-0") + ctx := metadata.NewIncomingContext(context.Background(), md) + _, err := srv.DownloadForRange(ctx, &api.DownloadRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test download for range error") + patch.Reset() + + }) +} +func testDelErr(t *testing.T) { + c.Convey("test delete file err", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewFileSvrForTest(cnf, gateway.Log) + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("app_id", sdkAPPID)) + _, err := srv.Delete(ctx, &api.DeleteRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + }) +} +func testMetaErr(t *testing.T) { + c.Convey("test meta file err", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewFileSvrForTest(cnf, gateway.Log) + ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs("app_id", sdkAPPID)) + _, err := srv.Metadata(ctx, &api.FileMetadataRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, errors.ErrIdentityMissing.Error()) + + patch:=gomonkey.ApplyFuncReturn((*file.FileUsecase).Metadata,nil,fmt.Errorf("test metadata error")) + defer patch.Reset() + ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-identity", sdkAPPID)) + _, err = srv.Metadata(ctx, &api.FileMetadataRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test metadata error") + + }) +} func TestFile(t *testing.T) { t.Run("upload", upload) t.Run("download", download) + t.Run("testUploadErr", testUploadErr) + t.Run("testDownloadErr", testDownloadErr) t.Run("uploadParts", uploadParts) + t.Run("testRangeDownload", testRangeDownload) + t.Run("testRangeDownloadErr", testRangeDownloadErr) t.Run("downloadParts", downloadParts) t.Run("deleteFile", deleteFile) + t.Run("testDelErr", testDelErr) + t.Run("testMetaErr", testMetaErr) } diff --git a/internal/service/gateway_test.go b/internal/service/gateway_test.go index 2053743..b47997f 100644 --- a/internal/service/gateway_test.go +++ b/internal/service/gateway_test.go @@ -2,6 +2,7 @@ package service_test import ( "context" + "fmt" "net/http" "os" "path/filepath" @@ -9,6 +10,12 @@ import ( "testing" "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz/endpoint" + "github.com/begonia-org/begonia/internal/service" goloadbalancer "github.com/begonia-org/go-loadbalancer" api "github.com/begonia-org/go-sdk/api/endpoint/v1" "github.com/begonia-org/go-sdk/client" @@ -103,7 +110,15 @@ func getEndpoint(t *testing.T) { // c.So(rsp.Details.Endpoints.Name, c.ShouldEqual, "test") }) } - +func listEndpoint(t *testing.T) { + apiClient := client.NewEndpointAPI(apiAddr, accessKey, secret) + c.Convey("test list endpoint api", t, func() { + rsp, err := apiClient.List(context.Background(), []string{"test", "test2"}, nil) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + c.So(len(rsp.Endpoints), c.ShouldBeGreaterThan, 0) + }) +} func delEndpoint(t *testing.T) { apiClient := client.NewEndpointAPI(apiAddr, accessKey, secret) @@ -121,10 +136,43 @@ func delEndpoint(t *testing.T) { }) } +func testEndpointSvrErr(t *testing.T) { + c.Convey("test endpoint server error", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewEndpointSvrForTest(cnf, gateway.Log) + // _,err:=srv.PostEndpointConfig(context.Background(),nil) + patch := gomonkey.ApplyFuncReturn((*endpoint.EndpointUsecase).AddConfig, nil, fmt.Errorf("test add endpoint error")) + defer patch.Reset() + _, err := srv.Post(context.Background(), nil) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldEqual, "test add endpoint error") + patch.Reset() + + _, err = srv.Update(context.Background(), &api.EndpointSrvUpdateRequest{}) + c.So(err, c.ShouldNotBeNil) + + _, err = srv.Get(context.Background(), &api.DetailsEndpointRequest{}) + c.So(err, c.ShouldNotBeNil) + patch = patch.ApplyFuncReturn((*endpoint.EndpointUsecase).Delete, fmt.Errorf("test DEL endpoint error")) + _, err = srv.Delete(context.Background(), &api.DeleteEndpointRequest{}) + c.So(err, c.ShouldNotBeNil) + patch.Reset() + patch = patch.ApplyFuncReturn((*endpoint.EndpointUsecase).List, nil, fmt.Errorf("test list endpoint error")) + _, err = srv.List(context.Background(), &api.ListEndpointRequest{}) + c.So(err, c.ShouldNotBeNil) + patch.Reset() + }) +} func TestEndpoint(t *testing.T) { t.Run("post", postEndpoint) t.Run("patch", patchEndpoint) t.Run("get", getEndpoint) + t.Run("list", listEndpoint) + t.Run("testErr", testEndpointSvrErr) t.Run("del", delEndpoint) } diff --git a/internal/service/service.go b/internal/service/service.go index 5ba5416..42223d7 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -3,8 +3,11 @@ package service import ( "context" - api "github.com/begonia-org/go-sdk/api/file/v1" - userAPI "github.com/begonia-org/go-sdk/api/user/v1" + app "github.com/begonia-org/go-sdk/api/app/v1" + ep "github.com/begonia-org/go-sdk/api/endpoint/v1" + file "github.com/begonia-org/go-sdk/api/file/v1" + sys "github.com/begonia-org/go-sdk/api/sys/v1" + user "github.com/begonia-org/go-sdk/api/user/v1" "github.com/google/wire" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" @@ -15,40 +18,27 @@ type Service interface { Desc() *grpc.ServiceDesc } -var ProviderSet = wire.NewSet(NewAuthzService,NewUserService, - NewFileService, - NewServices, - NewEndpointsService, - NewAppService, +var ProviderSet = wire.NewSet(NewAuthzService, NewUserService, + NewFileService, + NewServices, + NewEndpointsService, + NewAppService, NewSysService) -var ServiceOptionsSet = wire.NewSet(WithFileService, WithAuthzService) type ServiceOptions func(*grpc.Server, *runtime.ServeMux, string) error -func NewServices(file *FileService, - authz *AuthzService, - ep *EndpointsService, - app *AppService, - sys *SysService, - user *UserService, +func NewServices(file file.FileServiceServer, + authz user.AuthServiceServer, + ep ep.EndpointServiceServer, + app app.AppsServiceServer, + sys sys.SystemServiceServer, + users user.UserServiceServer, ) []Service { services := make([]Service, 0) - services = append(services, file, authz, ep, app, sys,user) + services = append(services, file.(Service), authz.(Service), ep.(Service), app.(Service), sys.(Service), users.(Service)) return services } -func WithFileService(file *FileService, opts []grpc.DialOption) ServiceOptions { - return func(server *grpc.Server, mux *runtime.ServeMux, endpoint string) error { - api.RegisterFileServiceServer(server, file) - return api.RegisterFileServiceHandlerFromEndpoint(context.Background(), mux, endpoint, opts) - } -} -func WithAuthzService(authz *AuthzService, opts []grpc.DialOption) ServiceOptions { - return func(server *grpc.Server, mux *runtime.ServeMux, endpoint string) error { - userAPI.RegisterAuthServiceServer(server, authz) - return userAPI.RegisterAuthServiceHandlerFromEndpoint(context.Background(), mux, endpoint, opts) - } -} func GetIdentity(ctx context.Context) string { md, ok := metadata.FromIncomingContext(ctx) diff --git a/internal/service/sys.go b/internal/service/sys.go index bf78d44..db4b4da 100644 --- a/internal/service/sys.go +++ b/internal/service/sys.go @@ -16,7 +16,7 @@ func (s *SysService) Desc() *grpc.ServiceDesc { return &api.SystemService_ServiceDesc } -func NewSysService() *SysService { +func NewSysService() api.SystemServiceServer { return &SysService{} } diff --git a/internal/service/user.go b/internal/service/user.go index 877202c..9b8ccc7 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -17,7 +17,7 @@ type UserService struct { config *config.Config } -func NewUserService(biz *biz.UserUsecase, log logger.Logger, config *config.Config) *UserService { +func NewUserService(biz *biz.UserUsecase, log logger.Logger, config *config.Config) api.UserServiceServer { return &UserService{biz: biz, log: log, config: config} } diff --git a/internal/service/user_test.go b/internal/service/user_test.go index 42a474a..ddbac67 100644 --- a/internal/service/user_test.go +++ b/internal/service/user_test.go @@ -6,6 +6,12 @@ import ( "testing" "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/service" api "github.com/begonia-org/go-sdk/api/user/v1" "github.com/begonia-org/go-sdk/client" common "github.com/begonia-org/go-sdk/common/api/v1" @@ -71,17 +77,71 @@ func patchUser(t *testing.T) { t, func() { apiClient := client.NewUsersAPI(apiAddr, accessKey, secret) - rsp, err := apiClient.PatchUser(context.Background(), uid, map[string]interface{}{ + rsp, err := apiClient.UpdateUser(context.Background(), uid, map[string]interface{}{ "password": "123456ecfasddccddd", "email": fmt.Sprintf("%s@example.com", time.Now().Format("20060102150405"))}) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) }) } +func testRegisterErr(t *testing.T) { + c.Convey( + "test register user error", + t, + func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewUserSvrForTest(cnf, gateway.Log) + patch := gomonkey.ApplyFuncReturn((*biz.UserUsecase).Add, fmt.Errorf("test add user error")) + defer patch.Reset() + _, err := srv.Register(context.Background(), &api.PostUserRequest{}) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "test add user error") + + }) +} +func testUpdateErr(t *testing.T){ + c.Convey("test update user error",t,func(){ + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewUserSvrForTest(cnf, gateway.Log) + patch := gomonkey.ApplyFuncReturn((*biz.UserUsecase).Update, fmt.Errorf("test update user error")) + defer patch.Reset() + _,err:=srv.Update(context.Background(),&api.PatchUserRequest{Uid: "",Owner: "test-user-01"}) + c.So(err,c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"test update user error") + patch.Reset() + }) +} +func testDelUserErr(t *testing.T){ + c.Convey("test delete user error",t,func(){ + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv := service.NewUserSvrForTest(cnf, gateway.Log) + patch := gomonkey.ApplyFuncReturn((*biz.UserUsecase).Delete, fmt.Errorf("test delete user error")) + defer patch.Reset() + _,err:=srv.Delete(context.Background(),&api.DeleteUserRequest{Uid: ""}) + c.So(err,c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"test delete user error") + patch.Reset() + }) +} func TestUser(t *testing.T) { t.Run("add user", addUser) t.Run("get user", getUser) // uid = "442210231930327040" t.Run("patch user", patchUser) t.Run("delete user", deleteUser) + t.Run("test register user error", testRegisterErr) + t.Run("test update user error",testUpdateErr) + t.Run("test delete user error",testDelUserErr) } diff --git a/internal/service/wire.go b/internal/service/wire.go new file mode 100644 index 0000000..35371ca --- /dev/null +++ b/internal/service/wire.go @@ -0,0 +1,42 @@ +//go:build wireinject +// +build wireinject + +package service + +import ( + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/data" + "github.com/begonia-org/begonia/internal/pkg" + + "github.com/begonia-org/go-sdk/logger" + app "github.com/begonia-org/go-sdk/api/app/v1" + ep "github.com/begonia-org/go-sdk/api/endpoint/v1" + file "github.com/begonia-org/go-sdk/api/file/v1" + sys "github.com/begonia-org/go-sdk/api/sys/v1" + user "github.com/begonia-org/go-sdk/api/user/v1" + + "github.com/google/wire" + + "github.com/spark-lence/tiga" +) + + + +func NewAuthzSvrForTest(config *tiga.Configuration, log logger.Logger)user.AuthServiceServer { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, NewAuthzService)) +} +func NewAPPSvrForTest(config *tiga.Configuration, log logger.Logger)app.AppsServiceServer { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, NewAppService)) +} +func NewEndpointSvrForTest(config *tiga.Configuration, log logger.Logger)ep.EndpointServiceServer { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, NewEndpointsService)) +} +func NewFileSvrForTest(config *tiga.Configuration, log logger.Logger)file.FileServiceServer { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, NewFileService)) +} +func NewSysSvrForTest(config *tiga.Configuration, log logger.Logger)sys.SystemServiceServer { + panic(wire.Build(NewSysService)) +} +func NewUserSvrForTest(config *tiga.Configuration, log logger.Logger)user.UserServiceServer { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, NewUserService)) +} \ No newline at end of file diff --git a/internal/service/wire_gen.go b/internal/service/wire_gen.go new file mode 100644 index 0000000..4683957 --- /dev/null +++ b/internal/service/wire_gen.go @@ -0,0 +1,92 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package service + +import ( + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/biz/endpoint" + "github.com/begonia-org/begonia/internal/biz/file" + "github.com/begonia-org/begonia/internal/data" + "github.com/begonia-org/begonia/internal/pkg/config" + "github.com/begonia-org/begonia/internal/pkg/crypto" + v1_2 "github.com/begonia-org/go-sdk/api/app/v1" + v1_3 "github.com/begonia-org/go-sdk/api/endpoint/v1" + v1_4 "github.com/begonia-org/go-sdk/api/file/v1" + v1_5 "github.com/begonia-org/go-sdk/api/sys/v1" + "github.com/begonia-org/go-sdk/api/user/v1" + "github.com/begonia-org/go-sdk/logger" + "github.com/spark-lence/tiga" +) + +// Injectors from wire.go: + +func NewAuthzSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1.AuthServiceServer { + redisDao := data.NewRDB(config2) + configConfig := config.NewConfig(config2) + layeredCache := data.NewLayeredCache(redisDao, configConfig, log) + authzRepo := data.NewAuthzRepoImpl(log, layeredCache) + mySQLDao := data.NewMySQL(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + curd := data.NewCurdImpl(mySQLDao, configConfig) + userRepo := data.NewUserRepoImpl(dataData, layeredCache, curd, configConfig) + usersAuth := crypto.NewUsersAuth() + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authServiceServer := NewAuthzService(authzUsecase, log, usersAuth, configConfig) + return authServiceServer +} + +func NewAPPSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_2.AppsServiceServer { + mySQLDao := data.NewMySQL(config2) + configConfig := config.NewConfig(config2) + curd := data.NewCurdImpl(mySQLDao, configConfig) + redisDao := data.NewRDB(config2) + layeredCache := data.NewLayeredCache(redisDao, configConfig, log) + appRepo := data.NewAppRepoImpl(curd, layeredCache, configConfig) + appUsecase := biz.NewAppUsecase(appRepo, configConfig) + appsServiceServer := NewAppService(appUsecase, log, configConfig) + return appsServiceServer +} + +func NewEndpointSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_3.EndpointServiceServer { + mySQLDao := data.NewMySQL(config2) + redisDao := data.NewRDB(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + configConfig := config.NewConfig(config2) + endpointRepo := data.NewEndpointRepoImpl(dataData, configConfig) + fileUsecase := file.NewFileUsecase(configConfig) + endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, fileUsecase, configConfig) + endpointServiceServer := NewEndpointsService(endpointUsecase, log, configConfig) + return endpointServiceServer +} + +func NewFileSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_4.FileServiceServer { + configConfig := config.NewConfig(config2) + fileUsecase := file.NewFileUsecase(configConfig) + fileServiceServer := NewFileService(fileUsecase, configConfig) + return fileServiceServer +} + +func NewSysSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_5.SystemServiceServer { + systemServiceServer := NewSysService() + return systemServiceServer +} + +func NewUserSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1.UserServiceServer { + mySQLDao := data.NewMySQL(config2) + redisDao := data.NewRDB(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + configConfig := config.NewConfig(config2) + layeredCache := data.NewLayeredCache(redisDao, configConfig, log) + curd := data.NewCurdImpl(mySQLDao, configConfig) + userRepo := data.NewUserRepoImpl(dataData, layeredCache, curd, configConfig) + userUsecase := biz.NewUserUsecase(userRepo, configConfig) + userServiceServer := NewUserService(userUsecase, log, configConfig) + return userServiceServer +}