github.com/google/go-github/v60@v60.0.0/github/repos_rules_test.go (about)

     1  // Copyright 2023 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package github
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"net/http"
    12  	"testing"
    13  
    14  	"github.com/google/go-cmp/cmp"
    15  )
    16  
    17  func TestRepositoryRule_UnmarshalJSON(t *testing.T) {
    18  	tests := map[string]struct {
    19  		data    string
    20  		want    *RepositoryRule
    21  		wantErr bool
    22  	}{
    23  		"Invalid JSON": {
    24  			data: `{`,
    25  			want: &RepositoryRule{
    26  				Type:       "",
    27  				Parameters: nil,
    28  			},
    29  			wantErr: true,
    30  		},
    31  		"Valid creation": {
    32  			data: `{"type":"creation"}`,
    33  			want: NewCreationRule(),
    34  		},
    35  		"Valid deletion": {
    36  			data: `{"type":"deletion"}`,
    37  			want: &RepositoryRule{
    38  				Type:       "deletion",
    39  				Parameters: nil,
    40  			},
    41  		},
    42  		"Valid required_linear_history": {
    43  			data: `{"type":"required_linear_history"}`,
    44  			want: &RepositoryRule{
    45  				Type:       "required_linear_history",
    46  				Parameters: nil,
    47  			},
    48  		},
    49  		"Valid required_signatures": {
    50  			data: `{"type":"required_signatures"}`,
    51  			want: &RepositoryRule{
    52  				Type:       "required_signatures",
    53  				Parameters: nil,
    54  			},
    55  		},
    56  		"Valid non_fast_forward": {
    57  			data: `{"type":"non_fast_forward"}`,
    58  			want: &RepositoryRule{
    59  				Type:       "non_fast_forward",
    60  				Parameters: nil,
    61  			},
    62  		},
    63  		"Valid update params": {
    64  			data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`,
    65  			want: NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{UpdateAllowsFetchAndMerge: true}),
    66  		},
    67  		"Invalid update params": {
    68  			data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":"true"}}`,
    69  			want: &RepositoryRule{
    70  				Type:       "update",
    71  				Parameters: nil,
    72  			},
    73  			wantErr: true,
    74  		},
    75  		"Valid required_deployments params": {
    76  			data: `{"type":"required_deployments","parameters":{"required_deployment_environments":["test"]}}`,
    77  			want: NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{
    78  				RequiredDeploymentEnvironments: []string{"test"},
    79  			}),
    80  		},
    81  		"Invalid required_deployments params": {
    82  			data: `{"type":"required_deployments","parameters":{"required_deployment_environments":true}}`,
    83  			want: &RepositoryRule{
    84  				Type:       "required_deployments",
    85  				Parameters: nil,
    86  			},
    87  			wantErr: true,
    88  		},
    89  		"Valid commit_message_pattern params": {
    90  			data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`,
    91  			want: NewCommitMessagePatternRule(&RulePatternParameters{
    92  				Operator: "starts_with",
    93  				Pattern:  "github",
    94  			}),
    95  		},
    96  		"Invalid commit_message_pattern params": {
    97  			data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":1}}`,
    98  			want: &RepositoryRule{
    99  				Type:       "commit_message_pattern",
   100  				Parameters: nil,
   101  			},
   102  			wantErr: true,
   103  		},
   104  		"Valid commit_author_email_pattern params": {
   105  			data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`,
   106  			want: NewCommitAuthorEmailPatternRule(&RulePatternParameters{
   107  				Operator: "starts_with",
   108  				Pattern:  "github",
   109  			}),
   110  		},
   111  		"Invalid commit_author_email_pattern params": {
   112  			data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`,
   113  			want: &RepositoryRule{
   114  				Type:       "commit_author_email_pattern",
   115  				Parameters: nil,
   116  			},
   117  			wantErr: true,
   118  		},
   119  		"Valid committer_email_pattern params": {
   120  			data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`,
   121  			want: NewCommitterEmailPatternRule(&RulePatternParameters{
   122  				Operator: "starts_with",
   123  				Pattern:  "github",
   124  			}),
   125  		},
   126  		"Invalid committer_email_pattern params": {
   127  			data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`,
   128  			want: &RepositoryRule{
   129  				Type:       "committer_email_pattern",
   130  				Parameters: nil,
   131  			},
   132  			wantErr: true,
   133  		},
   134  		"Valid branch_name_pattern params": {
   135  			data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`,
   136  			want: NewBranchNamePatternRule(&RulePatternParameters{
   137  				Operator: "starts_with",
   138  				Pattern:  "github",
   139  			}),
   140  		},
   141  		"Invalid branch_name_pattern params": {
   142  			data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`,
   143  			want: &RepositoryRule{
   144  				Type:       "branch_name_pattern",
   145  				Parameters: nil,
   146  			},
   147  			wantErr: true,
   148  		},
   149  		"Valid tag_name_pattern params": {
   150  			data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`,
   151  			want: NewTagNamePatternRule(&RulePatternParameters{
   152  				Operator: "starts_with",
   153  				Pattern:  "github",
   154  			}),
   155  		},
   156  		"Invalid tag_name_pattern params": {
   157  			data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`,
   158  			want: &RepositoryRule{
   159  				Type:       "tag_name_pattern",
   160  				Parameters: nil,
   161  			},
   162  			wantErr: true,
   163  		},
   164  		"Valid pull_request params": {
   165  			data: `{
   166  				"type":"pull_request",
   167  				"parameters":{
   168  					"dismiss_stale_reviews_on_push": true,
   169  					"require_code_owner_review": true,
   170  					"require_last_push_approval": true,
   171  					"required_approving_review_count": 1,
   172  					"required_review_thread_resolution":true
   173  				}
   174  			}`,
   175  			want: NewPullRequestRule(&PullRequestRuleParameters{
   176  				DismissStaleReviewsOnPush:      true,
   177  				RequireCodeOwnerReview:         true,
   178  				RequireLastPushApproval:        true,
   179  				RequiredApprovingReviewCount:   1,
   180  				RequiredReviewThreadResolution: true,
   181  			}),
   182  		},
   183  		"Invalid pull_request params": {
   184  			data: `{"type":"pull_request","parameters": {"dismiss_stale_reviews_on_push":"true"}}`,
   185  			want: &RepositoryRule{
   186  				Type:       "pull_request",
   187  				Parameters: nil,
   188  			},
   189  			wantErr: true,
   190  		},
   191  		"Valid required_status_checks params": {
   192  			data: `{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test","integration_id":1}],"strict_required_status_checks_policy":true}}`,
   193  			want: NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{
   194  				RequiredStatusChecks: []RuleRequiredStatusChecks{
   195  					{
   196  						Context:       "test",
   197  						IntegrationID: Int64(1),
   198  					},
   199  				},
   200  				StrictRequiredStatusChecksPolicy: true,
   201  			}),
   202  		},
   203  		"Invalid required_status_checks params": {
   204  			data: `{"type":"required_status_checks",
   205  			"parameters": {
   206  				"required_status_checks": [
   207  				  {
   208  					"context": 1
   209  				  }
   210  				]
   211  			  }}`,
   212  			want: &RepositoryRule{
   213  				Type:       "required_status_checks",
   214  				Parameters: nil,
   215  			},
   216  			wantErr: true,
   217  		},
   218  		"Required workflows params": {
   219  			data: `{"type":"workflows","parameters":{"workflows":[{"path": ".github/workflows/test.yml", "repository_id": 1}]}}`,
   220  			want: NewRequiredWorkflowsRule(&RequiredWorkflowsRuleParameters{
   221  				RequiredWorkflows: []*RuleRequiredWorkflow{
   222  					{
   223  						Path:         ".github/workflows/test.yml",
   224  						RepositoryID: Int64(1),
   225  					},
   226  				},
   227  			}),
   228  		},
   229  		"Invalid type": {
   230  			data: `{"type":"unknown"}`,
   231  			want: &RepositoryRule{
   232  				Type:       "",
   233  				Parameters: nil,
   234  			},
   235  			wantErr: true,
   236  		},
   237  	}
   238  
   239  	for name, tc := range tests {
   240  		rule := &RepositoryRule{}
   241  
   242  		t.Run(name, func(t *testing.T) {
   243  			err := rule.UnmarshalJSON([]byte(tc.data))
   244  			if err == nil && tc.wantErr {
   245  				t.Errorf("RepositoryRule.UnmarshalJSON returned nil instead of an error")
   246  			}
   247  			if err != nil && !tc.wantErr {
   248  				t.Errorf("RepositoryRule.UnmarshalJSON returned an unexpected error: %+v", err)
   249  			}
   250  			if !cmp.Equal(tc.want, rule) {
   251  				t.Errorf("RepositoryRule.UnmarshalJSON expected rule %+v, got %+v", tc.want, rule)
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestRepositoriesService_GetRulesForBranch(t *testing.T) {
   258  	client, mux, _, teardown := setup()
   259  	defer teardown()
   260  
   261  	mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) {
   262  		testMethod(t, r, "GET")
   263  		fmt.Fprint(w, `[
   264  			{
   265  			  "type": "creation"
   266  			},
   267  			{
   268  			  "type": "update",
   269  			  "parameters": {
   270  			    "update_allows_fetch_and_merge": true
   271  			  }
   272  			}
   273  		]`)
   274  	})
   275  
   276  	ctx := context.Background()
   277  	rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch")
   278  	if err != nil {
   279  		t.Errorf("Repositories.GetRulesForBranch returned error: %v", err)
   280  	}
   281  
   282  	creationRule := NewCreationRule()
   283  	updateRule := NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{
   284  		UpdateAllowsFetchAndMerge: true,
   285  	})
   286  
   287  	want := []*RepositoryRule{
   288  		creationRule,
   289  		updateRule,
   290  	}
   291  	if !cmp.Equal(rules, want) {
   292  		t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", rules, want)
   293  	}
   294  
   295  	const methodName = "GetRulesForBranch"
   296  
   297  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   298  		got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch")
   299  		if got != nil {
   300  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   301  		}
   302  		return resp, err
   303  	})
   304  }
   305  
   306  func TestRepositoriesService_GetRulesForBranchEmptyUpdateRule(t *testing.T) {
   307  	client, mux, _, teardown := setup()
   308  	defer teardown()
   309  
   310  	mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) {
   311  		testMethod(t, r, "GET")
   312  		fmt.Fprint(w, `[
   313  			{
   314  			  "type": "update"
   315  			}
   316  		]`)
   317  	})
   318  
   319  	ctx := context.Background()
   320  	rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch")
   321  	if err != nil {
   322  		t.Errorf("Repositories.GetRulesForBranch returned error: %v", err)
   323  	}
   324  
   325  	updateRule := NewUpdateRule(nil)
   326  
   327  	want := []*RepositoryRule{
   328  		updateRule,
   329  	}
   330  	if !cmp.Equal(rules, want) {
   331  		t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", Stringify(rules), Stringify(want))
   332  	}
   333  
   334  	const methodName = "GetRulesForBranch"
   335  
   336  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   337  		got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch")
   338  		if got != nil {
   339  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   340  		}
   341  		return resp, err
   342  	})
   343  }
   344  
   345  func TestRepositoriesService_GetAllRulesets(t *testing.T) {
   346  	client, mux, _, teardown := setup()
   347  	defer teardown()
   348  
   349  	mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) {
   350  		testMethod(t, r, "GET")
   351  		fmt.Fprint(w, `[
   352  			{
   353  			  "id": 42,
   354  			  "name": "ruleset",
   355  			  "source_type": "Repository",
   356  			  "source": "o/repo",
   357  			  "enforcement": "enabled"
   358  			},
   359  			{
   360  			  "id": 314,
   361  			  "name": "Another ruleset",
   362  			  "source_type": "Repository",
   363  			  "source": "o/repo",
   364  			  "enforcement": "enabled"
   365  			}
   366  		]`)
   367  	})
   368  
   369  	ctx := context.Background()
   370  	ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false)
   371  	if err != nil {
   372  		t.Errorf("Repositories.GetAllRulesets returned error: %v", err)
   373  	}
   374  
   375  	want := []*Ruleset{
   376  		{
   377  			ID:          Int64(42),
   378  			Name:        "ruleset",
   379  			SourceType:  String("Repository"),
   380  			Source:      "o/repo",
   381  			Enforcement: "enabled",
   382  		},
   383  		{
   384  			ID:          Int64(314),
   385  			Name:        "Another ruleset",
   386  			SourceType:  String("Repository"),
   387  			Source:      "o/repo",
   388  			Enforcement: "enabled",
   389  		},
   390  	}
   391  	if !cmp.Equal(ruleSet, want) {
   392  		t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want)
   393  	}
   394  
   395  	const methodName = "GetAllRulesets"
   396  
   397  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   398  		got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false)
   399  		if got != nil {
   400  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   401  		}
   402  		return resp, err
   403  	})
   404  }
   405  
   406  func TestRepositoriesService_CreateRuleset(t *testing.T) {
   407  	client, mux, _, teardown := setup()
   408  	defer teardown()
   409  
   410  	mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) {
   411  		testMethod(t, r, "POST")
   412  		fmt.Fprint(w, `{
   413  			"id": 42,
   414  			"name": "ruleset",
   415  			"source_type": "Repository",
   416  			"source": "o/repo",
   417  			"enforcement": "enabled"
   418  		}`)
   419  	})
   420  
   421  	ctx := context.Background()
   422  	ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{
   423  		Name:        "ruleset",
   424  		Enforcement: "enabled",
   425  	})
   426  	if err != nil {
   427  		t.Errorf("Repositories.CreateRuleset returned error: %v", err)
   428  	}
   429  
   430  	want := &Ruleset{
   431  		ID:          Int64(42),
   432  		Name:        "ruleset",
   433  		SourceType:  String("Repository"),
   434  		Source:      "o/repo",
   435  		Enforcement: "enabled",
   436  	}
   437  	if !cmp.Equal(ruleSet, want) {
   438  		t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want)
   439  	}
   440  
   441  	const methodName = "CreateRuleset"
   442  
   443  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   444  		got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{})
   445  		if got != nil {
   446  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   447  		}
   448  		return resp, err
   449  	})
   450  }
   451  
   452  func TestRepositoriesService_GetRuleset(t *testing.T) {
   453  	client, mux, _, teardown := setup()
   454  	defer teardown()
   455  
   456  	mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) {
   457  		testMethod(t, r, "GET")
   458  		fmt.Fprint(w, `{
   459  			"id": 42,
   460  			"name": "ruleset",
   461  			"source_type": "Organization",
   462  			"source": "o",
   463  			"enforcement": "enabled"
   464  		}`)
   465  	})
   466  
   467  	ctx := context.Background()
   468  	ruleSet, _, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true)
   469  	if err != nil {
   470  		t.Errorf("Repositories.GetRuleset returned error: %v", err)
   471  	}
   472  
   473  	want := &Ruleset{
   474  		ID:          Int64(42),
   475  		Name:        "ruleset",
   476  		SourceType:  String("Organization"),
   477  		Source:      "o",
   478  		Enforcement: "enabled",
   479  	}
   480  	if !cmp.Equal(ruleSet, want) {
   481  		t.Errorf("Repositories.GetRuleset returned %+v, want %+v", ruleSet, want)
   482  	}
   483  
   484  	const methodName = "GetRuleset"
   485  
   486  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   487  		got, resp, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true)
   488  		if got != nil {
   489  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   490  		}
   491  		return resp, err
   492  	})
   493  }
   494  
   495  func TestRepositoriesService_UpdateRuleset(t *testing.T) {
   496  	client, mux, _, teardown := setup()
   497  	defer teardown()
   498  
   499  	mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) {
   500  		testMethod(t, r, "PUT")
   501  		fmt.Fprint(w, `{
   502  			"id": 42,
   503  			"name": "ruleset",
   504  			"source_type": "Repository",
   505  			"source": "o/repo",
   506  			"enforcement": "enabled"
   507  		}`)
   508  	})
   509  
   510  	ctx := context.Background()
   511  	ruleSet, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, &Ruleset{
   512  		Name:        "ruleset",
   513  		Enforcement: "enabled",
   514  	})
   515  	if err != nil {
   516  		t.Errorf("Repositories.UpdateRuleset returned error: %v", err)
   517  	}
   518  
   519  	want := &Ruleset{
   520  		ID:          Int64(42),
   521  		Name:        "ruleset",
   522  		SourceType:  String("Repository"),
   523  		Source:      "o/repo",
   524  		Enforcement: "enabled",
   525  	}
   526  	if !cmp.Equal(ruleSet, want) {
   527  		t.Errorf("Repositories.UpdateRuleset returned %+v, want %+v", ruleSet, want)
   528  	}
   529  
   530  	const methodName = "UpdateRuleset"
   531  
   532  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   533  		got, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, nil)
   534  		if got != nil {
   535  			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
   536  		}
   537  		return resp, err
   538  	})
   539  }
   540  
   541  func TestRepositoriesService_DeleteRuleset(t *testing.T) {
   542  	client, mux, _, teardown := setup()
   543  	defer teardown()
   544  
   545  	mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) {
   546  		testMethod(t, r, "DELETE")
   547  	})
   548  
   549  	ctx := context.Background()
   550  	_, err := client.Repositories.DeleteRuleset(ctx, "o", "repo", 42)
   551  	if err != nil {
   552  		t.Errorf("Repositories.DeleteRuleset returned error: %v", err)
   553  	}
   554  
   555  	const methodName = "DeleteRuleset"
   556  
   557  	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
   558  		return client.Repositories.DeleteRuleset(ctx, "o", "repo", 42)
   559  	})
   560  }