mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-10 21:55:42 +08:00
### What problem does this PR solve? As title ### Type of change - [x] New Feature (non-breaking change which adds functionality)
268 lines
8.1 KiB
Go
268 lines
8.1 KiB
Go
//
|
|
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
//
|
|
// 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 service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"ragflow/internal/common"
|
|
"ragflow/internal/dao"
|
|
"ragflow/internal/entity"
|
|
)
|
|
|
|
type deleteIndexDocEngine struct {
|
|
fakeChatDocEngine
|
|
deleteCalls []deleteIndexDocEngineCall
|
|
}
|
|
|
|
type deleteIndexDocEngineCall struct {
|
|
condition map[string]interface{}
|
|
indexName string
|
|
datasetID string
|
|
}
|
|
|
|
func (e *deleteIndexDocEngine) DeleteChunks(_ context.Context, condition map[string]interface{}, indexName string, datasetID string) (int64, error) {
|
|
e.deleteCalls = append(e.deleteCalls, deleteIndexDocEngineCall{
|
|
condition: condition,
|
|
indexName: indexName,
|
|
datasetID: datasetID,
|
|
})
|
|
return 1, nil
|
|
}
|
|
|
|
func testDatasetServiceForDeleteIndex(docEngine *deleteIndexDocEngine) *DatasetService {
|
|
return &DatasetService{
|
|
kbDAO: dao.NewKnowledgebaseDAO(),
|
|
taskDAO: dao.NewTaskDAO(),
|
|
docEngine: docEngine,
|
|
}
|
|
}
|
|
|
|
func insertDeleteIndexKB(t *testing.T, indexType string, taskID string) {
|
|
t.Helper()
|
|
|
|
finishAt := time.Date(2026, 6, 23, 10, 0, 0, 0, time.UTC)
|
|
kb := &entity.Knowledgebase{
|
|
ID: "kb-1",
|
|
TenantID: "user-1",
|
|
Name: "test-kb",
|
|
EmbdID: "embedding@OpenAI",
|
|
CreatedBy: "user-1",
|
|
Permission: string(entity.TenantPermissionMe),
|
|
ParserID: "naive",
|
|
ParserConfig: entity.JSONMap{},
|
|
Status: sptr("1"),
|
|
}
|
|
|
|
switch indexType {
|
|
case "graph":
|
|
kb.GraphragTaskID = &taskID
|
|
kb.GraphragTaskFinishAt = &finishAt
|
|
case "raptor":
|
|
kb.RaptorTaskID = &taskID
|
|
kb.RaptorTaskFinishAt = &finishAt
|
|
case "mindmap":
|
|
kb.MindmapTaskID = &taskID
|
|
kb.MindmapTaskFinishAt = &finishAt
|
|
}
|
|
|
|
if err := dao.DB.Create(kb).Error; err != nil {
|
|
t.Fatalf("insert kb: %v", err)
|
|
}
|
|
if taskID != "" {
|
|
if err := dao.DB.Create(&entity.Task{ID: taskID, DocID: "doc-1", TaskType: indexTypeToTaskType[indexType]}).Error; err != nil {
|
|
t.Fatalf("insert task: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDatasetServiceDeleteIndexGraphWipeFalseOnlyCancelsTask(t *testing.T) {
|
|
db := setupServiceTestDB(t)
|
|
pushServiceDB(t, db)
|
|
insertDeleteIndexKB(t, "graph", "graph-task")
|
|
|
|
docEngine := &deleteIndexDocEngine{}
|
|
code, err := testDatasetServiceForDeleteIndex(docEngine).DeleteIndex("user-1", "kb-1", "graph", false)
|
|
if err != nil {
|
|
t.Fatalf("DeleteIndex failed: %v", err)
|
|
}
|
|
if code != common.CodeSuccess {
|
|
t.Fatalf("expected success code, got %d", code)
|
|
}
|
|
if len(docEngine.deleteCalls) != 0 {
|
|
t.Fatalf("wipe=false should not delete doc-store artefacts, got %#v", docEngine.deleteCalls)
|
|
}
|
|
|
|
assertDeleteIndexTaskDeleted(t, "graph-task")
|
|
kb := getDeleteIndexKB(t)
|
|
if kb.GraphragTaskID == nil || *kb.GraphragTaskID != "" {
|
|
t.Fatalf("expected graphrag_task_id to be cleared to empty string, got %#v", kb.GraphragTaskID)
|
|
}
|
|
if kb.GraphragTaskFinishAt != nil {
|
|
t.Fatalf("expected graphrag_task_finish_at to be cleared, got %#v", kb.GraphragTaskFinishAt)
|
|
}
|
|
}
|
|
|
|
func TestDatasetServiceDeleteIndexGraphWipeTrueDeletesArtefacts(t *testing.T) {
|
|
db := setupServiceTestDB(t)
|
|
pushServiceDB(t, db)
|
|
insertDeleteIndexKB(t, "graph", "graph-task")
|
|
|
|
docEngine := &deleteIndexDocEngine{}
|
|
code, err := testDatasetServiceForDeleteIndex(docEngine).DeleteIndex("user-1", "kb-1", "graph", true)
|
|
if err != nil {
|
|
t.Fatalf("DeleteIndex failed: %v", err)
|
|
}
|
|
if code != common.CodeSuccess {
|
|
t.Fatalf("expected success code, got %d", code)
|
|
}
|
|
if len(docEngine.deleteCalls) != 1 {
|
|
t.Fatalf("expected one doc-store delete call, got %#v", docEngine.deleteCalls)
|
|
}
|
|
|
|
call := docEngine.deleteCalls[0]
|
|
if call.indexName != "ragflow_user-1" || call.datasetID != "kb-1" {
|
|
t.Fatalf("unexpected delete target: %#v", call)
|
|
}
|
|
if call.condition["kb_id"] != "kb-1" {
|
|
t.Fatalf("delete condition must include kb_id, got %#v", call.condition)
|
|
}
|
|
assertStringSet(t, call.condition["knowledge_graph_kwd"], []string{"graph", "subgraph", "entity", "relation", "community_report"})
|
|
assertDeleteIndexTaskDeleted(t, "graph-task")
|
|
}
|
|
|
|
func TestDatasetServiceDeleteIndexRaptorWipeTrueDeletesRaptorArtefacts(t *testing.T) {
|
|
db := setupServiceTestDB(t)
|
|
pushServiceDB(t, db)
|
|
insertDeleteIndexKB(t, "raptor", "raptor-task")
|
|
|
|
docEngine := &deleteIndexDocEngine{}
|
|
code, err := testDatasetServiceForDeleteIndex(docEngine).DeleteIndex("user-1", "kb-1", "raptor", true)
|
|
if err != nil {
|
|
t.Fatalf("DeleteIndex failed: %v", err)
|
|
}
|
|
if code != common.CodeSuccess {
|
|
t.Fatalf("expected success code, got %d", code)
|
|
}
|
|
if len(docEngine.deleteCalls) != 1 {
|
|
t.Fatalf("expected one doc-store delete call, got %#v", docEngine.deleteCalls)
|
|
}
|
|
call := docEngine.deleteCalls[0]
|
|
if call.condition["kb_id"] != "kb-1" {
|
|
t.Fatalf("delete condition must include kb_id, got %#v", call.condition)
|
|
}
|
|
assertStringSet(t, call.condition["raptor_kwd"], []string{"raptor"})
|
|
assertDeleteIndexTaskDeleted(t, "raptor-task")
|
|
|
|
kb := getDeleteIndexKB(t)
|
|
if kb.RaptorTaskID == nil || *kb.RaptorTaskID != "" {
|
|
t.Fatalf("expected raptor_task_id to be cleared to empty string, got %#v", kb.RaptorTaskID)
|
|
}
|
|
if kb.RaptorTaskFinishAt != nil {
|
|
t.Fatalf("expected raptor_task_finish_at to be cleared, got %#v", kb.RaptorTaskFinishAt)
|
|
}
|
|
}
|
|
|
|
func TestDatasetServiceDeleteIndexMindmapDoesNotDeleteDocStore(t *testing.T) {
|
|
db := setupServiceTestDB(t)
|
|
pushServiceDB(t, db)
|
|
insertDeleteIndexKB(t, "mindmap", "mindmap-task")
|
|
|
|
docEngine := &deleteIndexDocEngine{}
|
|
code, err := testDatasetServiceForDeleteIndex(docEngine).DeleteIndex("user-1", "kb-1", "mindmap", true)
|
|
if err != nil {
|
|
t.Fatalf("DeleteIndex failed: %v", err)
|
|
}
|
|
if code != common.CodeSuccess {
|
|
t.Fatalf("expected success code, got %d", code)
|
|
}
|
|
if len(docEngine.deleteCalls) != 0 {
|
|
t.Fatalf("mindmap delete should not delete doc-store artefacts, got %#v", docEngine.deleteCalls)
|
|
}
|
|
assertDeleteIndexTaskDeleted(t, "mindmap-task")
|
|
|
|
kb := getDeleteIndexKB(t)
|
|
if kb.MindmapTaskID == nil || *kb.MindmapTaskID != "" {
|
|
t.Fatalf("expected mindmap_task_id to be cleared to empty string, got %#v", kb.MindmapTaskID)
|
|
}
|
|
if kb.MindmapTaskFinishAt != nil {
|
|
t.Fatalf("expected mindmap_task_finish_at to be cleared, got %#v", kb.MindmapTaskFinishAt)
|
|
}
|
|
}
|
|
|
|
func TestDatasetServiceDeleteIndexRejectsInvalidType(t *testing.T) {
|
|
db := setupServiceTestDB(t)
|
|
pushServiceDB(t, db)
|
|
|
|
code, err := testDatasetServiceForDeleteIndex(&deleteIndexDocEngine{}).DeleteIndex("user-1", "kb-1", "invalid", true)
|
|
if err == nil {
|
|
t.Fatal("expected invalid index type error")
|
|
}
|
|
if code != common.CodeArgumentError {
|
|
t.Fatalf("expected argument error code, got %d", code)
|
|
}
|
|
}
|
|
|
|
func assertDeleteIndexTaskDeleted(t *testing.T, taskID string) {
|
|
t.Helper()
|
|
var task entity.Task
|
|
err := dao.DB.Where("id = ?", taskID).First(&task).Error
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
t.Fatalf("expected task %s to be deleted, got err=%v task=%#v", taskID, err, task)
|
|
}
|
|
}
|
|
|
|
func getDeleteIndexKB(t *testing.T) entity.Knowledgebase {
|
|
t.Helper()
|
|
var kb entity.Knowledgebase
|
|
if err := dao.DB.Where("id = ?", "kb-1").First(&kb).Error; err != nil {
|
|
t.Fatalf("fetch kb: %v", err)
|
|
}
|
|
return kb
|
|
}
|
|
|
|
func assertStringSet(t *testing.T, actual interface{}, expected []string) {
|
|
t.Helper()
|
|
|
|
items, ok := actual.([]interface{})
|
|
if !ok {
|
|
t.Fatalf("expected []interface{}, got %#v", actual)
|
|
}
|
|
if len(items) != len(expected) {
|
|
t.Fatalf("expected %d items, got %#v", len(expected), items)
|
|
}
|
|
|
|
seen := make(map[string]bool, len(items))
|
|
for _, item := range items {
|
|
value, ok := item.(string)
|
|
if !ok {
|
|
t.Fatalf("expected string item, got %#v", item)
|
|
}
|
|
seen[value] = true
|
|
}
|
|
for _, item := range expected {
|
|
if !seen[item] {
|
|
t.Fatalf("missing %q in %#v", item, items)
|
|
}
|
|
}
|
|
}
|