Build and publish Docker images / Build and publish images (push) Successful in 2m10s
246 lines
8.1 KiB
Go
246 lines
8.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/gochat/gochat/internal/geoip"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type fakeVisitorGeoResolver struct {
|
|
location geoip.Location
|
|
ok bool
|
|
seenIP net.IP
|
|
}
|
|
|
|
func (f *fakeVisitorGeoResolver) Lookup(ip net.IP) (geoip.Location, bool) {
|
|
f.seenIP = append(net.IP(nil), ip...)
|
|
return f.location, f.ok
|
|
}
|
|
|
|
func TestFormatVisitorName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
province string
|
|
city string
|
|
want string
|
|
}{
|
|
{name: "province and city", province: "河北省", city: "保定市", want: "河北保定客户"},
|
|
{name: "municipality", province: "北京市", city: "北京市", want: "北京客户"},
|
|
{name: "province only", province: "河北省", want: "河北客户"},
|
|
{name: "empty", want: ""},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, formatVisitorName(tt.province, tt.city))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWidgetService_InitUsesGeoIPNameWithoutPersistingIP(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
_, inbox := seedWidgetInbox(t, db)
|
|
resolver := &fakeVisitorGeoResolver{
|
|
location: geoip.Location{Province: "河北省", City: "保定市"},
|
|
ok: true,
|
|
}
|
|
svc.SetVisitorGeoResolver(resolver)
|
|
|
|
resp, err := svc.Init(context.Background(), WidgetInitRequest{
|
|
WebsiteToken: "test_ws_token_123",
|
|
ClientIP: "203.0.113.10",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, resp.Contact)
|
|
assert.Equal(t, inbox.ID, resp.InboxID)
|
|
assert.Equal(t, "203.0.113.10", resolver.seenIP.String())
|
|
assert.Equal(t, "河北保定客户", resp.Contact.Name)
|
|
|
|
var attributes map[string]any
|
|
require.NoError(t, json.Unmarshal(resp.Contact.AdditionalAttributes, &attributes))
|
|
assert.Equal(t, visitorNameSourceGeoIP, attributes[visitorNameSourceKey])
|
|
assert.Equal(t, "河北", attributes[visitorProvinceKey])
|
|
assert.Equal(t, "保定", attributes[visitorCityKey])
|
|
assert.NotContains(t, attributes, "client_ip")
|
|
assert.NotContains(t, attributes, "created_at_ip")
|
|
}
|
|
|
|
func TestWidgetService_InitFallsBackWhenGeoIPHasNoRecord(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
seedWidgetInbox(t, db)
|
|
svc.SetVisitorGeoResolver(&fakeVisitorGeoResolver{ok: false})
|
|
|
|
resp, err := svc.Init(context.Background(), WidgetInitRequest{
|
|
WebsiteToken: "test_ws_token_123",
|
|
ClientIP: "198.51.100.20",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, anonymousVisitorName, resp.Contact.Name)
|
|
}
|
|
|
|
func TestWidgetService_InitPreservesRealName(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
seedWidgetInbox(t, db)
|
|
resolver := &fakeVisitorGeoResolver{location: geoip.Location{Province: "河北", City: "保定"}, ok: true}
|
|
svc.SetVisitorGeoResolver(resolver)
|
|
|
|
resp, err := svc.Init(context.Background(), WidgetInitRequest{
|
|
WebsiteToken: "test_ws_token_123",
|
|
ContactName: "张三",
|
|
ClientIP: "203.0.113.11",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "张三", resp.Contact.Name)
|
|
assert.Nil(t, resolver.seenIP)
|
|
}
|
|
|
|
func TestWidgetService_InitUpgradesLegacyAnonymousName(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
account, _ := seedWidgetInbox(t, db)
|
|
legacy := &model.Contact{
|
|
AccountID: account.ID,
|
|
Name: legacyAnonymousVisitorName,
|
|
Email: "legacy@example.com",
|
|
ContactType: "visitor",
|
|
}
|
|
require.NoError(t, db.Create(legacy).Error)
|
|
resolver := &fakeVisitorGeoResolver{location: geoip.Location{Province: "北京市", City: "北京市"}, ok: true}
|
|
svc.SetVisitorGeoResolver(resolver)
|
|
|
|
resp, err := svc.Init(context.Background(), WidgetInitRequest{
|
|
WebsiteToken: "test_ws_token_123",
|
|
ContactEmail: "legacy@example.com",
|
|
ClientIP: "203.0.113.12",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, legacy.ID, resp.ContactID)
|
|
assert.Equal(t, "北京客户", resp.Contact.Name)
|
|
}
|
|
|
|
func TestWidgetService_RealNameWinsOverGeneratedName(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
account, _ := seedWidgetInbox(t, db)
|
|
contact := &model.Contact{
|
|
AccountID: account.ID,
|
|
Name: anonymousVisitorName,
|
|
AdditionalAttributes: mustJSON(map[string]any{
|
|
visitorNameSourceKey: visitorNameSourceFallback,
|
|
}),
|
|
}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
svc.SetVisitorGeoResolver(&fakeVisitorGeoResolver{
|
|
location: geoip.Location{Province: "河北", City: "保定"},
|
|
ok: true,
|
|
})
|
|
|
|
updated, err := svc.updateContactFields(context.Background(), contact, WidgetContactUpdate{Name: "张三"})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "张三", updated.Name)
|
|
assert.Empty(t, jsonMap(updated.AdditionalAttributes)[visitorNameSourceKey])
|
|
|
|
updated, err = svc.enrichAnonymousWidgetContact(context.Background(), updated, "203.0.113.14")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "张三", updated.Name)
|
|
}
|
|
|
|
func TestWidgetService_DoesNotOverwriteManuallyRenamedFallbackContact(t *testing.T) {
|
|
_, svc := setupWidgetServiceTest(t)
|
|
contact := &model.Contact{
|
|
Name: "客服改名",
|
|
AdditionalAttributes: mustJSON(map[string]any{
|
|
visitorNameSourceKey: visitorNameSourceFallback,
|
|
}),
|
|
}
|
|
svc.SetVisitorGeoResolver(&fakeVisitorGeoResolver{
|
|
location: geoip.Location{Province: "河北", City: "保定"},
|
|
ok: true,
|
|
})
|
|
|
|
updated, err := svc.enrichAnonymousWidgetContact(context.Background(), contact, "203.0.113.15")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "客服改名", updated.Name)
|
|
}
|
|
|
|
func TestWidgetService_ProtectsVisitorAttributesFromClientUpdates(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
account, _ := seedWidgetInbox(t, db)
|
|
contact := &model.Contact{
|
|
AccountID: account.ID,
|
|
Name: "河北保定客户",
|
|
AdditionalAttributes: mustJSON(map[string]any{
|
|
visitorNameSourceKey: visitorNameSourceGeoIP,
|
|
visitorProvinceKey: "河北",
|
|
visitorCityKey: "保定",
|
|
}),
|
|
}
|
|
require.NoError(t, db.Create(contact).Error)
|
|
|
|
updated, err := svc.updateContactFields(context.Background(), contact, WidgetContactUpdate{
|
|
AdditionalAttributes: map[string]any{
|
|
visitorNameSourceKey: "spoofed",
|
|
visitorProvinceKey: "北京",
|
|
"customer_note": "kept",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
attributes := map[string]any{}
|
|
require.NoError(t, json.Unmarshal(updated.AdditionalAttributes, &attributes))
|
|
assert.Equal(t, visitorNameSourceGeoIP, attributes[visitorNameSourceKey])
|
|
assert.Equal(t, "河北", attributes[visitorProvinceKey])
|
|
assert.Equal(t, "kept", attributes["customer_note"])
|
|
}
|
|
|
|
func TestWidgetService_PublicContactUsesGeoIPNameAndFiltersClientMetadata(t *testing.T) {
|
|
db, svc := setupWidgetServiceTest(t)
|
|
account, _ := seedWidgetInbox(t, db)
|
|
svc.SetVisitorGeoResolver(&fakeVisitorGeoResolver{
|
|
location: geoip.Location{Province: "北京市", City: "北京市"},
|
|
ok: true,
|
|
})
|
|
|
|
contact, err := svc.findPublicContact(context.Background(), account.ID, PublicContactRequest{
|
|
AdditionalAttributes: map[string]any{
|
|
visitorNameSourceKey: "spoofed",
|
|
"customer_note": "kept",
|
|
},
|
|
ClientIP: "203.0.113.16",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "北京客户", contact.Name)
|
|
attributes := jsonMap(contact.AdditionalAttributes)
|
|
assert.Equal(t, visitorNameSourceGeoIP, attributes[visitorNameSourceKey])
|
|
assert.Equal(t, "kept", attributes["customer_note"])
|
|
}
|
|
|
|
func TestPublicWidgetAdditionalAttributesHidesVisitorMetadata(t *testing.T) {
|
|
public := PublicWidgetAdditionalAttributes(mustJSON(map[string]any{
|
|
visitorNameSourceKey: visitorNameSourceGeoIP,
|
|
visitorProvinceKey: "河北",
|
|
"created_at_ip": "203.0.113.10",
|
|
"customer_note": "kept",
|
|
}))
|
|
assert.Equal(t, map[string]any{"customer_note": "kept"}, public)
|
|
}
|
|
|
|
func TestWidgetService_SubmitOfflineMessageUsesGeoIPName(t *testing.T) {
|
|
db, svc := setupWidgetOfflineMessageTest(t)
|
|
account := createTestAccountForWidget(t, db)
|
|
inbox := createTestInboxForWidget(t, db, account.ID)
|
|
svc.SetVisitorGeoResolver(&fakeVisitorGeoResolver{
|
|
location: geoip.Location{Province: "河北省", City: "保定市"},
|
|
ok: true,
|
|
})
|
|
|
|
msg, err := svc.SubmitOfflineMessage(context.Background(), inbox.ID, account.ID, &model.WidgetOfflineMessageSubmission{
|
|
Message: "Need help",
|
|
ClientIP: "203.0.113.13",
|
|
}, "", "")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "河北保定客户", msg.ContactName)
|
|
}
|