fe0d24ae87
- Apply the session-list additions to the SQLite init migration (Lite build) and make QueryPaged dialect-aware: LOWER(..) LIKE on SQLite, ILIKE on Postgres, drop NULLS LAST on SQLite. Escape LIKE wildcards in the keyword via the existing escapeLikeKeyword helper. - SetPinned now returns rowsAffected so the handler can respond 404 on unknown / unauthorized session IDs instead of a misleading 200. - GET /sessions always returns the enriched shape (pin state + IM origin fields) so the frontend never needs a second roundtrip; the dual-path legacy branch in the handler is gone. - Register pin routes with the wildcard name that matches each verb's existing radix tree (POST :session_id, DELETE :id) and accept either param name in the handler; avoids gin's "wildcard conflicts" panic. - Drop the redundant [platform] prefix from IM session titles now that the list renders a platform icon alongside the title; add unit tests for shortID / buildUserSessionTitle / buildThreadSessionTitle. - Frontend: remove the submenu search input and its i18n/keyword wiring (search lives elsewhere in the app); pin icon uses the TDesign `pin` glyph and inherits color so the active session turns green; optimistic pin moves the item to the top of the list so it shows up at the top of the Pinned group; IM text badge replaced by the platform SVG icons from assets/img/im, desaturated by default and full-color on hover or when the session is active.
110 lines
2.3 KiB
Go
110 lines
2.3 KiB
Go
package im
|
|
|
|
import "testing"
|
|
|
|
func TestShortID(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
}{
|
|
{"empty", "", ""},
|
|
{"shorter than 8", "abc", "abc"},
|
|
{"exactly 8", "12345678", "12345678"},
|
|
{"longer than 8 keeps suffix", "aaaaaaaaXXXXXXXX", "XXXXXXXX"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := shortID(tt.in); got != tt.want {
|
|
t.Errorf("shortID(%q) = %q, want %q", tt.in, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildUserSessionTitle(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
msg *IncomingMessage
|
|
want string
|
|
}{
|
|
{
|
|
name: "group with username",
|
|
msg: &IncomingMessage{
|
|
Platform: "feishu",
|
|
UserName: "李四",
|
|
ChatType: ChatTypeGroup,
|
|
ChatID: "oc_aaaaaaaaaaaaaaaa",
|
|
},
|
|
want: "李四 · group aaaaaaaa",
|
|
},
|
|
{
|
|
name: "direct message with username",
|
|
msg: &IncomingMessage{
|
|
Platform: "feishu",
|
|
UserName: "李四",
|
|
ChatType: ChatTypeDirect,
|
|
},
|
|
want: "李四 · dm",
|
|
},
|
|
{
|
|
name: "group without username falls back to user id",
|
|
msg: &IncomingMessage{
|
|
Platform: "wecom",
|
|
UserID: "WeCom_ZhangSan",
|
|
ChatType: ChatTypeGroup,
|
|
ChatID: "wc_group_1234",
|
|
},
|
|
want: "user ZhangSan · group oup_1234",
|
|
},
|
|
{
|
|
name: "no user identity at all",
|
|
msg: &IncomingMessage{
|
|
Platform: "slack",
|
|
ChatType: ChatTypeDirect,
|
|
},
|
|
want: "user · dm",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := buildUserSessionTitle(tt.msg); got != tt.want {
|
|
t.Errorf("buildUserSessionTitle() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildThreadSessionTitle(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
msg *IncomingMessage
|
|
want string
|
|
}{
|
|
{
|
|
name: "chat + thread",
|
|
msg: &IncomingMessage{
|
|
Platform: "slack",
|
|
ChatID: "C0123456789",
|
|
ThreadID: "1700000000.111000",
|
|
},
|
|
want: "chat 23456789 · thread 0.111000",
|
|
},
|
|
{
|
|
name: "thread without chat id",
|
|
msg: &IncomingMessage{
|
|
Platform: "feishu",
|
|
ThreadID: "om_thread_abcdefgh",
|
|
},
|
|
want: "thread abcdefgh",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := buildThreadSessionTitle(tt.msg); got != tt.want {
|
|
t.Errorf("buildThreadSessionTitle() = %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|