diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/constant/TestCaseAIGeneratorConstant.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/constant/TestCaseAIGeneratorConstant.java new file mode 100644 index 00000000..b2d70191 --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/constant/TestCaseAIGeneratorConstant.java @@ -0,0 +1,11 @@ +package cd.casic.ci.process.constant; +/** + * AI测试用例生成url常量类 + * */ +public class TestCaseAIGeneratorConstant { + public static final String targetUpload ="/upload-target"; + public static final String taskCreate ="/create-task"; + public static final String getTaskStatus ="/query-task/"; + public static final String getTaskLog ="/task-log/"; + public static final String downloadTestCase ="/download-case/"; +} diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITargetUploadReq.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITargetUploadReq.java new file mode 100644 index 00000000..6ee7c99f --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITargetUploadReq.java @@ -0,0 +1,13 @@ +package cd.casic.ci.process.dto.req.testCase; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.zip.ZipOutputStream; + +@Data +public class TestCaseAITargetUploadReq { + private String binaryPath; + private MultipartFile file; +} diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITaskCreateReq.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITaskCreateReq.java new file mode 100644 index 00000000..ca022c80 --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/dto/req/testCase/TestCaseAITaskCreateReq.java @@ -0,0 +1,25 @@ +package cd.casic.ci.process.dto.req.testCase; + +import lombok.Data; +/** + * { + * "model_id": "gpt-4", // 可选,模型ID,默认值为:deepseek/deepseekchat + * "api_key": "your-api-key", // 可选,API密钥 + * "api_base": "https://api.openai.com/v1", // 可选,API基础URL,默认值为: + * https://api.deepseek.com + * "binary_id": "abc123def456", // 条件可选,二进制文件ID, 如果未提供,则需要提供 + * case_format参数 + * "case_format": "png", // 条件可选,测试用例文件格式, 如果未提供,则需要提 + * 供binary_id参数 + * "custom_prompt": "", // 可选,测试用例生成任务的自定义提示词, 如:"构造 + * 尽可能小的png文件" + * "count": 10 // 可选,生成用例数量,默认10 + * } + * */ +@Data +public class TestCaseAITaskCreateReq { + private String binaryId; + private String caseFormat; + private String customPrompt; + private Integer count; +} diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/TestCaseAIGeneratorService.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/TestCaseAIGeneratorService.java new file mode 100644 index 00000000..29946d31 --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/TestCaseAIGeneratorService.java @@ -0,0 +1,22 @@ +package cd.casic.ci.process.process.service.testCase; + +import cd.casic.ci.process.dto.req.testCase.TestCaseAITargetUploadReq; +import cd.casic.ci.process.dto.req.testCase.TestCaseAITaskCreateReq; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface TestCaseAIGeneratorService { + /** + * 目标文件上传 + * */ + public String targetUpload(String binaryPath, InputStream inputStream) throws IOException; + /** + * 创建测试用例生成任务 + * */ + public String taskCreate(TestCaseAITaskCreateReq req); + public String getTaskStatus(String taskId); + public String getTaskLog(String taskId); + public void downloadTestCase(String taskId, OutputStream outputStream) throws IOException; +} diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/impl/TestCaseAIGeneratorServiceImpl.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/impl/TestCaseAIGeneratorServiceImpl.java new file mode 100644 index 00000000..c501fad8 --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/process/service/testCase/impl/TestCaseAIGeneratorServiceImpl.java @@ -0,0 +1,106 @@ +package cd.casic.ci.process.process.service.testCase.impl; + +import cd.casic.ci.process.constant.TestCaseAIGeneratorConstant; +import cd.casic.ci.process.dto.req.testCase.TestCaseAITargetUploadReq; +import cd.casic.ci.process.dto.req.testCase.TestCaseAITaskCreateReq; +import cd.casic.ci.process.process.service.testCase.TestCaseAIGeneratorService; +import cd.casic.ci.process.properties.TestCaseAIProperties; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.amazonaws.util.IOUtils; +import jakarta.annotation.Resource; +import net.schmizz.sshj.transport.kex.ECDHNistP; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.util.HashMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +@Service +public class TestCaseAIGeneratorServiceImpl implements TestCaseAIGeneratorService { + @Resource + private TestCaseAIProperties properties; + @Resource + private RestTemplate restTemplate; + + + @Override + public String targetUpload(String binaryPath, InputStream inputStream) throws IOException { + File tempFile = File.createTempFile("target", ".zip"); + tempFile.deleteOnExit(); + FileOutputStream fos = new FileOutputStream(tempFile); + ZipOutputStream zos = new ZipOutputStream(fos); + ZipEntry entry = new ZipEntry(tempFile.getName()); + zos.putNextEntry(entry); + IOUtils.copy(inputStream,zos); + zos.closeEntry(); + inputStream.close(); + FileSystemResource fsr = new FileSystemResource(tempFile); + String url = properties.getBaseUrl()+ TestCaseAIGeneratorConstant.targetUpload; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file",fsr); + body.add("binary_path",binaryPath); + HttpEntity> entity = new HttpEntity<>(body,headers); + ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.POST, entity, String.class, new HashMap<>()); + return exchange.getBody(); + } + + @Override + public String taskCreate(TestCaseAITaskCreateReq req) { + if (StringUtils.hasLength(req.getBinaryId()) &&StringUtils.hasLength(req.getCaseFormat())) { + return ""; + } + JSONObject jsonObject = new JSONObject(); + jsonObject.put("binary_id",req.getBinaryId()); + jsonObject.put("count",req.getCount()); + jsonObject.put("custom_prompt",req.getCustomPrompt()); + jsonObject.put("case_format",req.getCaseFormat()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(JSON.toJSONString(jsonObject),headers); + String url = properties.getBaseUrl() + TestCaseAIGeneratorConstant.taskCreate; + ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.POST, entity, String.class, new HashMap<>()); + return exchange.getBody(); + } + + @Override + public String getTaskStatus(String taskId) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>("{}",headers); + String url = properties.getBaseUrl() + TestCaseAIGeneratorConstant.getTaskStatus+taskId; + ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.GET, entity, String.class, new HashMap<>()); + return exchange.getBody(); + } + + @Override + public String getTaskLog(String taskId) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>("{}",headers); + String url = properties.getBaseUrl() + TestCaseAIGeneratorConstant.getTaskLog+taskId; + ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.GET, entity, String.class, new HashMap<>()); + return exchange.getBody(); + } + + @Override + public void downloadTestCase(String taskId, OutputStream outputStream) throws IOException { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>("{}",headers); + String url = properties.getBaseUrl() + TestCaseAIGeneratorConstant.downloadTestCase+taskId; + ResponseEntity exchange = restTemplate.exchange(url, HttpMethod.GET, entity, InputStream.class, new HashMap<>()); + IOUtils.copy(exchange.getBody(),outputStream); + } +} diff --git a/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/properties/TestCaseAIProperties.java b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/properties/TestCaseAIProperties.java new file mode 100644 index 00000000..bf8952dd --- /dev/null +++ b/modules/module-ci-process-biz/src/main/java/cd/casic/ci/process/properties/TestCaseAIProperties.java @@ -0,0 +1,12 @@ +package cd.casic.ci.process.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@ConfigurationProperties(prefix = "test-case.ai") +@Component +@Data +public class TestCaseAIProperties { + private String baseUrl; +} diff --git a/ops-server/src/test/java/cd/casic/server/DockerTest.java b/ops-server/src/test/java/cd/casic/server/DockerTest.java index 531292bb..c5df6aa8 100644 --- a/ops-server/src/test/java/cd/casic/server/DockerTest.java +++ b/ops-server/src/test/java/cd/casic/server/DockerTest.java @@ -1,4 +1,5 @@ package cd.casic.server; +import cd.casic.module.machine.utils.CryptogramUtil; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.ExecCreateCmdResponse;