Files
go-onlyoffice/crm_dedupe_test.go
eSliderandCursor b79c097874 feat(crm): dedupe duplicates, fix deal titles, and add cleanup CLI
Merge duplicate companies/persons/deals by name, remove duplicate contact
info and opportunity members, and rename malformed " @ Company" titles.
Applications sync now avoids empty-position titles and duplicate members.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 12:34:14 +01:00

134 lines
3.9 KiB
Go

package onlyoffice
import (
"context"
"reflect"
"testing"
)
type fakeDedupeClient struct {
contacts []map[string]any
opportunities []map[string]any
contactByID map[string]map[string]any
oppByID map[string]map[string]any
merged [][2]string
deletedOpp []string
deletedInfo [][2]string
removedMember [][2]string
renamed map[string]string
}
func (f *fakeDedupeClient) ListAllContacts(ctx context.Context) ([]map[string]any, error) {
return f.contacts, nil
}
func (f *fakeDedupeClient) ListAllOpportunities(ctx context.Context) ([]map[string]any, error) {
return f.opportunities, nil
}
func (f *fakeDedupeClient) GetContact(ctx context.Context, contactID string) (map[string]any, error) {
return f.contactByID[contactID], nil
}
func (f *fakeDedupeClient) GetOpportunity(ctx context.Context, id string) (map[string]any, error) {
return f.oppByID[id], nil
}
func (f *fakeDedupeClient) MergeContacts(ctx context.Context, primaryID, secondaryID string) (map[string]any, error) {
f.merged = append(f.merged, [2]string{primaryID, secondaryID})
return map[string]any{"id": primaryID}, nil
}
func (f *fakeDedupeClient) DeleteOpportunity(ctx context.Context, id string) (map[string]any, error) {
f.deletedOpp = append(f.deletedOpp, id)
return map[string]any{}, nil
}
func (f *fakeDedupeClient) DeleteContactInfo(ctx context.Context, contactID, dataID string) (map[string]any, error) {
f.deletedInfo = append(f.deletedInfo, [2]string{contactID, dataID})
return map[string]any{}, nil
}
func (f *fakeDedupeClient) ListCompanyPersons(ctx context.Context, companyID string) ([]map[string]any, error) {
return nil, nil
}
func (f *fakeDedupeClient) AddOpportunityMember(ctx context.Context, oppID, contactID string) (map[string]any, error) {
return map[string]any{}, nil
}
func (f *fakeDedupeClient) RemoveOpportunityMember(ctx context.Context, oppID, contactID string) (map[string]any, error) {
f.removedMember = append(f.removedMember, [2]string{oppID, contactID})
return map[string]any{}, nil
}
func (f *fakeDedupeClient) UpdateOpportunityTitle(ctx context.Context, id, newTitle string) (map[string]any, error) {
if f.renamed == nil {
f.renamed = make(map[string]string)
}
f.renamed[id] = newTitle
return map[string]any{"title": newTitle}, nil
}
func TestDedupeCompaniesOrchestration(t *testing.T) {
f := &fakeDedupeClient{
contacts: []map[string]any{
{"id": float64(857), "displayName": "contoso", "isCompany": true},
{"id": float64(908), "displayName": "contoso", "isCompany": true},
},
}
res, err := DedupeCompanies(context.Background(), f)
if err != nil {
t.Fatal(err)
}
if res.Merged != 1 || res.Groups != 1 {
t.Fatalf("res %+v", res)
}
want := [][2]string{{"857", "908"}}
if !reflect.DeepEqual(f.merged, want) {
t.Fatalf("merged %v", f.merged)
}
}
func TestDedupeOpportunityMembersOrchestration(t *testing.T) {
f := &fakeDedupeClient{
opportunities: []map[string]any{{"id": float64(231), "title": " @ contoso"}},
oppByID: map[string]map[string]any{
"231": {
"id": float64(231),
"members": []any{
map[string]any{"id": float64(857), "displayName": "contoso"},
map[string]any{"id": float64(908), "displayName": "contoso"},
},
},
},
}
res, err := DedupeOpportunityMembers(context.Background(), f)
if err != nil {
t.Fatal(err)
}
if res.Removed != 1 {
t.Fatalf("res %+v", res)
}
if len(f.removedMember) != 1 || f.removedMember[0][1] != "908" {
t.Fatalf("removed %v", f.removedMember)
}
}
func TestFixOpportunityTitlesOrchestration(t *testing.T) {
f := &fakeDedupeClient{
opportunities: []map[string]any{{"id": float64(231), "title": " @ contoso"}},
oppByID: map[string]map[string]any{"231": {"id": float64(231), "title": " @ contoso"}},
}
res, err := FixOpportunityTitles(context.Background(), f)
if err != nil {
t.Fatal(err)
}
if res.Renamed != 1 {
t.Fatalf("res %+v", res)
}
if f.renamed["231"] != "contoso" {
t.Fatalf("renamed %v", f.renamed)
}
}