Files
jules/engine/trace/trace_test.go
T

234 lines
6.2 KiB
Go

package trace
import (
"bytes"
"context"
"encoding/json"
"errors"
"log/slog"
"testing"
"github.com/d1nch8g/jules/chat"
"github.com/d1nch8g/jules/database"
"github.com/d1nch8g/jules/engine/actions"
"github.com/d1nch8g/jules/engine/user"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func captureLog(fn func()) string {
var buf bytes.Buffer
handler := slog.NewJSONHandler(&buf, nil)
logger := slog.New(handler)
slog.SetDefault(logger)
fn()
return buf.String()
}
func TestFromMessage_Log(t *testing.T) {
msg := chat.Message{
Chat: "telegram",
ID: "123456789",
Text: "Hello, Jules!",
}
output := captureLog(func() {
FromMessage(context.Background(), msg).Info("message processed")
})
assert.Contains(t, output, "message processed")
assert.Contains(t, output, "message_processing")
assert.Contains(t, output, "telegram")
assert.Contains(t, output, "123456789")
assert.Contains(t, output, "Hello, Jules!")
assert.Contains(t, output, "duration")
}
func TestFromNotification_Log(t *testing.T) {
notif := database.Notification{
ID: uuid.New(),
UserID: uuid.New(),
InitiatorID: uuid.New(),
Content: "call mom",
}
output := captureLog(func() {
FromNotification(context.Background(), notif).Info("notification processed")
})
assert.Contains(t, output, "notification processed")
assert.Contains(t, output, "notification_processing")
assert.Contains(t, output, notif.ID.String())
assert.Contains(t, output, notif.UserID.String())
assert.Contains(t, output, notif.InitiatorID.String())
assert.Contains(t, output, "call mom")
assert.Contains(t, output, "duration")
}
func TestSpan_User_Log(t *testing.T) {
u := &user.User{
User: &database.User{
ID: uuid.New(),
Language: "ru",
Timezone: "Europe/Moscow",
PreferredChat: "telegram",
Role: "free",
},
Chats: []database.Chat{
{Platform: "telegram"},
{Platform: "whatsapp"},
},
Facts: []database.Fact{
{Value: "fact1"},
{Value: "fact2"},
},
Contacts: []database.Contact{
{Name: "Mom"},
},
IncomingNotifications: []database.Notification{
{Content: "incoming1"},
},
OutgoingNotifications: []database.Notification{
{Content: "outgoing1"},
},
RecentActions: []database.Action{
{Payload: json.RawMessage(`{"type":"reply"}`)},
},
}
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).User(u).Info("user enriched")
})
assert.Contains(t, output, "user enriched")
assert.Contains(t, output, u.ID.String())
assert.Contains(t, output, "ru")
assert.Contains(t, output, "Europe/Moscow")
assert.Contains(t, output, "free")
assert.Contains(t, output, "telegram")
assert.Contains(t, output, "whatsapp")
assert.Contains(t, output, "fact1")
assert.Contains(t, output, "fact2")
assert.Contains(t, output, "Mom")
assert.Contains(t, output, "incoming1")
assert.Contains(t, output, "outgoing1")
assert.Contains(t, output, "reply")
assert.Contains(t, output, "duration")
}
func TestSpan_User_Nil(t *testing.T) {
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).User(nil).Info("nil user")
})
assert.Contains(t, output, "nil user")
assert.NotContains(t, output, "user_lang")
}
func TestSpan_User_NilUser(t *testing.T) {
u := &user.User{User: nil}
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).User(u).Info("nil inner user")
})
assert.Contains(t, output, "nil inner user")
assert.NotContains(t, output, "user_lang")
}
func TestSpan_LLMResponse(t *testing.T) {
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
LLMResponse("first").
LLMResponse("second").
Info("llm response logged")
})
assert.Contains(t, output, "llm response logged")
assert.Contains(t, output, "llm_response")
assert.Contains(t, output, "second")
assert.NotContains(t, output, "first")
}
func TestSpan_Actions(t *testing.T) {
acts := []actions.Action{
actions.AddFact{Type: "add_fact", Value: "test fact"},
actions.Message{Type: "message", Platform: "telegram", Text: "hello"},
}
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
Actions(acts).
Info("actions logged")
})
assert.Contains(t, output, "actions logged")
assert.Contains(t, output, "add_fact")
assert.Contains(t, output, "test fact")
assert.Contains(t, output, "message")
assert.Contains(t, output, "telegram")
assert.Contains(t, output, "hello")
}
func TestSpan_Actions_ReplacesPrevious(t *testing.T) {
first := []actions.Action{actions.AddFact{Type: "add_fact", Value: "first"}}
second := []actions.Action{actions.Message{Type: "message", Platform: "telegram", Text: "second"}}
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
Actions(first).
Actions(second).
Info("actions replaced")
})
assert.Contains(t, output, "actions replaced")
assert.NotContains(t, output, "first")
assert.Contains(t, output, "second")
}
func TestSpan_Warn_WithErrors(t *testing.T) {
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
Warn("warning message", errors.New("err1"), errors.New("err2"))
})
assert.Contains(t, output, "warning message")
assert.Contains(t, output, "WARN")
assert.Contains(t, output, "err1")
assert.Contains(t, output, "err2")
assert.Contains(t, output, "duration")
}
func TestSpan_Warn_NoErrors(t *testing.T) {
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
Warn("warning without errors")
})
assert.Contains(t, output, "warning without errors")
assert.Contains(t, output, "WARN")
assert.NotContains(t, output, "\"error\"")
}
func TestSpan_Error(t *testing.T) {
output := captureLog(func() {
FromMessage(context.Background(), chat.Message{}).
Error("error message", errors.New("fatal"))
})
assert.Contains(t, output, "error message")
assert.Contains(t, output, "ERROR")
assert.Contains(t, output, "fatal")
assert.Contains(t, output, "duration")
}
func TestSpan_Log_NoContext(t *testing.T) {
output := captureLog(func() {
s := FromMessage(context.Background(), chat.Message{Chat: "telegram", ID: "123", Text: "hi"})
s.ctx = nil
s.Info("no context")
})
assert.Contains(t, output, "no context")
assert.Contains(t, output, "duration")
}