test: increase DM and wiretap coverage
This commit is contained in:
parent
3b00967219
commit
1e66b7d698
@ -17,6 +17,7 @@ All notable changes to `discrawl` will be documented in this file.
|
||||
### Tests
|
||||
|
||||
- Added regression coverage for DM channel-name inference from cached profile data when Discord Desktop cache lacks explicit channel recipient metadata.
|
||||
- Added coverage for local DM conversation listing/filtering, DM cleanup paths, and Discord Desktop import helper edge cases.
|
||||
|
||||
## 0.5.1 - 2026-04-24
|
||||
|
||||
|
||||
@ -1680,6 +1680,17 @@ func TestRunMentionsValidation(t *testing.T) {
|
||||
rt := &runtime{stderr: &bytes.Buffer{}}
|
||||
rt.now = func() time.Time { return time.Date(2026, 3, 8, 12, 0, 0, 0, time.UTC) }
|
||||
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"extra"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--hours", "-1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--days", "-1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--hours", "1", "--days", "1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--hours", "1", "--since", "2026-03-01T00:00:00Z"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--limit", "-1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--last", "-1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--all", "--last", "1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--limit", "1", "--last", "1"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--since", "bad"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runDirectMessages([]string{"--before", "bad"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runMessages([]string{"--hours", "-1", "--channel", "general"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runMessages([]string{"--hours", "1", "--days", "1", "--channel", "general"})))
|
||||
require.Equal(t, 2, ExitCode(rt.runMessages([]string{"--hours", "1", "--since", "2026-03-01T00:00:00Z", "--channel", "general"})))
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -14,6 +15,42 @@ import (
|
||||
"github.com/steipete/discrawl/internal/store"
|
||||
)
|
||||
|
||||
func TestDesktopPathAndImportHelpers(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
t.Setenv("USERPROFILE", home)
|
||||
require.Equal(t, filepath.Join(home, "AppData", "Roaming", "discord"), DefaultPath())
|
||||
case "darwin":
|
||||
t.Setenv("HOME", home)
|
||||
require.Equal(t, filepath.Join(home, "Library", "Application Support", "discord"), DefaultPath())
|
||||
default:
|
||||
xdg := filepath.Join(home, "xdg")
|
||||
t.Setenv("XDG_CONFIG_HOME", xdg)
|
||||
require.Equal(t, filepath.Join(xdg, "discord"), DefaultPath())
|
||||
}
|
||||
|
||||
require.Equal(t, "dm", kindForChannelType(1, true))
|
||||
require.Equal(t, "group_dm", kindForChannelType(3, true))
|
||||
require.Equal(t, "text", kindForChannelType(0, false))
|
||||
require.Equal(t, "announcement", kindForChannelType(5, false))
|
||||
require.Equal(t, "thread_announcement", kindForChannelType(10, false))
|
||||
require.Equal(t, "thread_public", kindForChannelType(11, false))
|
||||
require.Equal(t, "thread_private", kindForChannelType(12, false))
|
||||
require.Equal(t, "forum", kindForChannelType(15, false))
|
||||
require.Equal(t, "desktop", kindForChannelType(99, false))
|
||||
embedParts := embedText(map[string]any{"embeds": []any{
|
||||
map[string]any{"title": " title ", "description": "body"},
|
||||
}})
|
||||
require.Equal(t, []string{"title", "body"}, embedParts)
|
||||
require.Equal(t, time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), snowflakeTime("0"))
|
||||
require.True(t, snowflakeTime("not-a-snowflake").IsZero())
|
||||
require.Equal(t, time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC), parseDiscordTime("2026-04-24T12:00:00Z"))
|
||||
require.True(t, parseDiscordTime("bad").IsZero())
|
||||
require.Empty(t, formatOptionalTime(time.Time{}))
|
||||
require.NotEmpty(t, formatOptionalTime(time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC)))
|
||||
}
|
||||
|
||||
func TestImportExtractsDirectMessageFromDesktopCache(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dir := t.TempDir()
|
||||
|
||||
222
internal/store/direct_messages_test.go
Normal file
222
internal/store/direct_messages_test.go
Normal file
@ -0,0 +1,222 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDirectMessageConversations(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
s, err := Open(ctx, filepath.Join(t.TempDir(), "discrawl.db"))
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
base := time.Date(2026, 4, 24, 12, 0, 0, 0, time.UTC)
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "dm1", GuildID: DirectMessageGuildID, Kind: "dm", Name: "Vincent K", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "dm2", GuildID: DirectMessageGuildID, Kind: "dm", Name: "Alice", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "dm-empty", GuildID: DirectMessageGuildID, Kind: "dm", Name: "Empty", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "c1", GuildID: "g1", Kind: "text", Name: "general", RawJSON: `{}`}))
|
||||
for _, message := range []MessageRecord{
|
||||
{
|
||||
ID: "m1",
|
||||
GuildID: DirectMessageGuildID,
|
||||
ChannelID: "dm1",
|
||||
ChannelName: "Vincent K",
|
||||
AuthorID: "u1",
|
||||
AuthorName: "Vincent K",
|
||||
CreatedAt: base.Format(time.RFC3339Nano),
|
||||
Content: "hello",
|
||||
NormalizedContent: "hello",
|
||||
RawJSON: `{"author":{"username":"vincentkoc","global_name":"Vincent K"}}`,
|
||||
},
|
||||
{
|
||||
ID: "m2",
|
||||
GuildID: DirectMessageGuildID,
|
||||
ChannelID: "dm1",
|
||||
ChannelName: "Vincent K",
|
||||
AuthorID: "self",
|
||||
AuthorName: "Peter",
|
||||
CreatedAt: base.Add(time.Minute).Format(time.RFC3339Nano),
|
||||
Content: "reply",
|
||||
NormalizedContent: "reply",
|
||||
RawJSON: `{"author":{"username":"steipete","global_name":"Peter"}}`,
|
||||
},
|
||||
{
|
||||
ID: "m3",
|
||||
GuildID: DirectMessageGuildID,
|
||||
ChannelID: "dm2",
|
||||
ChannelName: "Alice",
|
||||
AuthorID: "u2",
|
||||
AuthorName: "Alice",
|
||||
CreatedAt: base.Add(2 * time.Minute).Format(time.RFC3339Nano),
|
||||
Content: "newer",
|
||||
NormalizedContent: "newer",
|
||||
RawJSON: `{"author":{"username":"alice","global_name":"Alice Example"}}`,
|
||||
},
|
||||
{
|
||||
ID: "m4",
|
||||
GuildID: "g1",
|
||||
ChannelID: "c1",
|
||||
ChannelName: "general",
|
||||
AuthorID: "u2",
|
||||
AuthorName: "Alice",
|
||||
CreatedAt: base.Add(3 * time.Minute).Format(time.RFC3339Nano),
|
||||
Content: "guild message",
|
||||
NormalizedContent: "guild message",
|
||||
RawJSON: `{"author":{"username":"alice"}}`,
|
||||
},
|
||||
} {
|
||||
require.NoError(t, s.UpsertMessage(ctx, message))
|
||||
}
|
||||
|
||||
rows, err := s.DirectMessageConversations(ctx, DirectMessageConversationOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 3)
|
||||
require.Equal(t, "dm2", rows[0].ChannelID)
|
||||
require.Equal(t, "Alice", rows[0].Name)
|
||||
require.Equal(t, 1, rows[0].MessageCount)
|
||||
require.Equal(t, 1, rows[0].AuthorCount)
|
||||
require.Equal(t, base.Add(2*time.Minute), rows[0].FirstMessageAt)
|
||||
require.Equal(t, base.Add(2*time.Minute), rows[0].LastMessageAt)
|
||||
require.Equal(t, "dm1", rows[1].ChannelID)
|
||||
require.Equal(t, 2, rows[1].MessageCount)
|
||||
require.Equal(t, 2, rows[1].AuthorCount)
|
||||
require.Equal(t, "dm-empty", rows[2].ChannelID)
|
||||
require.Zero(t, rows[2].MessageCount)
|
||||
require.True(t, rows[2].FirstMessageAt.IsZero())
|
||||
require.True(t, rows[2].LastMessageAt.IsZero())
|
||||
|
||||
rows, err = s.DirectMessageConversations(ctx, DirectMessageConversationOptions{With: "vincent"})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
require.Equal(t, "dm1", rows[0].ChannelID)
|
||||
|
||||
rows, err = s.DirectMessageConversations(ctx, DirectMessageConversationOptions{With: "Alice Example"})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
require.Equal(t, "dm2", rows[0].ChannelID)
|
||||
|
||||
rows, err = s.DirectMessageConversations(ctx, DirectMessageConversationOptions{With: "u1"})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
require.Equal(t, "dm1", rows[0].ChannelID)
|
||||
|
||||
rows, err = s.DirectMessageConversations(ctx, DirectMessageConversationOptions{Limit: 1})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
require.Equal(t, "dm2", rows[0].ChannelID)
|
||||
}
|
||||
|
||||
func TestDeleteGuildDataAndOrphanChannels(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
s, err := Open(ctx, filepath.Join(t.TempDir(), "discrawl.db"))
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
now := time.Date(2026, 4, 24, 13, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)
|
||||
require.NoError(t, s.UpsertGuild(ctx, GuildRecord{ID: "g1", Name: "Delete Me", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertGuild(ctx, GuildRecord{ID: "g2", Name: "Keep Me", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "c1", GuildID: "g1", Kind: "text", Name: "general", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "c2", GuildID: "g2", Kind: "text", Name: "general", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertMember(ctx, MemberRecord{GuildID: "g1", UserID: "u1", Username: "gone", RoleIDsJSON: `[]`, RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertMember(ctx, MemberRecord{GuildID: "g2", UserID: "u2", Username: "kept", RoleIDsJSON: `[]`, RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertMessages(ctx, []MessageMutation{
|
||||
{
|
||||
Record: MessageRecord{
|
||||
ID: "m1",
|
||||
GuildID: "g1",
|
||||
ChannelID: "c1",
|
||||
ChannelName: "general",
|
||||
AuthorID: "u1",
|
||||
AuthorName: "Gone",
|
||||
CreatedAt: now,
|
||||
Content: "remove me",
|
||||
NormalizedContent: "remove me",
|
||||
HasAttachments: true,
|
||||
RawJSON: `{"author":{"username":"gone"}}`,
|
||||
},
|
||||
EventType: "wiretap",
|
||||
PayloadJSON: `{}`,
|
||||
Options: WriteOptions{AppendEvent: true, EnqueueEmbedding: true},
|
||||
Attachments: []AttachmentRecord{{
|
||||
AttachmentID: "a1",
|
||||
MessageID: "m1",
|
||||
GuildID: "g1",
|
||||
ChannelID: "c1",
|
||||
AuthorID: "u1",
|
||||
Filename: "gone.txt",
|
||||
}},
|
||||
Mentions: []MentionEventRecord{{
|
||||
MessageID: "m1",
|
||||
GuildID: "g1",
|
||||
ChannelID: "c1",
|
||||
AuthorID: "u1",
|
||||
TargetType: "user",
|
||||
TargetID: "u2",
|
||||
TargetName: "Kept",
|
||||
EventAt: now,
|
||||
}},
|
||||
},
|
||||
{
|
||||
Record: MessageRecord{
|
||||
ID: "m2",
|
||||
GuildID: "g2",
|
||||
ChannelID: "c2",
|
||||
ChannelName: "general",
|
||||
AuthorID: "u2",
|
||||
AuthorName: "Kept",
|
||||
CreatedAt: now,
|
||||
Content: "keep me",
|
||||
NormalizedContent: "keep me",
|
||||
RawJSON: `{"author":{"username":"kept"}}`,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
require.NoError(t, s.DeleteGuildData(ctx, "g1"))
|
||||
for _, query := range []string{
|
||||
"select count(*) from guilds where id = 'g1'",
|
||||
"select count(*) from channels where guild_id = 'g1'",
|
||||
"select count(*) from members where guild_id = 'g1'",
|
||||
"select count(*) from messages where guild_id = 'g1'",
|
||||
"select count(*) from message_fts where guild_id = 'g1'",
|
||||
"select count(*) from message_events where guild_id = 'g1'",
|
||||
"select count(*) from message_attachments where guild_id = 'g1'",
|
||||
"select count(*) from mention_events where guild_id = 'g1'",
|
||||
"select count(*) from embedding_jobs where message_id = 'm1'",
|
||||
} {
|
||||
_, rows, err := s.ReadOnlyQuery(ctx, query)
|
||||
require.NoError(t, err, query)
|
||||
require.Equal(t, "0", rows[0][0], query)
|
||||
}
|
||||
_, rows, err := s.ReadOnlyQuery(ctx, "select count(*) from messages where guild_id = 'g2'")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "1", rows[0][0])
|
||||
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "dm-keep", GuildID: DirectMessageGuildID, Kind: "dm", Name: "Keep", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertChannel(ctx, ChannelRecord{ID: "dm-orphan", GuildID: DirectMessageGuildID, Kind: "dm", Name: "Remove", RawJSON: `{}`}))
|
||||
require.NoError(t, s.UpsertMessage(ctx, MessageRecord{
|
||||
ID: "dm-m1",
|
||||
GuildID: DirectMessageGuildID,
|
||||
ChannelID: "dm-keep",
|
||||
ChannelName: "Keep",
|
||||
AuthorID: "u3",
|
||||
AuthorName: "Keep",
|
||||
CreatedAt: now,
|
||||
Content: "keep dm",
|
||||
NormalizedContent: "keep dm",
|
||||
RawJSON: `{}`,
|
||||
}))
|
||||
require.NoError(t, s.DeleteOrphanChannels(ctx, DirectMessageGuildID))
|
||||
_, rows, err = s.ReadOnlyQuery(ctx, "select id from channels where guild_id = '@me' order by id")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]string{{"dm-keep"}}, rows)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user