Files
m8sh/database/leveldb/chat_test.go
T
2026-06-06 19:20:38 +03:00

398 lines
9.4 KiB
Go

package leveldb
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"m8sh.su/x/m8sh/database"
)
func TestChat_Put(t *testing.T) {
db, cleanup := openDB(t)
defer cleanup()
chat := db.Chat()
user := "masha@d1.com"
t.Run("put incoming message", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Content: []byte("hello"),
Date: time.Now(),
})
require.NoError(t, err)
})
t.Run("put outgoing message", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: user,
To: "alice@d3.net",
Content: []byte("hey"),
Date: time.Now(),
})
require.NoError(t, err)
})
t.Run("put message with attachments", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Content: []byte("check this"),
Date: time.Now(),
Attachments: []database.Attachment{
{Filename: "img.png", Size: 512, MimeType: "image/png"},
},
})
require.NoError(t, err)
})
t.Run("put empty content", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Date: time.Now(),
})
require.NoError(t, err)
})
t.Run("put message with signature", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Content: []byte("signed"),
Date: time.Now(),
Signature: []byte("sig123"),
})
require.NoError(t, err)
})
t.Run("put group message", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Group: []string{user, "alice@d3.net", "carol@d4.org"},
Content: []byte("group chat"),
Date: time.Now(),
})
require.NoError(t, err)
})
}
func TestChat_Dialogues(t *testing.T) {
db, cleanup := openDB(t)
defer cleanup()
chat := db.Chat()
user := "masha@d1.com"
t.Run("empty dialogues", func(t *testing.T) {
list, err := chat.Dialogues(user, 10, time.Time{})
require.NoError(t, err)
require.Empty(t, list)
})
t.Run("single dialogue", func(t *testing.T) {
now := time.Now()
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Content: []byte("hello"),
Date: now,
})
require.NoError(t, err)
list, err := chat.Dialogues(user, 10, time.Time{})
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, "bob@d2.io", list[0].Contact)
require.Equal(t, []byte("hello"), list[0].LastContent)
require.Equal(t, 1, list[0].UnreadCount)
})
t.Run("multiple messages increment unread", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: "bob@d2.io",
To: user,
Content: []byte("second"),
Date: time.Now(),
})
require.NoError(t, err)
list, err := chat.Dialogues(user, 10, time.Time{})
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, 2, list[0].UnreadCount)
})
t.Run("filter after time", func(t *testing.T) {
past := time.Now().Add(-time.Hour)
err := chat.Put(user, database.Message{
From: "carol@d4.org",
To: user,
Content: []byte("old"),
Date: past,
})
require.NoError(t, err)
list, err := chat.Dialogues(user, 10, time.Now().Add(-time.Minute))
require.NoError(t, err)
for _, d := range list {
require.NotEqual(t, "carol@d4.org", d.Contact)
}
})
t.Run("limit respected", func(t *testing.T) {
list, err := chat.Dialogues(user, 1, time.Time{})
require.NoError(t, err)
require.Len(t, list, 1)
})
t.Run("user isolation", func(t *testing.T) {
list, err := chat.Dialogues("other@d5.io", 10, time.Time{})
require.NoError(t, err)
require.Empty(t, list)
})
}
func TestChat_Messages(t *testing.T) {
db, cleanup := openDB(t)
defer cleanup()
chat := db.Chat()
user := "masha@d1.com"
contact := "bob@d2.io"
t.Run("empty messages", func(t *testing.T) {
list, err := chat.Messages(user, contact, 10, time.Time{})
require.NoError(t, err)
require.Empty(t, list)
})
t.Run("messages for contact", func(t *testing.T) {
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("msg1"),
Date: time.Now(),
})
require.NoError(t, err)
list, err := chat.Messages(user, contact, 10, time.Time{})
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, []byte("msg1"), list[0].Content)
})
t.Run("filter after time", func(t *testing.T) {
contact := "filter-test@x.com"
now := time.Now()
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("recent"),
Date: now,
})
require.NoError(t, err)
past := now.Add(-time.Hour)
err = chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("old"),
Date: past,
})
require.NoError(t, err)
list, err := chat.Messages(user, contact, 10, now.Add(-time.Minute))
require.NoError(t, err)
require.Len(t, list, 1)
require.Equal(t, []byte("recent"), list[0].Content)
})
t.Run("limit respected", func(t *testing.T) {
for i := 0; i < 5; i++ {
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("msg"),
Date: time.Now().Add(time.Duration(i) * time.Second),
})
require.NoError(t, err)
}
list, err := chat.Messages(user, contact, 2, time.Time{})
require.NoError(t, err)
require.Len(t, list, 2)
})
t.Run("contact isolation", func(t *testing.T) {
list, err := chat.Messages(user, "unknown@d5.io", 10, time.Time{})
require.NoError(t, err)
require.Empty(t, list)
})
}
func TestChat_MarkSeen(t *testing.T) {
db, cleanup := openDB(t)
defer cleanup()
chat := db.Chat()
user := "masha@d1.com"
contact := "bob@d2.io"
now := time.Now()
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("unread"),
Date: now,
})
require.NoError(t, err)
t.Run("mark seen", func(t *testing.T) {
err := chat.MarkSeen(user, contact, now)
require.NoError(t, err)
list, err := chat.Messages(user, contact, 1, time.Time{})
require.NoError(t, err)
require.True(t, list[0].Seen)
})
t.Run("mark seen twice is idempotent", func(t *testing.T) {
err := chat.MarkSeen(user, contact, now)
require.NoError(t, err)
})
t.Run("mark seen non-existing returns ErrNotFound", func(t *testing.T) {
err := chat.MarkSeen(user, "unknown@d5.io", now)
require.ErrorIs(t, err, database.ErrNotFound)
})
}
func TestChat_Delete(t *testing.T) {
db, cleanup := openDB(t)
defer cleanup()
chat := db.Chat()
user := "masha@d1.com"
contact := "bob@d2.io"
now := time.Now()
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("delete me"),
Date: now,
})
require.NoError(t, err)
t.Run("delete existing", func(t *testing.T) {
err := chat.Delete(user, contact, now)
require.NoError(t, err)
list, err := chat.Messages(user, contact, 10, time.Time{})
require.NoError(t, err)
require.Empty(t, list)
})
t.Run("delete non-existing returns ErrNotFound", func(t *testing.T) {
err := chat.Delete(user, "unknown@d5.io", now)
require.ErrorIs(t, err, database.ErrNotFound)
})
t.Run("delete twice returns ErrNotFound", func(t *testing.T) {
err := chat.Delete(user, contact, now)
require.ErrorIs(t, err, database.ErrNotFound)
})
t.Run("delete last message updates dialogue", func(t *testing.T) {
contact := "dialog-update@test.com"
first := time.Now()
second := first.Add(time.Second)
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("first"),
Date: first,
})
require.NoError(t, err)
err = chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("second"),
Date: second,
})
require.NoError(t, err)
// Delete the last message.
err = chat.Delete(user, contact, second)
require.NoError(t, err)
// Dialogue should now show the first message.
dialogues, err := chat.Dialogues(user, 10, time.Time{})
require.NoError(t, err)
var found bool
for _, d := range dialogues {
if d.Contact == contact {
require.Equal(t, []byte("first"), d.LastContent)
found = true
}
}
require.True(t, found, "dialogue should still exist")
})
t.Run("delete only message removes dialogue", func(t *testing.T) {
contact := "remove-dialog@test.com"
msgTime := time.Now()
err := chat.Put(user, database.Message{
From: contact,
To: user,
Content: []byte("only"),
Date: msgTime,
})
require.NoError(t, err)
// Delete the only message.
err = chat.Delete(user, contact, msgTime)
require.NoError(t, err)
// Dialogue should be gone.
dialogues, err := chat.Dialogues(user, 10, time.Time{})
require.NoError(t, err)
for _, d := range dialogues {
require.NotEqual(t, contact, d.Contact)
}
})
}
func TestChat_AfterClose(t *testing.T) {
path := t.TempDir() + "/db"
db, err := New(path)
require.NoError(t, err)
chat := db.Chat()
err = chat.Put("masha@d1.com", database.Message{
From: "bob@d2.io",
To: "masha@d1.com",
Content: []byte("data"),
Date: time.Now(),
})
require.NoError(t, err)
require.NoError(t, db.Close())
require.NotPanics(t, func() {
chat.Put("masha@d1.com", database.Message{From: "x", To: "y", Date: time.Now()})
chat.Dialogues("masha@d1.com", 10, time.Time{})
chat.Messages("masha@d1.com", "bob@d2.io", 10, time.Time{})
chat.MarkSeen("masha@d1.com", "bob@d2.io", time.Now())
chat.Delete("masha@d1.com", "bob@d2.io", time.Now())
})
}