e1572b71b9
Allow platform system administrators to update builtin model parameters and credentials from the management UI while keeping secrets masked in API responses. UI edits clear YAML ownership so startup reconciliation no longer overwrites runtime overrides.
182 lines
5.7 KiB
Go
182 lines
5.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
apperrors "github.com/Tencent/WeKnora/internal/errors"
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type stubKBRepoForModelDelete struct {
|
|
count int64
|
|
}
|
|
|
|
func (s *stubKBRepoForModelDelete) CreateKnowledgeBase(context.Context, *types.KnowledgeBase) error {
|
|
return nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) GetKnowledgeBaseByID(context.Context, string) (*types.KnowledgeBase, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) GetKnowledgeBaseByIDAndTenant(context.Context, string, uint64) (*types.KnowledgeBase, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) GetKnowledgeBaseByIDs(context.Context, []string) ([]*types.KnowledgeBase, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) ListKnowledgeBases(context.Context) ([]*types.KnowledgeBase, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) ListKnowledgeBasesByTenantID(context.Context, uint64) ([]*types.KnowledgeBase, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) UpdateKnowledgeBase(context.Context, *types.KnowledgeBase) error {
|
|
return nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) DeleteKnowledgeBase(context.Context, string) error { return nil }
|
|
func (s *stubKBRepoForModelDelete) CountByVectorStoreID(context.Context, *gorm.DB, uint64, string) (int64, error) {
|
|
return 0, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) CountByModelID(context.Context, uint64, string) (int64, error) {
|
|
return s.count, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) SetUserKBPin(context.Context, uint64, string, string, bool) (*time.Time, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubKBRepoForModelDelete) ListUserKBPinIDs(context.Context, uint64, string) (map[string]time.Time, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
type stubAgentRepoForModelDelete struct {
|
|
count int64
|
|
}
|
|
|
|
func (s *stubAgentRepoForModelDelete) CreateAgent(context.Context, *types.CustomAgent) error {
|
|
return nil
|
|
}
|
|
func (s *stubAgentRepoForModelDelete) GetAgentByID(context.Context, string, uint64) (*types.CustomAgent, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubAgentRepoForModelDelete) ListAgentsByTenantID(context.Context, uint64) ([]*types.CustomAgent, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubAgentRepoForModelDelete) UpdateAgent(context.Context, *types.CustomAgent) error {
|
|
return nil
|
|
}
|
|
func (s *stubAgentRepoForModelDelete) DeleteAgent(context.Context, string, uint64) error { return nil }
|
|
func (s *stubAgentRepoForModelDelete) CountByModelID(context.Context, uint64, string) (int64, error) {
|
|
return s.count, nil
|
|
}
|
|
|
|
type stubModelRepoForDelete struct {
|
|
model *types.Model
|
|
delete func(id string) error
|
|
update func(model *types.Model) error
|
|
}
|
|
|
|
func (s *stubModelRepoForDelete) Create(context.Context, *types.Model) error { return nil }
|
|
func (s *stubModelRepoForDelete) GetByID(_ context.Context, _ uint64, id string) (*types.Model, error) {
|
|
if s.model != nil && s.model.ID == id {
|
|
return s.model, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
func (s *stubModelRepoForDelete) List(context.Context, uint64, types.ModelType, types.ModelSource) ([]*types.Model, error) {
|
|
return nil, nil
|
|
}
|
|
func (s *stubModelRepoForDelete) Update(_ context.Context, model *types.Model) error {
|
|
if s.update != nil {
|
|
return s.update(model)
|
|
}
|
|
return nil
|
|
}
|
|
func (s *stubModelRepoForDelete) Delete(_ context.Context, _ uint64, id string) error {
|
|
if s.delete != nil {
|
|
return s.delete(id)
|
|
}
|
|
return nil
|
|
}
|
|
func (s *stubModelRepoForDelete) ClearDefaultByType(context.Context, uint, types.ModelType, string) error {
|
|
return nil
|
|
}
|
|
|
|
func TestDeleteModel_RejectsWhenReferenced(t *testing.T) {
|
|
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(1))
|
|
modelID := "model-in-use"
|
|
|
|
svc := NewModelService(
|
|
&stubModelRepoForDelete{model: &types.Model{ID: modelID, TenantID: 1}},
|
|
&stubKBRepoForModelDelete{count: 1},
|
|
&stubAgentRepoForModelDelete{count: 0},
|
|
nil, nil, nil,
|
|
)
|
|
|
|
err := svc.DeleteModel(ctx, modelID)
|
|
require.Error(t, err)
|
|
appErr, ok := apperrors.IsAppError(err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, apperrors.ErrBadRequest, appErr.Code)
|
|
assert.Contains(t, appErr.Message, "knowledge base")
|
|
}
|
|
|
|
func TestDeleteModel_RejectsWhenUsedByAgent(t *testing.T) {
|
|
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(1))
|
|
modelID := "agent-model"
|
|
|
|
svc := NewModelService(
|
|
&stubModelRepoForDelete{model: &types.Model{ID: modelID, TenantID: 1}},
|
|
&stubKBRepoForModelDelete{count: 0},
|
|
&stubAgentRepoForModelDelete{count: 2},
|
|
nil, nil, nil,
|
|
)
|
|
|
|
err := svc.DeleteModel(ctx, modelID)
|
|
require.Error(t, err)
|
|
appErr, ok := apperrors.IsAppError(err)
|
|
require.True(t, ok)
|
|
assert.Contains(t, appErr.Message, "2 agent(s)")
|
|
}
|
|
|
|
func TestDeleteModel_SucceedsWhenUnreferenced(t *testing.T) {
|
|
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(1))
|
|
modelID := "free-model"
|
|
deleted := false
|
|
|
|
svc := NewModelService(
|
|
&stubModelRepoForDelete{
|
|
model: &types.Model{ID: modelID, TenantID: 1},
|
|
delete: func(id string) error {
|
|
assert.Equal(t, modelID, id)
|
|
deleted = true
|
|
return nil
|
|
},
|
|
},
|
|
&stubKBRepoForModelDelete{},
|
|
&stubAgentRepoForModelDelete{},
|
|
nil, nil, nil,
|
|
)
|
|
|
|
require.NoError(t, svc.DeleteModel(ctx, modelID))
|
|
assert.True(t, deleted)
|
|
}
|
|
|
|
func TestFormatModelInUseMessage(t *testing.T) {
|
|
t.Parallel()
|
|
assert.Equal(t,
|
|
"model is used by 1 knowledge base(s); reconfigure or remove those references before deleting",
|
|
formatModelInUseMessage(1, 0),
|
|
)
|
|
assert.Equal(t,
|
|
"model is used by 2 agent(s); reconfigure or remove those references before deleting",
|
|
formatModelInUseMessage(0, 2),
|
|
)
|
|
assert.Equal(t,
|
|
"model is used by 1 knowledge base(s) and 1 agent(s); reconfigure or remove those references before deleting",
|
|
formatModelInUseMessage(1, 1),
|
|
)
|
|
}
|