diff --git a/github/orgs_audit_log_test.go b/github/orgs_audit_log_test.go index a9235cfb288..12578095a81 100644 --- a/github/orgs_audit_log_test.go +++ b/github/orgs_audit_log_test.go @@ -6,6 +6,7 @@ package github import ( + "encoding/json" "fmt" "net/http" "strings" @@ -420,3 +421,47 @@ func TestAuditEntry_Marshal(t *testing.T) { testJSONMarshal(t, u, want) } + +func TestAuditEntry_UnmarshalJSON_NoAdditionalFields(t *testing.T) { + t.Parallel() + + var entry AuditEntry + payload := []byte(`{"action":"login","actor":"octo","token_id":10,"unknown":null}`) + + if err := json.Unmarshal(payload, &entry); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + if entry.Action == nil || *entry.Action != "login" { + t.Fatalf("Action mismatch, got: %v", entry.Action) + } + + if entry.Actor == nil || *entry.Actor != "octo" { + t.Fatalf("Actor mismatch, got: %v", entry.Actor) + } + + if entry.TokenID == nil || *entry.TokenID != int64(10) { + t.Fatalf("TokenID mismatch, got: %v", entry.TokenID) + } + + if entry.AdditionalFields != nil { + t.Fatalf("AdditionalFields should be nil, got: %#v", entry.AdditionalFields) + } +} + +func TestAuditEntry_MarshalJSON_FieldCollision(t *testing.T) { + t.Parallel() + + entry := &AuditEntry{ + Action: Ptr("login"), + AdditionalFields: map[string]any{ + "action": "override", + }, + } + + if _, err := entry.MarshalJSON(); err == nil { + t.Fatal("AuditEntry.MarshalJSON expected error for field collision, got nil") + } else if !strings.Contains(err.Error(), "unexpected field") { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/github/projects_test.go b/github/projects_test.go index ddfa2bd6a4e..c8c8e538c77 100644 --- a/github/projects_test.go +++ b/github/projects_test.go @@ -1561,6 +1561,59 @@ func TestProjectV2Item_UnmarshalJSON_InvalidJSON(t *testing.T) { } } +func TestProjectV2ItemContent_MarshalJSON_Empty(t *testing.T) { + t.Parallel() + + content := &ProjectV2ItemContent{} + + got, err := content.MarshalJSON() + if err != nil { + t.Fatalf("MarshalJSON returned error: %v", err) + } + + if string(got) != "null" { + t.Fatalf("MarshalJSON expected null, got %s", got) + } +} + +func TestProjectV2Item_UnmarshalJSON_ContentWithoutType(t *testing.T) { + t.Parallel() + + payload := `{"content":{"number":7}}` + var item ProjectV2Item + + if err := json.Unmarshal([]byte(payload), &item); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + if item.Content != nil { + t.Fatalf("Content should be nil when content_type is missing, got %#v", item.Content) + } +} + +func TestProjectV2Item_UnmarshalJSON_UnknownContentType(t *testing.T) { + t.Parallel() + + payload := `{"content_type":"Alien","content":{"id":1}}` + var item ProjectV2Item + + if err := json.Unmarshal([]byte(payload), &item); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + if item.ContentType == nil || *item.ContentType != "Alien" { + t.Fatalf("ContentType mismatch, got: %+v", item.ContentType) + } + + if item.Content == nil { + t.Fatal("Content should be initialized for unknown content_type") + } + + if item.Content.Issue != nil || item.Content.PullRequest != nil || item.Content.DraftIssue != nil { + t.Fatalf("Content fields should remain nil for unknown content_type, got %#v", item.Content) + } +} + func TestProjectV2Item_Marshal_Issue(t *testing.T) { t.Parallel() testJSONMarshal(t, &ProjectV2Item{}, "{}") diff --git a/github/rules_json_test.go b/github/rules_json_test.go new file mode 100644 index 00000000000..c2bea16f8c9 --- /dev/null +++ b/github/rules_json_test.go @@ -0,0 +1,98 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "strings" + "testing" +) + +func TestRepositoryRulesetRules_MarshalJSON_Empty(t *testing.T) { + t.Parallel() + + got, err := json.Marshal(&RepositoryRulesetRules{}) + if err != nil { + t.Fatalf("json.Marshal returned error: %v", err) + } + + if string(got) != "[]" { + t.Fatalf("expected empty array for no rules, got %s", got) + } +} + +func TestMarshalRepositoryRulesetRule_UpdateTypeValidation(t *testing.T) { + t.Parallel() + + if _, err := marshalRepositoryRulesetRule(RulesetRuleTypeUpdate, &EmptyRuleParameters{}); err == nil { + t.Fatal("expected type validation error, got nil") + } else if !strings.Contains(err.Error(), "UpdateRuleParameters") { + t.Fatalf("unexpected error: %v", err) + } +} + +func TestMarshalRepositoryRulesetRule_UpdateNoParams(t *testing.T) { + t.Parallel() + + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeUpdate, (*UpdateRuleParameters)(nil)) + if err != nil { + t.Fatalf("marshalRepositoryRulesetRule returned error: %v", err) + } + + if string(bytes) != `{"type":"update"}` { + t.Fatalf("marshalRepositoryRulesetRule expected type-only payload, got %s", bytes) + } +} + +func TestRepositoryRulesetRules_UnmarshalJSON(t *testing.T) { + t.Parallel() + + payload := `[{"type":"creation"},{"type":"required_deployments","parameters":{"required_deployment_environments":["prod"]}}]` + var rules RepositoryRulesetRules + + if err := json.Unmarshal([]byte(payload), &rules); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + if rules.Creation == nil { + t.Fatalf("Creation rule was not populated: %#v", rules.Creation) + } + + if rules.RequiredDeployments == nil || len(rules.RequiredDeployments.RequiredDeploymentEnvironments) != 1 || rules.RequiredDeployments.RequiredDeploymentEnvironments[0] != "prod" { + t.Fatalf("RequiredDeployments not populated as expected: %#v", rules.RequiredDeployments) + } +} + +func TestRepositoryRule_UnmarshalJSON(t *testing.T) { + t.Parallel() + + var creation RepositoryRule + if err := json.Unmarshal([]byte(`{"type":"creation"}`), &creation); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + if creation.Type != RulesetRuleTypeCreation { + t.Fatalf("Type mismatch, got %v", creation.Type) + } + + if creation.Parameters != nil { + t.Fatalf("creation rule should not carry parameters, got %#v", creation.Parameters) + } + + var update RepositoryRule + if err := json.Unmarshal([]byte(`{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`), &update); err != nil { + t.Fatalf("json.Unmarshal returned error: %v", err) + } + + params, ok := update.Parameters.(*UpdateRuleParameters) + if !ok || params == nil { + t.Fatalf("update parameters not decoded: %#v", update.Parameters) + } + + if !params.UpdateAllowsFetchAndMerge { + t.Fatalf("UpdateAllowsFetchAndMerge should be true, got %#v", params) + } +}