fix(contacts): hard-validate custom and sort output

This commit is contained in:
Peter Steinberger 2026-02-16 05:20:35 +01:00
parent 12bf5a23b3
commit a5bef10740
3 changed files with 89 additions and 2 deletions

View File

@ -426,7 +426,6 @@ func (c *ContactsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *
userDefined, parseErr := parseCustomUserDefined(c.Custom, true)
if parseErr != nil {
return usage(parseErr.Error())
}
existing.UserDefined = userDefined
updateFields = append(updateFields, "userDefined")
}

View File

@ -95,3 +95,41 @@ func TestPrimaryBirthday_EdgeCases(t *testing.T) {
t.Fatalf("unexpected: %q", got)
}
}
func TestParseCustomUserDefined_InvalidInput(t *testing.T) {
if _, _, err := parseCustomUserDefined([]string{"bad"}, true); err == nil {
t.Fatalf("expected error for missing '='")
}
if _, _, err := parseCustomUserDefined([]string{"=value"}, true); err == nil {
t.Fatalf("expected error for empty key")
}
if _, _, err := parseCustomUserDefined([]string{""}, false); err == nil {
t.Fatalf("expected error for empty custom value")
}
}
func TestParseCustomUserDefined_ValidInput(t *testing.T) {
fields, clear, err := parseCustomUserDefined([]string{"team=devops", " repo = gog"}, false)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if clear {
t.Fatalf("did not expect clear")
}
if len(fields) != 2 || fields[0].Key != "team" || fields[0].Value != "devops" || fields[1].Key != "repo" || fields[1].Value != "gog" {
t.Fatalf("unexpected fields: %#v", fields)
}
}
func TestParseCustomUserDefined_ClearAll(t *testing.T) {
fields, clear, err := parseCustomUserDefined([]string{""}, true)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !clear {
t.Fatalf("expected clear")
}
if len(fields) != 0 {
t.Fatalf("expected empty fields, got %v", len(fields))
}
}

View File

@ -68,7 +68,7 @@ func TestExecute_ContactsGet_ByResource_Text(t *testing.T) {
t.Cleanup(func() { newPeopleContactsService = origNew })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !(strings.Contains(r.URL.Path, "/people/c1") && r.Method == http.MethodGet) {
if !(strings.Contains(r.URL.Path, "/people/c1") && r.Method == http.MethodGet && !strings.Contains(r.URL.Path, ":")) {
http.NotFound(w, r)
return
}
@ -106,6 +106,56 @@ func TestExecute_ContactsGet_ByResource_Text(t *testing.T) {
}
}
func TestExecute_ContactsGet_CustomFieldsSorted_Text(t *testing.T) {
origNew := newPeopleContactsService
t.Cleanup(func() { newPeopleContactsService = origNew })
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !(strings.Contains(r.URL.Path, "/people/c1") && r.Method == http.MethodGet) {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"resourceName": "people/c1",
"userDefined": []map[string]any{
{"key": "zzz", "value": "3"},
{"key": "aaa", "value": "1"},
{"key": "mmm", "value": "2"},
},
})
}))
defer srv.Close()
svc, err := people.NewService(context.Background(),
option.WithoutAuthentication(),
option.WithHTTPClient(srv.Client()),
option.WithEndpoint(srv.URL+"/"),
)
if err != nil {
t.Fatalf("NewService: %v", err)
}
newPeopleContactsService = func(context.Context, string) (*people.Service, error) { return svc, nil }
out := captureStdout(t, func() {
_ = captureStderr(t, func() {
if err := Execute([]string{"--account", "a@b.com", "contacts", "get", "people/c1"}); err != nil {
t.Fatalf("Execute: %v", err)
}
})
})
a := strings.Index(out, "custom:aaa\t1")
b := strings.Index(out, "custom:mmm\t2")
c := strings.Index(out, "custom:zzz\t3")
if a < 0 || b < 0 || c < 0 {
t.Fatalf("missing custom fields: %q", out)
}
if !(a < b && b < c) {
t.Fatalf("custom fields not sorted: %q", out)
}
}
func TestExecute_CalendarFreeBusy_Text(t *testing.T) {
origNew := newCalendarService
t.Cleanup(func() { newCalendarService = origNew })