Skip to content

Commit

Permalink
feat(ai-function): create testset if not set testSetID
Browse files Browse the repository at this point in the history
  • Loading branch information
wangzhuzhen committed Sep 14, 2023
1 parent 8cad1c9 commit 2eb1daa
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 14 deletions.
95 changes: 95 additions & 0 deletions bundle/testset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bundle

import (
"encoding/json"
"fmt"

"github.com/pkg/errors"
log "github.com/sirupsen/logrus"

"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/bundle/apierrors"
"github.com/erda-project/erda/pkg/http/httpserver"
"github.com/erda-project/erda/pkg/http/httputil"
)

func (b *Bundle) CreateTestSet(req apistructs.TestSetCreateRequest) (*apistructs.TestSet, error) {
host, err := b.urls.ErdaServer()
if err != nil {
return &apistructs.TestSet{}, err
}
var resp httpserver.Resp
r, err := b.hc.Post(host).Path("/api/testsets").
Header(httputil.InternalHeader, "AI").
Header(httputil.UserHeader, req.IdentityInfo.UserID).
JSONBody(&req).Do().JSON(&resp)

if err != nil {
log.Errorf("CreateTestSet err: %v", err)
return &apistructs.TestSet{}, apierrors.ErrInvoke.InternalError(err)
}
if !r.IsOK() || !resp.Success {
log.Errorf("CreateTestSet response is not ok with StatusCode %d", r.StatusCode())
return &apistructs.TestSet{}, apierrors.ErrInvoke.InternalError(errors.Errorf("CreateTestSet response is not ok with StatusCode %d", r.StatusCode()))
}

testSetJsonBytes, err := json.Marshal(resp.Data)
if err != nil {
return &apistructs.TestSet{}, errors.Errorf("json.Marshal(resp.Data) failed: %v", err)
}

var testSet apistructs.TestSet
err = json.Unmarshal(testSetJsonBytes, &testSet)
if err != nil {
return &apistructs.TestSet{}, errors.Errorf("json.Unmarshal(testSetJsonBytes, &testSet) to apistructs.TestSet failed: %v", err)
}

return &testSet, nil
}

