mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
- 添加账户分组管理功能,支持创建、编辑、删除分组 - 实现基于分组的账户调度逻辑 - 添加分组权重和优先级支持 - 提供测试脚本验证多分组调度功能 - 修复代码格式化问题(统一使用LF换行符) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
380 lines
13 KiB
JavaScript
380 lines
13 KiB
JavaScript
/**
|
||
* 多分组功能测试脚本
|
||
* 测试一个账户可以属于多个分组的功能
|
||
*/
|
||
|
||
require('dotenv').config()
|
||
const redis = require('../src/models/redis')
|
||
const accountGroupService = require('../src/services/accountGroupService')
|
||
const claudeAccountService = require('../src/services/claudeAccountService')
|
||
|
||
// 测试配置
|
||
const TEST_PREFIX = 'multi_group_test_'
|
||
const CLEANUP_ON_FINISH = true
|
||
|
||
// 测试数据存储
|
||
const testData = {
|
||
groups: [],
|
||
accounts: []
|
||
}
|
||
|
||
// 颜色输出
|
||
const colors = {
|
||
green: '\x1b[32m',
|
||
red: '\x1b[31m',
|
||
yellow: '\x1b[33m',
|
||
blue: '\x1b[34m',
|
||
reset: '\x1b[0m'
|
||
}
|
||
|
||
function log(message, type = 'info') {
|
||
const color =
|
||
{
|
||
success: colors.green,
|
||
error: colors.red,
|
||
warning: colors.yellow,
|
||
info: colors.blue
|
||
}[type] || colors.reset
|
||
|
||
console.log(`${color}${message}${colors.reset}`)
|
||
}
|
||
|
||
async function sleep(ms) {
|
||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||
}
|
||
|
||
// 清理测试数据
|
||
async function cleanup() {
|
||
log('\n🧹 清理测试数据...', 'info')
|
||
|
||
// 删除测试账户
|
||
for (const account of testData.accounts) {
|
||
try {
|
||
await claudeAccountService.deleteAccount(account.id)
|
||
log(`✅ 删除测试账户: ${account.name}`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 删除账户失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
|
||
// 删除测试分组
|
||
for (const group of testData.groups) {
|
||
try {
|
||
// 先移除所有成员
|
||
const members = await accountGroupService.getGroupMembers(group.id)
|
||
for (const memberId of members) {
|
||
await accountGroupService.removeAccountFromGroup(memberId, group.id)
|
||
}
|
||
|
||
await accountGroupService.deleteGroup(group.id)
|
||
log(`✅ 删除测试分组: ${group.name}`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 删除分组失败: ${error.message}`, 'error')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 测试1: 创建测试数据
|
||
async function test1_createTestData() {
|
||
log('\n📝 测试1: 创建测试数据', 'info')
|
||
|
||
try {
|
||
// 创建3个测试分组
|
||
const group1 = await accountGroupService.createGroup({
|
||
name: `${TEST_PREFIX}高优先级组`,
|
||
platform: 'claude',
|
||
description: '高优先级账户分组'
|
||
})
|
||
testData.groups.push(group1)
|
||
log(`✅ 创建分组1: ${group1.name}`, 'success')
|
||
|
||
const group2 = await accountGroupService.createGroup({
|
||
name: `${TEST_PREFIX}备用组`,
|
||
platform: 'claude',
|
||
description: '备用账户分组'
|
||
})
|
||
testData.groups.push(group2)
|
||
log(`✅ 创建分组2: ${group2.name}`, 'success')
|
||
|
||
const group3 = await accountGroupService.createGroup({
|
||
name: `${TEST_PREFIX}专用组`,
|
||
platform: 'claude',
|
||
description: '专用账户分组'
|
||
})
|
||
testData.groups.push(group3)
|
||
log(`✅ 创建分组3: ${group3.name}`, 'success')
|
||
|
||
// 创建测试账户
|
||
const account1 = await claudeAccountService.createAccount({
|
||
name: `${TEST_PREFIX}测试账户1`,
|
||
email: 'test1@example.com',
|
||
refreshToken: 'test_refresh_token_1',
|
||
accountType: 'group'
|
||
})
|
||
testData.accounts.push(account1)
|
||
log(`✅ 创建测试账户1: ${account1.name}`, 'success')
|
||
|
||
const account2 = await claudeAccountService.createAccount({
|
||
name: `${TEST_PREFIX}测试账户2`,
|
||
email: 'test2@example.com',
|
||
refreshToken: 'test_refresh_token_2',
|
||
accountType: 'group'
|
||
})
|
||
testData.accounts.push(account2)
|
||
log(`✅ 创建测试账户2: ${account2.name}`, 'success')
|
||
|
||
log(`✅ 测试数据创建完成: 3个分组, 2个账户`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试1失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 测试2: 账户加入多个分组
|
||
async function test2_addAccountToMultipleGroups() {
|
||
log('\n📝 测试2: 账户加入多个分组', 'info')
|
||
|
||
try {
|
||
const [group1, group2, group3] = testData.groups
|
||
const [account1, account2] = testData.accounts
|
||
|
||
// 账户1加入分组1和分组2
|
||
await accountGroupService.addAccountToGroup(account1.id, group1.id, 'claude')
|
||
log(`✅ 账户1加入分组1: ${group1.name}`, 'success')
|
||
|
||
await accountGroupService.addAccountToGroup(account1.id, group2.id, 'claude')
|
||
log(`✅ 账户1加入分组2: ${group2.name}`, 'success')
|
||
|
||
// 账户2加入分组2和分组3
|
||
await accountGroupService.addAccountToGroup(account2.id, group2.id, 'claude')
|
||
log(`✅ 账户2加入分组2: ${group2.name}`, 'success')
|
||
|
||
await accountGroupService.addAccountToGroup(account2.id, group3.id, 'claude')
|
||
log(`✅ 账户2加入分组3: ${group3.name}`, 'success')
|
||
|
||
log(`✅ 多分组关系建立完成`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试2失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 测试3: 验证多分组关系
|
||
async function test3_verifyMultiGroupRelationships() {
|
||
log('\n📝 测试3: 验证多分组关系', 'info')
|
||
|
||
try {
|
||
const [group1, group2, group3] = testData.groups
|
||
const [account1, account2] = testData.accounts
|
||
|
||
// 验证账户1的分组关系
|
||
const account1Groups = await accountGroupService.getAccountGroup(account1.id)
|
||
log(`📊 账户1所属分组数量: ${account1Groups.length}`, 'info')
|
||
|
||
const account1GroupNames = account1Groups.map((g) => g.name).sort()
|
||
const expectedAccount1Groups = [group1.name, group2.name].sort()
|
||
|
||
if (JSON.stringify(account1GroupNames) === JSON.stringify(expectedAccount1Groups)) {
|
||
log(`✅ 账户1分组关系正确: [${account1GroupNames.join(', ')}]`, 'success')
|
||
} else {
|
||
throw new Error(
|
||
`账户1分组关系错误,期望: [${expectedAccount1Groups.join(', ')}], 实际: [${account1GroupNames.join(', ')}]`
|
||
)
|
||
}
|
||
|
||
// 验证账户2的分组关系
|
||
const account2Groups = await accountGroupService.getAccountGroup(account2.id)
|
||
log(`📊 账户2所属分组数量: ${account2Groups.length}`, 'info')
|
||
|
||
const account2GroupNames = account2Groups.map((g) => g.name).sort()
|
||
const expectedAccount2Groups = [group2.name, group3.name].sort()
|
||
|
||
if (JSON.stringify(account2GroupNames) === JSON.stringify(expectedAccount2Groups)) {
|
||
log(`✅ 账户2分组关系正确: [${account2GroupNames.join(', ')}]`, 'success')
|
||
} else {
|
||
throw new Error(
|
||
`账户2分组关系错误,期望: [${expectedAccount2Groups.join(', ')}], 实际: [${account2GroupNames.join(', ')}]`
|
||
)
|
||
}
|
||
|
||
log(`✅ 多分组关系验证通过`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试3失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 测试4: 验证分组成员关系
|
||
async function test4_verifyGroupMemberships() {
|
||
log('\n📝 测试4: 验证分组成员关系', 'info')
|
||
|
||
try {
|
||
const [group1, group2, group3] = testData.groups
|
||
const [account1, account2] = testData.accounts
|
||
|
||
// 验证分组1的成员
|
||
const group1Members = await accountGroupService.getGroupMembers(group1.id)
|
||
if (group1Members.includes(account1.id) && group1Members.length === 1) {
|
||
log(`✅ 分组1成员正确: [${account1.name}]`, 'success')
|
||
} else {
|
||
throw new Error(`分组1成员错误,期望: [${account1.id}], 实际: [${group1Members.join(', ')}]`)
|
||
}
|
||
|
||
// 验证分组2的成员(应该包含两个账户)
|
||
const group2Members = await accountGroupService.getGroupMembers(group2.id)
|
||
const expectedGroup2Members = [account1.id, account2.id].sort()
|
||
const actualGroup2Members = group2Members.sort()
|
||
|
||
if (JSON.stringify(actualGroup2Members) === JSON.stringify(expectedGroup2Members)) {
|
||
log(`✅ 分组2成员正确: [${account1.name}, ${account2.name}]`, 'success')
|
||
} else {
|
||
throw new Error(
|
||
`分组2成员错误,期望: [${expectedGroup2Members.join(', ')}], 实际: [${actualGroup2Members.join(', ')}]`
|
||
)
|
||
}
|
||
|
||
// 验证分组3的成员
|
||
const group3Members = await accountGroupService.getGroupMembers(group3.id)
|
||
if (group3Members.includes(account2.id) && group3Members.length === 1) {
|
||
log(`✅ 分组3成员正确: [${account2.name}]`, 'success')
|
||
} else {
|
||
throw new Error(`分组3成员错误,期望: [${account2.id}], 实际: [${group3Members.join(', ')}]`)
|
||
}
|
||
|
||
log(`✅ 分组成员关系验证通过`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试4失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 测试5: 从部分分组中移除账户
|
||
async function test5_removeFromPartialGroups() {
|
||
log('\n📝 测试5: 从部分分组中移除账户', 'info')
|
||
|
||
try {
|
||
const [group1, group2] = testData.groups
|
||
const [account1] = testData.accounts
|
||
|
||
// 将账户1从分组1中移除(但仍在分组2中)
|
||
await accountGroupService.removeAccountFromGroup(account1.id, group1.id)
|
||
log(`✅ 从分组1中移除账户1`, 'success')
|
||
|
||
// 验证账户1现在只属于分组2
|
||
const account1Groups = await accountGroupService.getAccountGroup(account1.id)
|
||
if (account1Groups.length === 1 && account1Groups[0].id === group2.id) {
|
||
log(`✅ 账户1现在只属于分组2: ${account1Groups[0].name}`, 'success')
|
||
} else {
|
||
const groupNames = account1Groups.map((g) => g.name)
|
||
throw new Error(`账户1分组状态错误,期望只在分组2中,实际: [${groupNames.join(', ')}]`)
|
||
}
|
||
|
||
// 验证分组1现在为空
|
||
const group1Members = await accountGroupService.getGroupMembers(group1.id)
|
||
if (group1Members.length === 0) {
|
||
log(`✅ 分组1现在为空`, 'success')
|
||
} else {
|
||
throw new Error(`分组1应该为空,但还有成员: [${group1Members.join(', ')}]`)
|
||
}
|
||
|
||
// 验证分组2仍有两个成员
|
||
const group2Members = await accountGroupService.getGroupMembers(group2.id)
|
||
if (group2Members.length === 2) {
|
||
log(`✅ 分组2仍有两个成员`, 'success')
|
||
} else {
|
||
throw new Error(`分组2应该有2个成员,实际: ${group2Members.length}个`)
|
||
}
|
||
|
||
log(`✅ 部分移除测试通过`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试5失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 测试6: 账户完全移除时的分组清理
|
||
async function test6_accountDeletionGroupCleanup() {
|
||
log('\n📝 测试6: 账户删除时的分组清理', 'info')
|
||
|
||
try {
|
||
const [, group2, group3] = testData.groups // 跳过第一个元素
|
||
const [account1, account2] = testData.accounts
|
||
|
||
// 记录删除前的状态
|
||
const beforeGroup2Members = await accountGroupService.getGroupMembers(group2.id)
|
||
const beforeGroup3Members = await accountGroupService.getGroupMembers(group3.id)
|
||
|
||
log(`📊 删除前分组2成员数: ${beforeGroup2Members.length}`, 'info')
|
||
log(`📊 删除前分组3成员数: ${beforeGroup3Members.length}`, 'info')
|
||
|
||
// 删除账户2(这应该会触发从所有分组中移除的逻辑)
|
||
await claudeAccountService.deleteAccount(account2.id)
|
||
log(`✅ 删除账户2: ${account2.name}`, 'success')
|
||
|
||
// 从测试数据中移除,避免cleanup时重复删除
|
||
testData.accounts = testData.accounts.filter((acc) => acc.id !== account2.id)
|
||
|
||
// 等待一下确保删除操作完成
|
||
await sleep(500)
|
||
|
||
// 验证分组2现在只有账户1
|
||
const afterGroup2Members = await accountGroupService.getGroupMembers(group2.id)
|
||
if (afterGroup2Members.length === 1 && afterGroup2Members[0] === account1.id) {
|
||
log(`✅ 分组2现在只有账户1`, 'success')
|
||
} else {
|
||
throw new Error(`分组2成员状态错误,期望只有账户1,实际: [${afterGroup2Members.join(', ')}]`)
|
||
}
|
||
|
||
// 验证分组3现在为空
|
||
const afterGroup3Members = await accountGroupService.getGroupMembers(group3.id)
|
||
if (afterGroup3Members.length === 0) {
|
||
log(`✅ 分组3现在为空`, 'success')
|
||
} else {
|
||
throw new Error(`分组3应该为空,但还有成员: [${afterGroup3Members.join(', ')}]`)
|
||
}
|
||
|
||
log(`✅ 账户删除的分组清理测试通过`, 'success')
|
||
} catch (error) {
|
||
log(`❌ 测试6失败: ${error.message}`, 'error')
|
||
throw error
|
||
}
|
||
}
|
||
|
||
// 主测试函数
|
||
async function runTests() {
|
||
log('\n🚀 开始多分组功能测试\n', 'info')
|
||
|
||
try {
|
||
// 连接Redis
|
||
await redis.connect()
|
||
log('✅ Redis连接成功', 'success')
|
||
|
||
// 执行测试
|
||
await test1_createTestData()
|
||
await test2_addAccountToMultipleGroups()
|
||
await test3_verifyMultiGroupRelationships()
|
||
await test4_verifyGroupMemberships()
|
||
await test5_removeFromPartialGroups()
|
||
await test6_accountDeletionGroupCleanup()
|
||
|
||
log('\n🎉 所有测试通过!多分组功能工作正常', 'success')
|
||
} catch (error) {
|
||
log(`\n❌ 测试失败: ${error.message}`, 'error')
|
||
console.error(error)
|
||
} finally {
|
||
// 清理测试数据
|
||
if (CLEANUP_ON_FINISH) {
|
||
await cleanup()
|
||
} else {
|
||
log('\n⚠️ 测试数据未清理,请手动清理', 'warning')
|
||
}
|
||
|
||
// 关闭Redis连接
|
||
await redis.disconnect()
|
||
process.exit(0)
|
||
}
|
||
}
|
||
|
||
// 运行测试
|
||
runTests()
|