Files
ManuAgent/server/src/test/java/tech/easyflow/manuagent/model/ModelServiceQueryTest.java
Zhu Junhao 988c0fe555 feat: 完善多模型配置与运行切换
将模型连接统一持久化管理,并支持 Agent 运行中切换模型以及中断后选择模型继续。补充配置校验、事务与前后端交互测试。
2026-09-03 10:46:25 +08:00

86 lines
3.4 KiB
Java

package tech.easyflow.manuagent.model;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.springframework.boot.ApplicationRunner;
import org.mockito.ArgumentCaptor;
import tech.easyflow.manuagent.auth.UserService;
import tech.easyflow.manuagent.config.AppProperties;
import tech.easyflow.manuagent.entity.ModelConfigEntity;
import tech.easyflow.manuagent.mapper.AppUserMapper;
import tech.easyflow.manuagent.mapper.ModelAssignmentMapper;
import tech.easyflow.manuagent.mapper.ModelConfigMapper;
import tech.easyflow.manuagent.mapper.AgentRunMapper;
/**
* 验证模型管理接口使用最小字段投影,不把加密 API Key 读入普通请求内存。
*/
class ModelServiceQueryTest {
/**
* 模型必须完全由管理接口和数据库维护,服务启动不得再通过 Key 文件自动灌入默认模型。
*/
@Test
void shouldNotInitializeModelConfigurationFromApplicationFiles() {
assertThat(ApplicationRunner.class.isAssignableFrom(ModelService.class)).isFalse();
}
/**
* 模型列表只需要页面展示字段,查询 SQL 不得包含密文和密钥版本列。
*/
@Test
void shouldExcludeEncryptedKeyFromModelListQuery() {
ModelConfigMapper modelMapper = mock(ModelConfigMapper.class);
when(modelMapper.selectListByQuery(any(QueryWrapper.class)))
.thenReturn(List.of(modelViewEntity()));
ModelService service = new ModelService(
modelMapper,
mock(ModelAssignmentMapper.class),
mock(AgentRunMapper.class),
mock(UserService.class),
mock(KeyCipher.class),
new ObjectMapper());
List<ModelService.ModelView> models = service.list();
ArgumentCaptor<QueryWrapper> queryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
verify(modelMapper).selectListByQuery(queryCaptor.capture());
String sql = queryCaptor.getValue().toSQL().toLowerCase(java.util.Locale.ROOT);
assertThat(models).hasSize(1);
assertThat(sql)
.contains("api_key_hint", "capabilities_json", "is_default")
.doesNotContain("api_key_ciphertext", "key_version");
}
/**
* 构造包含全部展示字段的实体,避免测试依赖数据库或模型密钥解密逻辑。
*
* @return 模拟数据库返回的安全投影实体
*/
private ModelConfigEntity modelViewEntity() {
ModelConfigEntity entity = new ModelConfigEntity();
entity.setId(UUID.randomUUID());
entity.setName("测试模型");
entity.setProvider("OPENAI_COMPATIBLE");
entity.setBaseUrl("https://example.test");
entity.setModelId("model");
entity.setApiKeyHint("••••1234");
entity.setConfigJson("{}");
entity.setCapabilitiesJson("{\"contextWindow\":8192}");
entity.setEnabled(true);
entity.setDefaultModel(true);
entity.setUpdatedAt(OffsetDateTime.now());
return entity;
}
}