428 lines
17 KiB
Go
428 lines
17 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"m8sh.su/d/jules/chat"
|
|
"m8sh.su/d/jules/database"
|
|
)
|
|
|
|
type mockDB struct {
|
|
users *mockUsers
|
|
chats *mockChats
|
|
facts *mockFacts
|
|
contacts *mockContacts
|
|
notifications *mockNotifications
|
|
actions *mockActions
|
|
}
|
|
|
|
func (m *mockDB) Users() database.Users { return m.users }
|
|
func (m *mockDB) Chats() database.Chats { return m.chats }
|
|
func (m *mockDB) Facts() database.Facts { return m.facts }
|
|
func (m *mockDB) Contacts() database.Contacts { return m.contacts }
|
|
func (m *mockDB) Notifications() database.Notifications { return m.notifications }
|
|
func (m *mockDB) Actions() database.Actions { return m.actions }
|
|
func (m *mockDB) Close() error { return nil }
|
|
|
|
type mockUsers struct {
|
|
getFunc func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error)
|
|
createFunc func(ctx context.Context, user *database.User) error
|
|
}
|
|
|
|
func (m *mockUsers) Get(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return m.getFunc(ctx, id, lookup)
|
|
}
|
|
|
|
func (m *mockUsers) Create(ctx context.Context, user *database.User) error {
|
|
return m.createFunc(ctx, user)
|
|
}
|
|
func (m *mockUsers) Update(ctx context.Context, user *database.User) error { return nil }
|
|
func (m *mockUsers) Delete(ctx context.Context, id uuid.UUID) error { return nil }
|
|
|
|
type mockChats struct {
|
|
getUserIDFunc func(ctx context.Context, platform, identifier string) (uuid.UUID, error)
|
|
attachFunc func(ctx context.Context, userID uuid.UUID, platform, identifier string) error
|
|
listFunc func(ctx context.Context, userID uuid.UUID) ([]database.Chat, error)
|
|
}
|
|
|
|
func (m *mockChats) Attach(ctx context.Context, userID uuid.UUID, platform, identifier string) error {
|
|
return m.attachFunc(ctx, userID, platform, identifier)
|
|
}
|
|
func (m *mockChats) Detach(ctx context.Context, userID uuid.UUID, platform string) error { return nil }
|
|
func (m *mockChats) GetUserID(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return m.getUserIDFunc(ctx, platform, identifier)
|
|
}
|
|
|
|
func (m *mockChats) List(ctx context.Context, userID uuid.UUID) ([]database.Chat, error) {
|
|
return m.listFunc(ctx, userID)
|
|
}
|
|
|
|
type mockFacts struct {
|
|
listFunc func(ctx context.Context, userID uuid.UUID) ([]database.Fact, error)
|
|
}
|
|
|
|
func (m *mockFacts) Add(ctx context.Context, userID uuid.UUID, value string) error { return nil }
|
|
func (m *mockFacts) List(ctx context.Context, userID uuid.UUID) ([]database.Fact, error) {
|
|
return m.listFunc(ctx, userID)
|
|
}
|
|
func (m *mockFacts) Delete(ctx context.Context, userID uuid.UUID, value string) error { return nil }
|
|
|
|
type mockContacts struct {
|
|
listFunc func(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error)
|
|
}
|
|
|
|
func (m *mockContacts) Add(ctx context.Context, contact *database.Contact) error { return nil }
|
|
func (m *mockContacts) List(ctx context.Context, ownerID uuid.UUID) ([]database.Contact, error) {
|
|
return m.listFunc(ctx, ownerID)
|
|
}
|
|
func (m *mockContacts) Delete(ctx context.Context, ownerID, targetID uuid.UUID) error { return nil }
|
|
|
|
type mockNotifications struct {
|
|
listFunc func(ctx context.Context, userID uuid.UUID) ([]database.Notification, error)
|
|
listOutgoingFunc func(ctx context.Context, initiatorID uuid.UUID) ([]database.Notification, error)
|
|
}
|
|
|
|
func (m *mockNotifications) Push(ctx context.Context, n *database.Notification) error { return nil }
|
|
func (m *mockNotifications) Pop(ctx context.Context, limit int) ([]database.Notification, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNotifications) List(ctx context.Context, userID uuid.UUID) ([]database.Notification, error) {
|
|
return m.listFunc(ctx, userID)
|
|
}
|
|
|
|
func (m *mockNotifications) ListOutgoing(ctx context.Context, initiatorID uuid.UUID) ([]database.Notification, error) {
|
|
return m.listOutgoingFunc(ctx, initiatorID)
|
|
}
|
|
func (m *mockNotifications) Delete(ctx context.Context, id uuid.UUID) error { return nil }
|
|
|
|
type mockActions struct {
|
|
recentFunc func(ctx context.Context, userID uuid.UUID, limit int) ([]database.Action, error)
|
|
}
|
|
|
|
func (m *mockActions) Log(ctx context.Context, userID uuid.UUID, typ, content string) error {
|
|
return nil
|
|
}
|
|
|
|
func (m *mockActions) Recent(ctx context.Context, userID uuid.UUID, limit int) ([]database.Action, error) {
|
|
return m.recentFunc(ctx, userID, limit)
|
|
}
|
|
|
|
func TestFromMessage_ExistingUser(t *testing.T) {
|
|
userID := uuid.New()
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return userID, nil
|
|
},
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) {
|
|
return []database.Chat{}, nil
|
|
},
|
|
},
|
|
users: &mockUsers{
|
|
getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
},
|
|
},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }, listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }},
|
|
actions: &mockActions{recentFunc: func(ctx context.Context, id uuid.UUID, limit int) ([]database.Action, error) { return nil, nil }},
|
|
}
|
|
|
|
u, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userID, u.ID)
|
|
}
|
|
|
|
func TestFromMessage_NewUser(t *testing.T) {
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return uuid.Nil, database.ErrNotFound
|
|
},
|
|
attachFunc: func(ctx context.Context, userID uuid.UUID, platform, identifier string) error {
|
|
return nil
|
|
},
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) {
|
|
return []database.Chat{}, nil
|
|
},
|
|
},
|
|
users: &mockUsers{
|
|
createFunc: func(ctx context.Context, user *database.User) error {
|
|
return nil
|
|
},
|
|
},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }, listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }},
|
|
actions: &mockActions{recentFunc: func(ctx context.Context, id uuid.UUID, limit int) ([]database.Action, error) { return nil, nil }},
|
|
}
|
|
|
|
u, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, uuid.Nil, u.ID)
|
|
assert.Equal(t, "telegram", u.PreferredChat)
|
|
}
|
|
|
|
func TestFromMessage_ChatsGetUserIDError(t *testing.T) {
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return uuid.Nil, errors.New("db down")
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "ensure user by message")
|
|
}
|
|
|
|
func TestFromMessage_UsersGetError(t *testing.T) {
|
|
userID := uuid.New()
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return userID, nil
|
|
},
|
|
},
|
|
users: &mockUsers{
|
|
getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return nil, errors.New("db down")
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "ensure user by message")
|
|
}
|
|
|
|
func TestFromMessage_EnrichmentErrors(t *testing.T) {
|
|
userID := uuid.New()
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
tests := []struct {
|
|
name string
|
|
db *mockDB
|
|
errContains string
|
|
}{
|
|
{
|
|
name: "chats list error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) {
|
|
return nil, errors.New("chats down")
|
|
},
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
},
|
|
errContains: "list chats",
|
|
},
|
|
{
|
|
name: "facts list error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil },
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) {
|
|
return nil, errors.New("facts down")
|
|
}},
|
|
},
|
|
errContains: "list facts",
|
|
},
|
|
{
|
|
name: "contacts list error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil },
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) {
|
|
return nil, errors.New("contacts down")
|
|
}},
|
|
},
|
|
errContains: "list contacts",
|
|
},
|
|
{
|
|
name: "incoming notifications error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil },
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) {
|
|
return nil, errors.New("notifs down")
|
|
},
|
|
},
|
|
},
|
|
errContains: "list incoming notifications",
|
|
},
|
|
{
|
|
name: "outgoing notifications error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil },
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil },
|
|
listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) {
|
|
return nil, errors.New("outgoing down")
|
|
},
|
|
},
|
|
},
|
|
errContains: "list outgoing notifications",
|
|
},
|
|
{
|
|
name: "actions recent error",
|
|
db: &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) { return userID, nil },
|
|
listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil },
|
|
},
|
|
users: &mockUsers{getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: userID}, nil
|
|
}},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }, listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }},
|
|
actions: &mockActions{recentFunc: func(ctx context.Context, id uuid.UUID, limit int) ([]database.Action, error) {
|
|
return nil, errors.New("actions down")
|
|
}},
|
|
},
|
|
errContains: "get recent actions",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := FromMessage(t.Context(), tt.db, msg, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), tt.errContains)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFromNotification(t *testing.T) {
|
|
notif := database.Notification{UserID: uuid.New(), Content: "test"}
|
|
|
|
db := &mockDB{
|
|
users: &mockUsers{
|
|
getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return &database.User{ID: id}, nil
|
|
},
|
|
},
|
|
chats: &mockChats{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Chat, error) { return nil, nil }},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }, listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }},
|
|
actions: &mockActions{recentFunc: func(ctx context.Context, id uuid.UUID, limit int) ([]database.Action, error) { return nil, nil }},
|
|
}
|
|
|
|
u, err := FromNotification(t.Context(), db, notif, 10)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, notif.UserID, u.ID)
|
|
}
|
|
|
|
func TestFromNotification_UserNotFound(t *testing.T) {
|
|
notif := database.Notification{UserID: uuid.New()}
|
|
|
|
db := &mockDB{
|
|
users: &mockUsers{
|
|
getFunc: func(ctx context.Context, id uuid.UUID, lookup database.UserLookup) (*database.User, error) {
|
|
return nil, database.ErrNotFound
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := FromNotification(t.Context(), db, notif, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "get user by id")
|
|
}
|
|
|
|
func TestCreateUserAndAttachChat_CreateError(t *testing.T) {
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return uuid.Nil, database.ErrNotFound
|
|
},
|
|
},
|
|
users: &mockUsers{
|
|
createFunc: func(ctx context.Context, user *database.User) error {
|
|
return errors.New("create failed")
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "create user")
|
|
}
|
|
|
|
func TestCreateUserAndAttachChat_AttachError(t *testing.T) {
|
|
msg := chat.Message{Chat: "telegram", ID: "123", Text: "hello"}
|
|
|
|
db := &mockDB{
|
|
chats: &mockChats{
|
|
getUserIDFunc: func(ctx context.Context, platform, identifier string) (uuid.UUID, error) {
|
|
return uuid.Nil, database.ErrNotFound
|
|
},
|
|
attachFunc: func(ctx context.Context, userID uuid.UUID, platform, identifier string) error {
|
|
return errors.New("attach failed")
|
|
},
|
|
},
|
|
users: &mockUsers{
|
|
createFunc: func(ctx context.Context, user *database.User) error {
|
|
return nil
|
|
},
|
|
},
|
|
facts: &mockFacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Fact, error) { return nil, nil }},
|
|
contacts: &mockContacts{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Contact, error) { return nil, nil }},
|
|
notifications: &mockNotifications{listFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }, listOutgoingFunc: func(ctx context.Context, id uuid.UUID) ([]database.Notification, error) { return nil, nil }},
|
|
actions: &mockActions{recentFunc: func(ctx context.Context, id uuid.UUID, limit int) ([]database.Action, error) { return nil, nil }},
|
|
}
|
|
|
|
_, err := FromMessage(t.Context(), db, msg, 10)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "attach chat")
|
|
}
|