@@ -19,6 +19,85 @@ run_contacts_other_tests() {
1919 gog contacts other search " $other_query " --json --max 1 > /dev/null
2020}
2121
22+ run_contacts_dedupe_apply_test () {
23+ local email first_json second_json first_id second_id dry_json dry_primary apply_json applied_primary merged_json
24+
25+ email=" gogcli-dedupe-$TS @example.com"
26+ first_json=$( gog contacts create \
27+ --given " gogcli" \
28+ --family " dedupe-$TS " \
29+ --email " $email " \
30+ --phone " +15550000001" \
31+ --json)
32+ first_id=$( extract_field " $first_json " resourceName)
33+ [ -n " $first_id " ] || { echo " Failed to parse first dedupe contact resourceName" >&2 ; exit 1; }
34+ register_contact_cleanup " $first_id "
35+
36+ second_json=$( gog contacts create \
37+ --given " gogcli" \
38+ --family " dedupe-$TS " \
39+ --email " $email " \
40+ --phone " +15550000002" \
41+ --json)
42+ second_id=$( extract_field " $second_json " resourceName)
43+ [ -n " $second_id " ] || { echo " Failed to parse second dedupe contact resourceName" >&2 ; exit 1; }
44+ register_contact_cleanup " $second_id "
45+
46+ dry_json=$( gog contacts dedupe \
47+ --resource " $first_id " \
48+ --resource " $second_id " \
49+ --match email \
50+ --apply \
51+ --dry-run \
52+ --json)
53+ dry_primary=$( $PY -c ' import json,sys
54+ obj=json.load(sys.stdin)
55+ first,second=sys.argv[1:3]
56+ request=obj.get("request") or {}
57+ groups=request.get("groups") or []
58+ if not obj.get("dry_run") or obj.get("op") != "contacts.dedupe.apply":
59+ raise SystemExit("unexpected dedupe dry-run envelope")
60+ if request.get("groups_merged") != 1 or request.get("contacts_deleted") != 1 or len(groups) != 1:
61+ raise SystemExit("dedupe dry-run found unrelated duplicate groups; refusing live apply")
62+ group=groups[0]
63+ primary=(group.get("primary") or {}).get("resource")
64+ deleted=[item.get("resource") for item in group.get("delete") or []]
65+ if primary not in (first,second) or deleted != [second if primary == first else first]:
66+ raise SystemExit("dedupe dry-run did not target only disposable contacts")
67+ print(primary)' " $first_id " " $second_id " <<< " $dry_json" )
68+
69+ apply_json=$( gog contacts dedupe \
70+ --resource " $first_id " \
71+ --resource " $second_id " \
72+ --match email \
73+ --apply \
74+ --force \
75+ --json)
76+ applied_primary=$( $PY -c ' import json,sys
77+ obj=json.load(sys.stdin)
78+ want=sys.argv[1]
79+ groups=obj.get("groups") or []
80+ if not obj.get("applied") or obj.get("groups_merged") != 1 or obj.get("contacts_deleted") != 1 or len(groups) != 1:
81+ raise SystemExit("unexpected dedupe apply result")
82+ primary=(groups[0].get("primary") or {}).get("resource")
83+ if primary != want:
84+ raise SystemExit("dedupe apply primary changed after dry-run")
85+ print(primary)' " $dry_primary " <<< " $apply_json" )
86+
87+ merged_json=$( gog contacts get " $applied_primary " --json)
88+ $PY -c ' import json,re,sys
89+ obj=json.load(sys.stdin)
90+ contact=obj.get("contact") or {}
91+ emails={str(item.get("value","")).strip().lower() for item in contact.get("emailAddresses") or []}
92+ phones={"".join(re.findall(r"\d", str(item.get("value","")))) for item in contact.get("phoneNumbers") or []}
93+ if sys.argv[1].lower() not in emails:
94+ raise SystemExit("merged contact missing dedupe email")
95+ if not {"15550000001","15550000002"}.issubset(phones):
96+ raise SystemExit("merged contact missing dedupe phone values")' " $email " <<< " $merged_json"
97+
98+ run_required " contacts" " contacts dedupe cleanup" gog contacts delete " $applied_primary " --force > /dev/null
99+ }
100+
22101run_contacts_tests () {
23102 if skip " contacts" ; then
24103 echo " ==> contacts (skipped)"
@@ -31,6 +110,7 @@ run_contacts_tests() {
31110 contact_json=$( gog contacts create --given " gogcli" --family " smoke-$TS " --email " gogcli-smoke-$TS @example.com" --phone " +1555555$TS " --json)
32111 contact_id=$( extract_field " $contact_json " resourceName)
33112 [ -n " $contact_id " ] || { echo " Failed to parse contact resourceName" >&2 ; exit 1; }
113+ register_contact_cleanup " $contact_id "
34114
35115 run_required " contacts" " contacts get" gog contacts get " $contact_id " --json > /dev/null
36116 run_required " contacts" " contacts update" gog contacts update " $contact_id " --given " gogcli" --family " smoke-updated-$TS " --email " gogcli-smoke-$TS @example.com" --birthday " 1990-05-12" --notes " gogcli smoke $TS " --json > /dev/null
@@ -40,6 +120,7 @@ run_contacts_tests() {
40120 grep -q " EMAIL:gogcli-smoke-$TS @example.com" " $export_path " || { echo " contacts export missing email" >&2 ; exit 1; }
41121 grep -q " BDAY:19900512" " $export_path " || { echo " contacts export missing birthday" >&2 ; exit 1; }
42122 run_required " contacts" " contacts delete" gog contacts delete " $contact_id " --force > /dev/null
123+ run_required " contacts" " contacts dedupe apply" run_contacts_dedupe_apply_test
43124
44125 if is_consumer_account " $ACCOUNT " ; then
45126 echo " ==> contacts directory (skipped; Workspace only)"
0 commit comments