func (b *Bundle) GetTestSets(req apistructs.TestSetListRequest) ([]apistructs.TestSet, error) {
host, err := b.urls.ErdaServer()
if err != nil {
return nil, err
}
pathVars := fmt.Sprintf("?recycled=%v&parentID=%d&projectID=%d", req.Recycled, *req.ParentID, *req.ProjectID)
var resp httpserver.Resp
r, err := b.hc.Get(host).Path("/api/testsets"+pathVars).
Header(httputil.InternalHeader, "AI").
JSONBody(&req).Do().JSON(&resp)

if err != nil {
log.Errorf("CreateTestSet err: %v", err)
return nil, apierrors.ErrInvoke.InternalError(err)
}
if !r.IsOK() || !resp.Success {
return nil, apierrors.ErrInvoke.InternalError(errors.Errorf("CreateTestSet response is not ok with StatusCode %d", r.StatusCode()))
}

testSetJsonBytes, err := json.Marshal(resp.Data)
if err != nil {
return nil, errors.Errorf("json.Marshal(resp.Data) failed: %v", err)
}

var testSets []apistructs.TestSet
err = json.Unmarshal(testSetJsonBytes, &testSets)
if err != nil {
return nil, errors.Errorf("json.Unmarshal(testSetJsonBytes, &testSet) to apistructs.TestSet failed: %v", err)
}

return testSets, nil
}
2 changes: 1 addition & 1 deletion cmd/erda-server/bootstrap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -619,4 +619,4 @@ grpc-client@erda.core.monitor.settings:
erda.core.monitor.settings-client:
service-profile-overview:
erda.app.ai-function:
openaiAddr: ${OPENAI_ADDR:http://ai-proxy:8081}
openaiAddr: ${AI_PROXY_ADDR:http://ai-proxy:8081}
14 changes: 8 additions & 6 deletions internal/pkg/ai-functions/functions/test-case/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,22 @@ type FunctionParams struct {
Requirements []TestCaseParam `json:"requirements,omitempty"`
}
type TestCaseParam struct {
IssueID uint64 `json:"issueID,omitempty"`
Prompt string `json:"prompt,omitempty"`
IssueID uint64 `json:"issueID,omitempty"`
Prompt string `json:"prompt,omitempty"`
Req apistructs.TestCaseCreateRequest `json:"testCaseCreateReq,omitempty"`
}

// TestCaseFunctionInput 用于为单个需求生成测试用例的输入
type TestCaseFunctionInput struct {
TestSetID uint64
IssueID uint64
Prompt string
TestSetParentID uint64
TestSetID uint64
IssueID uint64
Prompt string
}

// TestCaseMeta 用于关联生成的测试用例与对应的需求
type TestCaseMeta struct {
Req apistructs.TestCaseCreateRequest `json:"testCaseCreateReq,omitempty"` // 当前项目 ID,用于权限校验
Req apistructs.TestCaseCreateRequest `json:"testCaseCreateReq,omitempty"` // 当前项目 ID 对应的创建测试用例请求
RequirementName string `json:"requirementName"` // 需求对应的 issue 的 Title
RequirementID uint64 `json:"requirementID"` // 需求对应的 issueID
TestCaseID uint64 `json:"testcaseID,omitempty"` // 创建测试用例成功返回的测试用例 ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ import (
"github.com/sirupsen/logrus"

"github.com/erda-project/erda-proto-go/apps/aifunction/pb"
"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/bundle"
"github.com/erda-project/erda/internal/pkg/ai-functions/functions"
aitestcase "github.com/erda-project/erda/internal/pkg/ai-functions/functions/test-case"
aiHandlerUtils "github.com/erda-project/erda/internal/pkg/ai-functions/handler/utils"
"github.com/erda-project/erda/pkg/http/httpserver"
)

const AIGeneratedTestSeName = "AI_Generated"

func (h *AIFunction) createTestCaseForRequirementIDAndTestID(ctx context.Context, factory functions.FunctionFactory, req *pb.ApplyRequest, openaiURL *url.URL) (any, error) {
results := make([]any, 0)
var wg sync.WaitGroup
Expand All @@ -48,6 +52,46 @@ func (h *AIFunction) createTestCaseForRequirementIDAndTestID(ctx context.Context
return nil, errors.Wrapf(err, "validateParamsForCreateTestcase faild")
}

// 用户未指定测试集可能需要创建测试集
if functionParams.TestSetID == 0 {
bdl := bundle.New(bundle.WithErdaServer())
var parentTestSetId uint64 = 0
projectId := req.GetBackground().GetProjectID()
userId := req.GetBackground().GetUserID()

testSets, err := bdl.GetTestSets(apistructs.TestSetListRequest{
ParentID: &parentTestSetId,
ProjectID: &projectId,
})
if err != nil {
return nil, errors.Wrap(err, "get testSets by project failed")
}

needCreate := true
for _, testSet := range testSets {
if testSet.ParentID == 0 && testSet.Name == AIGeneratedTestSeName {
functionParams.TestSetID = testSet.ID
needCreate = false
break
}
}

if needCreate {
testSet, err := bdl.CreateTestSet(apistructs.TestSetCreateRequest{
ProjectID: &projectId,
ParentID: &parentTestSetId,
Name: AIGeneratedTestSeName,
IdentityInfo: apistructs.IdentityInfo{
UserID: userId,
},
})
if err != nil {
return nil, errors.Wrap(err, "create TestSet failed")
}
functionParams.TestSetID = testSet.ID
}
}

for _, tp := range functionParams.Requirements {
wg.Add(1)
var err error
Expand All @@ -72,19 +116,43 @@ func processSingleTestCase(ctx context.Context, factory functions.FunctionFactor
IssueID: tp.IssueID,
Prompt: tp.Prompt,
}
if len(tp.Req.StepAndResults) > 0 {
// 表示是修改后批量应用应用生成的测试用例,直接调用创建接口,无需再次生成
bdl := bundle.New(bundle.WithErdaServer())
// 根据 issueID 获取对应的需求 Title
issue, err := bdl.GetIssue(tp.IssueID)
if err != nil {
return errors.Wrap(err, "get requirement info failed when create testcase")
}

result, err := aiHandlerUtils.GetChatMessageFunctionCallArguments(ctx, factory, req, openaiURL, tp.Prompt, callbackInput)
if err != nil {
return err
aiCreateTestCaseResponse, err := bdl.CreateTestCase(tp.Req)
if err != nil {
err = errors.Errorf("create testcase with req %+v failed: %v", tp.Req, err)
return errors.Wrap(err, "bundle CreateTestCase failed for ")
}

*results = append(*results, aitestcase.TestCaseMeta{
Req: tp.Req,
RequirementName: issue.Title,
RequirementID: tp.IssueID,
TestCaseID: aiCreateTestCaseResponse.TestCaseID,
})
} else {
// 表示需要生成
result, err := aiHandlerUtils.GetChatMessageFunctionCallArguments(ctx, factory, req, openaiURL, tp.Prompt, callbackInput)
if err != nil {
return err
}

*results = append(*results, result)
}

*results = append(*results, result)
return nil
}

// validateParamsForCreateTestcase 校验创建测试用例对应的参数配置
func validateParamsForCreateTestcase(req aitestcase.FunctionParams) error {
if req.TestSetID <= 0 {
if req.TestSetID < 0 {
return errors.Errorf("AI function functionParams testSetID for %s invalid", aitestcase.Name)
}

Expand Down
Loading

0 comments on commit 2eb1daa

Please sign in to comment.