Implement chat management functionalities in Xun store
- Added CreateChat, GetChat, UpdateChat, and DeleteChat methods to manage chat sessions effectively. - Implemented validation for required fields and handling of nullable fields during chat creation and updates. - Enhanced chat retrieval with pagination and filtering options, including time-based grouping. - Introduced helper functions for converting database rows to Chat structs and grouping chats by time. - Updated message and resume models to support soft deletes, improving data management and integrity.
This commit is contained in:
parent
3c3177a171
commit
f2e0312e61
7 changed files with 1451 additions and 160 deletions
|
|
@ -1,6 +1,13 @@
|
|||
package xun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/yaoapp/xun/dbal/query"
|
||||
"github.com/yaoapp/yao/agent/store/types"
|
||||
)
|
||||
|
||||
|
|
@ -10,30 +17,436 @@ import (
|
|||
|
||||
// CreateChat creates a new chat session
|
||||
func (store *Xun) CreateChat(chat *types.Chat) error {
|
||||
// TODO: implement
|
||||
return nil
|
||||
if chat == nil {
|
||||
return fmt.Errorf("chat cannot be nil")
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if chat.AssistantID == "" {
|
||||
return fmt.Errorf("assistant_id is required")
|
||||
}
|
||||
|
||||
// Generate chat_id if not provided
|
||||
if chat.ChatID == "" {
|
||||
chat.ChatID = uuid.New().String()
|
||||
}
|
||||
|
||||
// Check if chat already exists
|
||||
exists, err := store.newQueryChat().
|
||||
Where("chat_id", chat.ChatID).
|
||||
Exists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("chat %s already exists", chat.ChatID)
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
if chat.Mode == "" {
|
||||
chat.Mode = "chat"
|
||||
}
|
||||
if chat.Status == "" {
|
||||
chat.Status = "active"
|
||||
}
|
||||
if chat.Share == "" {
|
||||
chat.Share = "private"
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
data := map[string]interface{}{
|
||||
"chat_id": chat.ChatID,
|
||||
"assistant_id": chat.AssistantID,
|
||||
"mode": chat.Mode,
|
||||
"status": chat.Status,
|
||||
"public": chat.Public,
|
||||
"share": chat.Share,
|
||||
"sort": chat.Sort,
|
||||
"created_at": time.Now(),
|
||||
"updated_at": time.Now(),
|
||||
}
|
||||
|
||||
// Handle nullable fields
|
||||
if chat.Title != "" {
|
||||
data["title"] = chat.Title
|
||||
}
|
||||
if chat.LastMessageAt != nil {
|
||||
data["last_message_at"] = *chat.LastMessageAt
|
||||
}
|
||||
if chat.Metadata != nil {
|
||||
metadataJSON, err := jsoniter.MarshalToString(chat.Metadata)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
data["metadata"] = metadataJSON
|
||||
}
|
||||
|
||||
// Insert
|
||||
return store.newQueryChat().Insert(data)
|
||||
}
|
||||
|
||||
// GetChat retrieves a single chat by ID
|
||||
func (store *Xun) GetChat(chatID string) (*types.Chat, error) {
|
||||
// TODO: implement
|
||||
return nil, nil
|
||||
if chatID == "" {
|
||||
return nil, fmt.Errorf("chat_id is required")
|
||||
}
|
||||
|
||||
row, err := store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
WhereNull("deleted_at").
|
||||
First()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if row == nil {
|
||||
return nil, fmt.Errorf("chat %s not found", chatID)
|
||||
}
|
||||
|
||||
data := row.ToMap()
|
||||
if len(data) == 0 || data["chat_id"] == nil {
|
||||
return nil, fmt.Errorf("chat %s not found", chatID)
|
||||
}
|
||||
|
||||
return store.rowToChat(data)
|
||||
}
|
||||
|
||||
// UpdateChat updates chat fields
|
||||
func (store *Xun) UpdateChat(chatID string, updates map[string]interface{}) error {
|
||||
// TODO: implement
|
||||
return nil
|
||||
if chatID == "" {
|
||||
return fmt.Errorf("chat_id is required")
|
||||
}
|
||||
if len(updates) == 0 {
|
||||
return fmt.Errorf("no fields to update")
|
||||
}
|
||||
|
||||
// Check if chat exists
|
||||
exists, err := store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
WhereNull("deleted_at").
|
||||
Exists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("chat %s not found", chatID)
|
||||
}
|
||||
|
||||
// Prepare update data
|
||||
data := make(map[string]interface{})
|
||||
|
||||
// Process each update field
|
||||
for key, value := range updates {
|
||||
// Skip system fields
|
||||
if key == "chat_id" || key == "created_at" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle metadata specially
|
||||
if key == "metadata" {
|
||||
if value != nil {
|
||||
metadataJSON, err := jsoniter.MarshalToString(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal metadata: %w", err)
|
||||
}
|
||||
data["metadata"] = metadataJSON
|
||||
} else {
|
||||
data["metadata"] = nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
data[key] = value
|
||||
}
|
||||
|
||||
// Always update updated_at
|
||||
data["updated_at"] = time.Now()
|
||||
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("no valid fields to update")
|
||||
}
|
||||
|
||||
_, err = store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
Update(data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteChat deletes a chat and its associated messages
|
||||
// DeleteChat deletes a chat and its associated messages (soft delete)
|
||||
func (store *Xun) DeleteChat(chatID string) error {
|
||||
// TODO: implement
|
||||
return nil
|
||||
if chatID == "" {
|
||||
return fmt.Errorf("chat_id is required")
|
||||
}
|
||||
|
||||
// Check if chat exists
|
||||
exists, err := store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
WhereNull("deleted_at").
|
||||
Exists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("chat %s not found", chatID)
|
||||
}
|
||||
|
||||
// Soft delete the chat
|
||||
_, err = store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
Update(map[string]interface{}{
|
||||
"deleted_at": time.Now(),
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ListChats retrieves a paginated list of chats with optional grouping
|
||||
func (store *Xun) ListChats(filter types.ChatFilter) (*types.ChatList, error) {
|
||||
// TODO: implement
|
||||
return nil, nil
|
||||
// Set defaults
|
||||
if filter.Page <= 0 {
|
||||
filter.Page = 1
|
||||
}
|
||||
if filter.PageSize <= 0 {
|
||||
filter.PageSize = 20
|
||||
}
|
||||
if filter.OrderBy == "" {
|
||||
filter.OrderBy = "last_message_at"
|
||||
}
|
||||
if filter.Order == "" {
|
||||
filter.Order = "desc"
|
||||
}
|
||||
if filter.TimeField == "" {
|
||||
filter.TimeField = "last_message_at"
|
||||
}
|
||||
|
||||
// Build base query
|
||||
qb := store.newQueryChat().WhereNull("deleted_at")
|
||||
|
||||
// Apply filters
|
||||
if filter.AssistantID != "" {
|
||||
qb.Where("assistant_id", filter.AssistantID)
|
||||
}
|
||||
if filter.Status != "" {
|
||||
qb.Where("status", filter.Status)
|
||||
}
|
||||
if filter.Keywords != "" {
|
||||
qb.Where("title", "like", fmt.Sprintf("%%%s%%", filter.Keywords))
|
||||
}
|
||||
|
||||
// Apply time range filter
|
||||
if filter.StartTime != nil {
|
||||
qb.Where(filter.TimeField, ">=", *filter.StartTime)
|
||||
}
|
||||
if filter.EndTime != nil {
|
||||
qb.Where(filter.TimeField, "<=", *filter.EndTime)
|
||||
}
|
||||
|
||||
// Apply custom query filter (for permission filtering)
|
||||
if filter.QueryFilter != nil {
|
||||
qb.Where(filter.QueryFilter)
|
||||
}
|
||||
|
||||
// Get total count
|
||||
total, err := qb.Clone().Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate pagination
|
||||
pageCount := int(math.Ceil(float64(total) / float64(filter.PageSize)))
|
||||
if pageCount < 1 {
|
||||
pageCount = 1
|
||||
}
|
||||
offset := (filter.Page - 1) * filter.PageSize
|
||||
|
||||
// Get paginated results
|
||||
rows, err := qb.OrderBy(filter.OrderBy, filter.Order).
|
||||
Offset(offset).
|
||||
Limit(filter.PageSize).
|
||||
Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert rows to Chat objects
|
||||
chats := make([]*types.Chat, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
data := row.ToMap()
|
||||
if data == nil || data["chat_id"] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
chat, err := store.rowToChat(data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
chats = append(chats, chat)
|
||||
}
|
||||
|
||||
result := &types.ChatList{
|
||||
Data: chats,
|
||||
Page: filter.Page,
|
||||
PageSize: filter.PageSize,
|
||||
PageCount: pageCount,
|
||||
Total: int(total),
|
||||
}
|
||||
|
||||
// Apply time-based grouping if requested
|
||||
if filter.GroupBy == "time" {
|
||||
result.Groups = store.groupChatsByTime(chats)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Helper Functions
|
||||
// =============================================================================
|
||||
|
||||
// rowToChat converts a database row to a Chat struct
|
||||
func (store *Xun) rowToChat(data map[string]interface{}) (*types.Chat, error) {
|
||||
chat := &types.Chat{
|
||||
ChatID: getString(data, "chat_id"),
|
||||
Title: getString(data, "title"),
|
||||
AssistantID: getString(data, "assistant_id"),
|
||||
Mode: getString(data, "mode"),
|
||||
Status: getString(data, "status"),
|
||||
Public: getBool(data, "public"),
|
||||
Share: getString(data, "share"),
|
||||
Sort: getInt(data, "sort"),
|
||||
}
|
||||
|
||||
// Handle timestamps
|
||||
if createdAt := getTime(data, "created_at"); createdAt != nil {
|
||||
chat.CreatedAt = *createdAt
|
||||
}
|
||||
if updatedAt := getTime(data, "updated_at"); updatedAt != nil {
|
||||
chat.UpdatedAt = *updatedAt
|
||||
}
|
||||
if lastMsgAt := getTime(data, "last_message_at"); lastMsgAt != nil {
|
||||
chat.LastMessageAt = lastMsgAt
|
||||
}
|
||||
|
||||
// Handle metadata
|
||||
if metadata := data["metadata"]; metadata != nil {
|
||||
if metaStr, ok := metadata.(string); ok && metaStr != "" {
|
||||
var meta map[string]interface{}
|
||||
if err := jsoniter.UnmarshalFromString(metaStr, &meta); err == nil {
|
||||
chat.Metadata = meta
|
||||
}
|
||||
} else if metaMap, ok := metadata.(map[string]interface{}); ok {
|
||||
chat.Metadata = metaMap
|
||||
}
|
||||
}
|
||||
|
||||
return chat, nil
|
||||
}
|
||||
|
||||
// groupChatsByTime groups chats by time periods
|
||||
func (store *Xun) groupChatsByTime(chats []*types.Chat) []*types.ChatGroup {
|
||||
now := time.Now()
|
||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
yesterday := today.AddDate(0, 0, -1)
|
||||
thisWeekStart := today.AddDate(0, 0, -int(today.Weekday()))
|
||||
thisMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
|
||||
groups := map[string]*types.ChatGroup{
|
||||
"today": {Key: "today", Label: "Today", Chats: []*types.Chat{}},
|
||||
"yesterday": {Key: "yesterday", Label: "Yesterday", Chats: []*types.Chat{}},
|
||||
"this_week": {Key: "this_week", Label: "This Week", Chats: []*types.Chat{}},
|
||||
"this_month": {Key: "this_month", Label: "This Month", Chats: []*types.Chat{}},
|
||||
"earlier": {Key: "earlier", Label: "Earlier", Chats: []*types.Chat{}},
|
||||
}
|
||||
|
||||
for _, chat := range chats {
|
||||
// Use last_message_at if available, otherwise created_at
|
||||
var chatTime time.Time
|
||||
if chat.LastMessageAt != nil {
|
||||
chatTime = *chat.LastMessageAt
|
||||
} else {
|
||||
chatTime = chat.CreatedAt
|
||||
}
|
||||
|
||||
chatDate := time.Date(chatTime.Year(), chatTime.Month(), chatTime.Day(), 0, 0, 0, 0, chatTime.Location())
|
||||
|
||||
switch {
|
||||
case chatDate.Equal(today) || chatDate.After(today):
|
||||
groups["today"].Chats = append(groups["today"].Chats, chat)
|
||||
case chatDate.Equal(yesterday):
|
||||
groups["yesterday"].Chats = append(groups["yesterday"].Chats, chat)
|
||||
case chatDate.After(thisWeekStart) || chatDate.Equal(thisWeekStart):
|
||||
groups["this_week"].Chats = append(groups["this_week"].Chats, chat)
|
||||
case chatDate.After(thisMonthStart) || chatDate.Equal(thisMonthStart):
|
||||
groups["this_month"].Chats = append(groups["this_month"].Chats, chat)
|
||||
default:
|
||||
groups["earlier"].Chats = append(groups["earlier"].Chats, chat)
|
||||
}
|
||||
}
|
||||
|
||||
// Update counts and filter empty groups
|
||||
result := make([]*types.ChatGroup, 0)
|
||||
for _, key := range []string{"today", "yesterday", "this_week", "this_month", "earlier"} {
|
||||
group := groups[key]
|
||||
group.Count = len(group.Chats)
|
||||
if group.Count > 0 {
|
||||
result = append(result, group)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// getTime helper function to convert database value to time.Time pointer
|
||||
func getTime(data map[string]interface{}, key string) *time.Time {
|
||||
if v := data[key]; v != nil {
|
||||
switch t := v.(type) {
|
||||
case time.Time:
|
||||
return &t
|
||||
case *time.Time:
|
||||
return t
|
||||
case string:
|
||||
// Try parsing various formats
|
||||
formats := []string{
|
||||
time.RFC3339,
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02 15:04:05.999999-07:00",
|
||||
"2006-01-02T15:04:05Z",
|
||||
}
|
||||
for _, format := range formats {
|
||||
if parsed, err := time.Parse(format, t); err == nil {
|
||||
return &parsed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateChatLastMessageAt updates the last_message_at timestamp for a chat
|
||||
func (store *Xun) UpdateChatLastMessageAt(chatID string, timestamp time.Time) error {
|
||||
if chatID == "" {
|
||||
return fmt.Errorf("chat_id is required")
|
||||
}
|
||||
|
||||
_, err := store.newQueryChat().
|
||||
Where("chat_id", chatID).
|
||||
Update(map[string]interface{}{
|
||||
"last_message_at": timestamp,
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// newQueryChatWithPermission creates a new query builder with permission filtering
|
||||
func (store *Xun) newQueryChatWithPermission(filter types.ChatFilter) query.Query {
|
||||
qb := store.newQueryChat().WhereNull("deleted_at")
|
||||
|
||||
if filter.QueryFilter != nil {
|
||||
qb.Where(filter.QueryFilter)
|
||||
}
|
||||
|
||||
return qb
|
||||
}
|
||||
|
|
|
|||
881
agent/store/xun/chat_test.go
Normal file
881
agent/store/xun/chat_test.go
Normal file
|
|
@ -0,0 +1,881 @@
|
|||
package xun_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/yaoapp/xun/dbal/query"
|
||||
"github.com/yaoapp/yao/agent/store/types"
|
||||
"github.com/yaoapp/yao/agent/store/xun"
|
||||
"github.com/yaoapp/yao/config"
|
||||
"github.com/yaoapp/yao/test"
|
||||
)
|
||||
|
||||
// TestCreateChat tests creating chat sessions
|
||||
func TestCreateChat(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
t.Run("CreateNewChat", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: "Test Chat",
|
||||
Mode: "chat",
|
||||
Status: "active",
|
||||
Share: "private",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
if chat.ChatID == "" {
|
||||
t.Error("Expected chat_id to be generated")
|
||||
}
|
||||
|
||||
t.Logf("Created chat with ID: %s", chat.ChatID)
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("CreateChatWithAllFields", func(t *testing.T) {
|
||||
now := time.Now()
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: "Full Chat",
|
||||
Mode: "task",
|
||||
Status: "active",
|
||||
Public: true,
|
||||
Share: "team",
|
||||
Sort: 100,
|
||||
LastMessageAt: &now,
|
||||
Metadata: map[string]interface{}{
|
||||
"source": "test",
|
||||
"tags": []string{"test", "chat"},
|
||||
},
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve and verify
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.Title != "Full Chat" {
|
||||
t.Errorf("Expected title 'Full Chat', got '%s'", retrieved.Title)
|
||||
}
|
||||
if retrieved.Mode != "task" {
|
||||
t.Errorf("Expected mode 'task', got '%s'", retrieved.Mode)
|
||||
}
|
||||
if !retrieved.Public {
|
||||
t.Error("Expected public to be true")
|
||||
}
|
||||
if retrieved.Share != "team" {
|
||||
t.Errorf("Expected share 'team', got '%s'", retrieved.Share)
|
||||
}
|
||||
if retrieved.Sort != 100 {
|
||||
t.Errorf("Expected sort 100, got %d", retrieved.Sort)
|
||||
}
|
||||
if retrieved.Metadata == nil {
|
||||
t.Error("Expected metadata to be set")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("CreateChatWithCustomID", func(t *testing.T) {
|
||||
customID := fmt.Sprintf("custom_chat_%d", time.Now().UnixNano())
|
||||
chat := &types.Chat{
|
||||
ChatID: customID,
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
if chat.ChatID != customID {
|
||||
t.Errorf("Expected chat_id '%s', got '%s'", customID, chat.ChatID)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("CreateDuplicateChatFails", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create first chat: %v", err)
|
||||
}
|
||||
|
||||
// Try to create with same ID
|
||||
duplicateChat := &types.Chat{
|
||||
ChatID: chat.ChatID,
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
|
||||
err = store.CreateChat(duplicateChat)
|
||||
if err == nil {
|
||||
t.Error("Expected error when creating duplicate chat")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("CreateChatWithoutAssistantIDFails", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
Title: "No Assistant",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err == nil {
|
||||
t.Error("Expected error when creating chat without assistant_id")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CreateNilChatFails", func(t *testing.T) {
|
||||
err := store.CreateChat(nil)
|
||||
if err == nil {
|
||||
t.Error("Expected error when creating nil chat")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CreateChatWithDefaults", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve and verify defaults
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.Mode != "chat" {
|
||||
t.Errorf("Expected default mode 'chat', got '%s'", retrieved.Mode)
|
||||
}
|
||||
if retrieved.Status != "active" {
|
||||
t.Errorf("Expected default status 'active', got '%s'", retrieved.Status)
|
||||
}
|
||||
if retrieved.Share != "private" {
|
||||
t.Errorf("Expected default share 'private', got '%s'", retrieved.Share)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
}
|
||||
|
||||
// TestGetChat tests retrieving chat sessions
|
||||
func TestGetChat(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
t.Run("GetExistingChat", func(t *testing.T) {
|
||||
// Create chat first
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: "Get Test Chat",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
// Get it
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.ChatID != chat.ChatID {
|
||||
t.Errorf("Expected chat_id '%s', got '%s'", chat.ChatID, retrieved.ChatID)
|
||||
}
|
||||
if retrieved.Title != "Get Test Chat" {
|
||||
t.Errorf("Expected title 'Get Test Chat', got '%s'", retrieved.Title)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("GetNonExistentChat", func(t *testing.T) {
|
||||
_, err := store.GetChat("nonexistent_chat_id")
|
||||
if err == nil {
|
||||
t.Error("Expected error when getting non-existent chat")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetChatWithEmptyID", func(t *testing.T) {
|
||||
_, err := store.GetChat("")
|
||||
if err == nil {
|
||||
t.Error("Expected error when getting chat with empty ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetDeletedChatFails", func(t *testing.T) {
|
||||
// Create and delete chat
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.DeleteChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to delete chat: %v", err)
|
||||
}
|
||||
|
||||
// Try to get deleted chat
|
||||
_, err = store.GetChat(chat.ChatID)
|
||||
if err == nil {
|
||||
t.Error("Expected error when getting deleted chat")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestUpdateChat tests updating chat sessions
|
||||
func TestUpdateChat(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
t.Run("UpdateTitle", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: "Original Title",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{
|
||||
"title": "Updated Title",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update chat: %v", err)
|
||||
}
|
||||
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.Title != "Updated Title" {
|
||||
t.Errorf("Expected title 'Updated Title', got '%s'", retrieved.Title)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("UpdateMultipleFields", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: "Original",
|
||||
Status: "active",
|
||||
Share: "private",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{
|
||||
"title": "Updated",
|
||||
"status": "archived",
|
||||
"share": "team",
|
||||
"public": true,
|
||||
"sort": 50,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update chat: %v", err)
|
||||
}
|
||||
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.Title != "Updated" {
|
||||
t.Errorf("Expected title 'Updated', got '%s'", retrieved.Title)
|
||||
}
|
||||
if retrieved.Status != "archived" {
|
||||
t.Errorf("Expected status 'archived', got '%s'", retrieved.Status)
|
||||
}
|
||||
if retrieved.Share != "team" {
|
||||
t.Errorf("Expected share 'team', got '%s'", retrieved.Share)
|
||||
}
|
||||
if !retrieved.Public {
|
||||
t.Error("Expected public to be true")
|
||||
}
|
||||
if retrieved.Sort != 50 {
|
||||
t.Errorf("Expected sort 50, got %d", retrieved.Sort)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("UpdateMetadata", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"key1": "value1",
|
||||
"key2": 123,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update metadata: %v", err)
|
||||
}
|
||||
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.Metadata == nil {
|
||||
t.Fatal("Expected metadata to be set")
|
||||
}
|
||||
if retrieved.Metadata["key1"] != "value1" {
|
||||
t.Errorf("Expected metadata key1 'value1', got '%v'", retrieved.Metadata["key1"])
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("UpdateNonExistentChatFails", func(t *testing.T) {
|
||||
err := store.UpdateChat("nonexistent_chat", map[string]interface{}{
|
||||
"title": "Test",
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected error when updating non-existent chat")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateWithEmptyIDFails", func(t *testing.T) {
|
||||
err := store.UpdateChat("", map[string]interface{}{
|
||||
"title": "Test",
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Expected error when updating with empty ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UpdateWithEmptyFieldsFails", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{})
|
||||
if err == nil {
|
||||
t.Error("Expected error when updating with empty fields")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
|
||||
t.Run("UpdateSkipsSystemFields", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
originalID := chat.ChatID
|
||||
|
||||
// Try to update system fields
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{
|
||||
"chat_id": "new_id",
|
||||
"title": "Valid Update",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update chat: %v", err)
|
||||
}
|
||||
|
||||
// Verify chat_id unchanged
|
||||
retrieved, err := store.GetChat(originalID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve chat: %v", err)
|
||||
}
|
||||
|
||||
if retrieved.ChatID != originalID {
|
||||
t.Errorf("Expected chat_id to remain '%s', got '%s'", originalID, retrieved.ChatID)
|
||||
}
|
||||
if retrieved.Title != "Valid Update" {
|
||||
t.Errorf("Expected title 'Valid Update', got '%s'", retrieved.Title)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
_ = store.DeleteChat(chat.ChatID)
|
||||
})
|
||||
}
|
||||
|
||||
// TestDeleteChat tests deleting chat sessions
|
||||
func TestDeleteChat(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
t.Run("DeleteExistingChat", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
err = store.DeleteChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to delete chat: %v", err)
|
||||
}
|
||||
|
||||
// Verify deleted
|
||||
_, err = store.GetChat(chat.ChatID)
|
||||
if err == nil {
|
||||
t.Error("Expected error when getting deleted chat")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DeleteNonExistentChatFails", func(t *testing.T) {
|
||||
err := store.DeleteChat("nonexistent_chat")
|
||||
if err == nil {
|
||||
t.Error("Expected error when deleting non-existent chat")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DeleteWithEmptyIDFails", func(t *testing.T) {
|
||||
err := store.DeleteChat("")
|
||||
if err == nil {
|
||||
t.Error("Expected error when deleting with empty ID")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DeleteAlreadyDeletedChatFails", func(t *testing.T) {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
|
||||
// Delete first time
|
||||
err = store.DeleteChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to delete chat: %v", err)
|
||||
}
|
||||
|
||||
// Try to delete again
|
||||
err = store.DeleteChat(chat.ChatID)
|
||||
if err == nil {
|
||||
t.Error("Expected error when deleting already deleted chat")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestListChats tests listing chat sessions
|
||||
func TestListChats(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
// Create test chats
|
||||
chatIDs := []string{}
|
||||
for i := 0; i < 5; i++ {
|
||||
chat := &types.Chat{
|
||||
AssistantID: "test_assistant",
|
||||
Title: fmt.Sprintf("Chat %d", i),
|
||||
Status: "active",
|
||||
}
|
||||
if i >= 3 {
|
||||
chat.Status = "archived"
|
||||
}
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
chatIDs = append(chatIDs, chat.ChatID)
|
||||
|
||||
// Add small delay to ensure different timestamps
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Clean up at the end
|
||||
defer func() {
|
||||
for _, id := range chatIDs {
|
||||
_ = store.DeleteChat(id)
|
||||
}
|
||||
}()
|
||||
|
||||
t.Run("ListAllChats", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats: %v", err)
|
||||
}
|
||||
|
||||
if len(result.Data) < 5 {
|
||||
t.Errorf("Expected at least 5 chats, got %d", len(result.Data))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsByStatus", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
Status: "active",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats: %v", err)
|
||||
}
|
||||
|
||||
for _, chat := range result.Data {
|
||||
if chat.Status != "active" {
|
||||
t.Errorf("Expected status 'active', got '%s'", chat.Status)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsByAssistant", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
AssistantID: "test_assistant",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats: %v", err)
|
||||
}
|
||||
|
||||
for _, chat := range result.Data {
|
||||
if chat.AssistantID != "test_assistant" {
|
||||
t.Errorf("Expected assistant_id 'test_assistant', got '%s'", chat.AssistantID)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsByKeywords", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
Keywords: "Chat 1",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats: %v", err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, chat := range result.Data {
|
||||
if chat.Title == "Chat 1" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("Expected to find chat with title 'Chat 1'")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsPagination", func(t *testing.T) {
|
||||
// First page
|
||||
result1, err := store.ListChats(types.ChatFilter{
|
||||
Page: 1,
|
||||
PageSize: 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list first page: %v", err)
|
||||
}
|
||||
|
||||
if len(result1.Data) > 2 {
|
||||
t.Errorf("Expected max 2 chats, got %d", len(result1.Data))
|
||||
}
|
||||
if result1.Page != 1 {
|
||||
t.Errorf("Expected page 1, got %d", result1.Page)
|
||||
}
|
||||
if result1.PageSize != 2 {
|
||||
t.Errorf("Expected pagesize 2, got %d", result1.PageSize)
|
||||
}
|
||||
|
||||
// Second page
|
||||
if result1.Total > 2 {
|
||||
result2, err := store.ListChats(types.ChatFilter{
|
||||
Page: 2,
|
||||
PageSize: 2,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list second page: %v", err)
|
||||
}
|
||||
if result2.Page != 2 {
|
||||
t.Errorf("Expected page 2, got %d", result2.Page)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsWithGrouping", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
GroupBy: "time",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats with grouping: %v", err)
|
||||
}
|
||||
|
||||
// Should have groups when GroupBy is "time"
|
||||
if result.Groups == nil {
|
||||
t.Error("Expected groups to be set when GroupBy='time'")
|
||||
}
|
||||
|
||||
// Verify group structure
|
||||
for _, group := range result.Groups {
|
||||
if group.Key == "" {
|
||||
t.Error("Expected group key to be set")
|
||||
}
|
||||
if group.Label == "" {
|
||||
t.Error("Expected group label to be set")
|
||||
}
|
||||
if group.Count != len(group.Chats) {
|
||||
t.Errorf("Expected count %d to match chats length %d", group.Count, len(group.Chats))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsWithTimeRange", func(t *testing.T) {
|
||||
now := time.Now()
|
||||
yesterday := now.AddDate(0, 0, -1)
|
||||
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
StartTime: &yesterday,
|
||||
EndTime: &now,
|
||||
TimeField: "created_at",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats with time range: %v", err)
|
||||
}
|
||||
|
||||
// Should return chats created within the time range
|
||||
t.Logf("Found %d chats in time range", len(result.Data))
|
||||
})
|
||||
|
||||
t.Run("ListChatsWithSorting", func(t *testing.T) {
|
||||
// Ascending order
|
||||
resultAsc, err := store.ListChats(types.ChatFilter{
|
||||
OrderBy: "created_at",
|
||||
Order: "asc",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats ascending: %v", err)
|
||||
}
|
||||
|
||||
// Descending order
|
||||
resultDesc, err := store.ListChats(types.ChatFilter{
|
||||
OrderBy: "created_at",
|
||||
Order: "desc",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats descending: %v", err)
|
||||
}
|
||||
|
||||
// Verify different order
|
||||
if len(resultAsc.Data) > 1 && len(resultDesc.Data) > 1 {
|
||||
if resultAsc.Data[0].ChatID == resultDesc.Data[0].ChatID {
|
||||
// This is fine if there's only one chat, but otherwise order should differ
|
||||
if len(resultAsc.Data) > 1 {
|
||||
t.Logf("First chat in asc: %s, first in desc: %s", resultAsc.Data[0].ChatID, resultDesc.Data[0].ChatID)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ListChatsWithQueryFilter", func(t *testing.T) {
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
QueryFilter: func(qb query.Query) {
|
||||
qb.Where("status", "active")
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats with query filter: %v", err)
|
||||
}
|
||||
|
||||
for _, chat := range result.Data {
|
||||
if chat.Status != "active" {
|
||||
t.Errorf("Expected status 'active', got '%s'", chat.Status)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestChatCompleteWorkflow tests a complete chat workflow
|
||||
func TestChatCompleteWorkflow(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
store, err := xun.NewXun(types.Setting{
|
||||
Connector: "default",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
t.Run("CompleteWorkflow", func(t *testing.T) {
|
||||
// 1. Create chat
|
||||
chat := &types.Chat{
|
||||
AssistantID: "workflow_assistant",
|
||||
Title: "Workflow Test Chat",
|
||||
Mode: "chat",
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
err := store.CreateChat(chat)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chat: %v", err)
|
||||
}
|
||||
t.Logf("Created chat: %s", chat.ChatID)
|
||||
|
||||
// 2. Get chat
|
||||
retrieved, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get chat: %v", err)
|
||||
}
|
||||
if retrieved.Title != "Workflow Test Chat" {
|
||||
t.Errorf("Expected title 'Workflow Test Chat', got '%s'", retrieved.Title)
|
||||
}
|
||||
|
||||
// 3. Update chat
|
||||
err = store.UpdateChat(chat.ChatID, map[string]interface{}{
|
||||
"title": "Updated Workflow Chat",
|
||||
"status": "archived",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update chat: %v", err)
|
||||
}
|
||||
|
||||
// 4. Verify update
|
||||
updated, err := store.GetChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get updated chat: %v", err)
|
||||
}
|
||||
if updated.Title != "Updated Workflow Chat" {
|
||||
t.Errorf("Expected title 'Updated Workflow Chat', got '%s'", updated.Title)
|
||||
}
|
||||
if updated.Status != "archived" {
|
||||
t.Errorf("Expected status 'archived', got '%s'", updated.Status)
|
||||
}
|
||||
|
||||
// 5. List chats
|
||||
result, err := store.ListChats(types.ChatFilter{
|
||||
AssistantID: "workflow_assistant",
|
||||
Page: 1,
|
||||
PageSize: 20,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to list chats: %v", err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, c := range result.Data {
|
||||
if c.ChatID == chat.ChatID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("Expected to find chat in list")
|
||||
}
|
||||
|
||||
// 6. Delete chat
|
||||
err = store.DeleteChat(chat.ChatID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to delete chat: %v", err)
|
||||
}
|
||||
|
||||
// 7. Verify deletion
|
||||
_, err = store.GetChat(chat.ChatID)
|
||||
if err == nil {
|
||||
t.Error("Expected error when getting deleted chat")
|
||||
}
|
||||
|
||||
t.Log("Complete workflow passed!")
|
||||
})
|
||||
}
|
||||
|
|
@ -33,4 +33,3 @@ func (store *Xun) DeleteMessages(chatID string, messageIDs []string) error {
|
|||
// TODO: implement
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,4 +46,3 @@ func (store *Xun) DeleteResume(chatID string) error {
|
|||
// TODO: implement
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
288
data/bindata.go
288
data/bindata.go
File diff suppressed because it is too large
Load diff
|
|
@ -129,6 +129,5 @@
|
|||
"comment": "Index for message ordering within chat"
|
||||
}
|
||||
],
|
||||
"option": { "timestamps": true, "soft_deletes": false }
|
||||
"option": { "timestamps": true, "soft_deletes": true }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,5 +166,5 @@
|
|||
"comment": "Index for resume ordering within request"
|
||||
}
|
||||
],
|
||||
"option": { "timestamps": true, "soft_deletes": false }
|
||||
"option": { "timestamps": true, "soft_deletes": true }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue