readysite / website / controllers / chat_test.go
5.6 KB
chat_test.go
package controllers

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/readysite/readysite/website/models"
)

// Test database setup is handled by testhelper_test.go

func TestChatController_NewConversationWithMessage(t *testing.T) {
	formData := url.Values{
		"content": {"Create a new blog page"},
	}

	req := httptest.NewRequest("POST", "/admin/chat/new", strings.NewReader(formData.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	w := httptest.NewRecorder()

	c := testChatController()
	handler := c.Handle(req)
	handler.(*ChatController).NewConversationWithMessage(w, req)

	// Should either redirect or render HTML
	if w.Code != http.StatusSeeOther && w.Code != http.StatusOK {
		t.Errorf("NewConversationWithMessage() status = %d, want redirect or success", w.Code)
	}
}

func TestChatController_SendMessage(t *testing.T) {
	// Create a conversation first
	conv := &models.Conversation{
		Title: "Test Conversation",
	}
	conv.ID = "test-conv-send"
	models.Conversations.Insert(conv)

	formData := url.Values{
		"content": {"Update the homepage"},
	}

	req := httptest.NewRequest("POST", "/admin/chat/test-conv-send", strings.NewReader(formData.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetPathValue("id", "test-conv-send")
	w := httptest.NewRecorder()

	c := testChatController()
	handler := c.Handle(req)
	handler.(*ChatController).SendMessage(w, req)

	// Check that a message was created
	messages, _ := models.Messages.Search("WHERE ConversationID = ?", "test-conv-send")
	if len(messages) == 0 {
		t.Error("Message should be created")
	}
}

func TestChatController_DeleteConversation(t *testing.T) {
	// Create a conversation
	conv := &models.Conversation{
		Title: "To Delete",
	}
	conv.ID = "delete-conv"
	models.Conversations.Insert(conv)

	// Add a message
	msg := &models.Message{
		ConversationID: "delete-conv",
		Role:           "user",
		Content:        "Test message",
	}
	models.Messages.Insert(msg)

	req := httptest.NewRequest("DELETE", "/admin/chat/delete-conv", nil)
	req.SetPathValue("id", "delete-conv")
	w := httptest.NewRecorder()

	c := testChatController()
	handler := c.Handle(req)
	handler.(*ChatController).Delete(w, req)

	// Verify deletion
	_, err := models.Conversations.Get("delete-conv")
	if err == nil {
		t.Error("Conversation should be deleted")
	}

	// Verify messages deleted too
	messages, _ := models.Messages.Search("WHERE ConversationID = ?", "delete-conv")
	if len(messages) > 0 {
		t.Error("Messages should be deleted with conversation")
	}
}

func TestChatController_TitleTruncation(t *testing.T) {
	longMessage := strings.Repeat("Hello world ", 20) // 240 chars

	formData := url.Values{
		"content": {longMessage},
	}

	req := httptest.NewRequest("POST", "/admin/chat/new", strings.NewReader(formData.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	w := httptest.NewRecorder()

	c := testChatController()
	handler := c.Handle(req)
	handler.(*ChatController).NewConversationWithMessage(w, req)

	// Check that conversation title is truncated
	convs, _ := models.Conversations.All()
	for _, conv := range convs {
		if len(conv.Title) > 53 { // 50 + "..."
			t.Errorf("Title should be truncated, got %d chars", len(conv.Title))
		}
	}
}

func TestChatController_EmptyMessage(t *testing.T) {
	formData := url.Values{
		"content": {""},
	}

	req := httptest.NewRequest("POST", "/admin/chat/new", strings.NewReader(formData.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	w := httptest.NewRecorder()

	c := testChatController()
	handler := c.Handle(req)
	handler.(*ChatController).NewConversationWithMessage(w, req)

	// Empty message should not create conversation (or should return error)
	// The exact behavior depends on implementation
	_ = w // Use w to avoid unused variable warning
}

func TestChatController_ConversationsList(t *testing.T) {
	// Create multiple conversations
	for i := 0; i < 5; i++ {
		conv := &models.Conversation{
			Title: "TestConv " + string(rune('A'+i)),
		}
		models.Conversations.Insert(conv)
	}

	req := httptest.NewRequest("GET", "/admin/chat", nil)

	_, c := Chat()
	handler := c.Handle(req)

	// Access template method
	cc := handler.(*ChatController)
	convs := cc.Conversations()
	if len(convs) < 5 {
		t.Errorf("Conversations() = %d items, want at least 5", len(convs))
	}
}

func TestChatController_UndoRedo(t *testing.T) {
	// Create conversation with message and mutation (use unique IDs)
	conv := &models.Conversation{
		Title:  "Undo Test",
		UserID: "test-admin-user", // Associate with test admin
	}
	conv.ID = "undo-conv-test-unique"
	models.Conversations.Insert(conv)

	msg := &models.Message{
		ConversationID: "undo-conv-test-unique",
		Role:           "assistant",
		Content:        "Created page",
		Status:         "complete",
	}
	msg.ID = "undo-msg-test-unique"
	models.Messages.Insert(msg)

	mutation := &models.Mutation{
		MessageID:   "undo-msg-test-unique",
		Action:      "create",
		EntityType:  "page",
		EntityID:    "test-page-undo",
		AfterState:  `{"id":"test-page-undo","title":"Test"}`,
		BeforeState: "",
		Undone:      false,
	}
	models.Mutations.Insert(mutation)

	// Check CanUndo - requires authentication
	c := testChatController()
	req := httptest.NewRequest("GET", "/admin/chat/undo-conv-test-unique", nil)
	req.SetPathValue("id", "undo-conv-test-unique")
	authenticateRequest(req)
	handler := c.Handle(req)
	cc := handler.(*ChatController)

	canUndo := cc.CanUndo()
	if !canUndo {
		t.Error("CanUndo() should return true when there are undoable mutations")
	}

	canRedo := cc.CanRedo()
	if canRedo {
		t.Error("CanRedo() should return false when nothing is undone")
	}
}
← Back