Files
VoCat/internal/server/sms_api_test.go
T
MengMengCode 9fc3f1c5b8 feat: add Telegram API URL handling and version comparison utilities
- Implemented `telegramAPIURL` and `validateTelegramAPIURL` functions for constructing and validating Telegram API URLs.
- Added semantic versioning utilities in `version.go` to compare versions and validate semantic version formats.
- Created tests for version comparison logic in `version_test.go`.
- Introduced IMS call handling in `call_runtime.go`, including methods for dialing, answering, and hanging up calls.
- Added tests for incoming call handling and validation in `call_runtime_test.go`.
- Developed a new `PluginsCard` component for managing plugins via URL or file upload in the web interface.
- Implemented plugin management functions in `extensions.ts` and created an `ExtensionPage` for displaying plugin contributions.
2026-08-09 19:28:12 +08:00

158 lines
4.3 KiB
Go

package server
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"vocat/internal/store"
)
func TestSMSThreadAllDevicesUsesIMSIFilter(t *testing.T) {
ctx := context.Background()
database, err := store.Open(ctx, ":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = database.Close() })
for index, imsi := range []string{"imsi-a", "imsi-b"} {
if _, err := database.SaveSMSMessage(ctx, store.SMSMessage{
MessageID: "message-" + imsi,
DeviceID: "ec20",
IMSI: imsi,
Peer: "VOXI",
Direction: "inbound",
Body: imsi,
Timestamp: time.Unix(1_700_000_000+int64(index), 0),
}); err != nil {
t.Fatal(err)
}
}
server := &Server{store: database}
request := httptest.NewRequest(
http.MethodGet,
"/api/sms/thread?device_id=all&imsi=imsi-a&peer=VOXI",
nil,
)
response := httptest.NewRecorder()
server.handleSMSThread(response, request)
if response.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", response.Code, response.Body.String())
}
var envelope struct {
Data []map[string]any `json:"data"`
}
if err := json.Unmarshal(response.Body.Bytes(), &envelope); err != nil {
t.Fatal(err)
}
if len(envelope.Data) != 1 || envelope.Data[0]["imsi"] != "imsi-a" {
t.Fatalf("thread data = %#v", envelope.Data)
}
}
func TestSMSThreadConfiguredDeviceUsesStableIMEI(t *testing.T) {
ctx := context.Background()
database, err := store.Open(ctx, ":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = database.Close() })
const imei = "867394042309830"
if err := database.UpsertDevice(ctx, store.Device{
ID: "ec20_2", Name: "EC20 renamed", ModemIMEI: imei,
}); err != nil {
t.Fatal(err)
}
if _, err := database.SaveSMSMessage(ctx, store.SMSMessage{
MessageID: "before-rename", DeviceID: "ec20_1", ModemIMEI: imei,
IMSI: "imsi-a", Peer: "VOXI", Direction: "inbound", Body: "history",
}); err != nil {
t.Fatal(err)
}
server := &Server{store: database}
request := httptest.NewRequest(
http.MethodGet,
"/api/sms/thread?device_id=ec20_2&imsi=imsi-a&peer=VOXI",
nil,
)
response := httptest.NewRecorder()
server.handleSMSThread(response, request)
if response.Code != http.StatusOK {
t.Fatalf("status = %d, body = %s", response.Code, response.Body.String())
}
var envelope struct {
Data []map[string]any `json:"data"`
}
if err := json.Unmarshal(response.Body.Bytes(), &envelope); err != nil {
t.Fatal(err)
}
if len(envelope.Data) != 1 || envelope.Data[0]["modem_imei"] != imei {
t.Fatalf("thread data = %#v", envelope.Data)
}
}
func TestNormalizeSMSDeviceFilter(t *testing.T) {
if got := normalizeSMSDeviceFilter(" ALL "); got != "" {
t.Fatalf("all filter = %q", got)
}
if got := normalizeSMSDeviceFilter("EC20"); got != "EC20" {
t.Fatalf("device filter = %q", got)
}
}
func TestSMSSendOutcome(t *testing.T) {
tests := []struct {
name string
all bool
accepted int
total int
delivered bool
want string
}{
{name: "delivered", all: true, accepted: 1, total: 1, delivered: true, want: "delivered"},
{name: "accepted but unconfirmed", all: true, accepted: 2, total: 2, want: "accepted_unconfirmed"},
{name: "partial", accepted: 1, total: 2, want: "partial"},
{name: "failed", total: 1, want: "failed"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := smsSendOutcome(test.all, test.accepted, test.total, test.delivered); got != test.want {
t.Fatalf("smsSendOutcome() = %q, want %q", got, test.want)
}
})
}
}
func TestBlockedSMSDestination(t *testing.T) {
tests := []struct {
name string
phone string
block bool
}{
{"e164 china", "+8613800138000", true},
{"no plus china", "8613800138000", true},
{"international prefix china", "008613800138000", true},
{"spaced china", "+86 138 0013 8000", true},
{"dashed china", "+86-138-0013-8000", true},
{"us e164", "+12025550177", false},
{"us no plus", "12025550177", false},
{"uk e164", "+447700900123", false},
{"italy", "+393331234567", false},
{"russia", "+79161234567", false},
{"japan", "+819012345678", false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
blocked, _ := blockedSMSDestination(test.phone)
if blocked != test.block {
t.Fatalf("blockedSMSDestination(%q) blocked = %v, want %v", test.phone, blocked, test.block)
}
})
}
}