...

Source file src/edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/predicate/file_test.go

Documentation: edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/predicate

     1  // Copyright 2018 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package predicate
    16  
    17  import (
    18  	"context"
    19  	"regexp"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  
    24  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull"
    25  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull/pulltest"
    26  
    27  	"edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
    28  )
    29  
    30  func TestChangedFiles(t *testing.T) {
    31  	p := &ChangedFiles{
    32  		Paths: []common.Regexp{
    33  			common.NewCompiledRegexp(regexp.MustCompile("app/.*\\.go")),
    34  			common.NewCompiledRegexp(regexp.MustCompile("server/.*\\.go")),
    35  		},
    36  		IgnorePaths: []common.Regexp{
    37  			common.NewCompiledRegexp(regexp.MustCompile(".*/special\\.go")),
    38  		},
    39  	}
    40  
    41  	runFileTests(t, p, []FileTestCase{
    42  		{
    43  			"empty",
    44  			[]*pull.File{},
    45  			&common.PredicateResult{
    46  				Satisfied: false,
    47  				Values:    []string{},
    48  				ConditionsMap: map[string][]string{
    49  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
    50  					"while ignoring": {".*/special\\.go"},
    51  				},
    52  			},
    53  		},
    54  		{
    55  			"onlyMatches",
    56  			[]*pull.File{
    57  				{
    58  					Filename: "app/client.go",
    59  					Status:   pull.FileAdded,
    60  				},
    61  				{
    62  					Filename: "server/server.go",
    63  					Status:   pull.FileModified,
    64  				},
    65  			},
    66  			&common.PredicateResult{
    67  				Satisfied: true,
    68  				Values:    []string{"app/client.go"},
    69  				ConditionsMap: map[string][]string{
    70  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
    71  					"while ignoring": {".*/special\\.go"},
    72  				},
    73  			},
    74  		},
    75  		{
    76  			"someMatches",
    77  			[]*pull.File{
    78  				{
    79  					Filename: "app/client.go",
    80  					Status:   pull.FileAdded,
    81  				},
    82  				{
    83  					Filename: "model/user.go",
    84  					Status:   pull.FileModified,
    85  				},
    86  			},
    87  			&common.PredicateResult{
    88  				Satisfied: true,
    89  				Values:    []string{"app/client.go"},
    90  				ConditionsMap: map[string][]string{
    91  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
    92  					"while ignoring": {".*/special\\.go"},
    93  				},
    94  			},
    95  		},
    96  		{
    97  			"noMatches",
    98  			[]*pull.File{
    99  				{
   100  					Filename: "model/order.go",
   101  					Status:   pull.FileDeleted,
   102  				},
   103  				{
   104  					Filename: "model/user.go",
   105  					Status:   pull.FileModified,
   106  				},
   107  			},
   108  			&common.PredicateResult{
   109  				Satisfied: false,
   110  				Values:    []string{"model/order.go", "model/user.go"},
   111  				ConditionsMap: map[string][]string{
   112  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
   113  					"while ignoring": {".*/special\\.go"},
   114  				},
   115  			},
   116  		},
   117  		{
   118  			"ignoreAll",
   119  			[]*pull.File{
   120  				{
   121  					Filename: "app/special.go",
   122  					Status:   pull.FileDeleted,
   123  				},
   124  				{
   125  					Filename: "server/special.go",
   126  					Status:   pull.FileModified,
   127  				},
   128  			},
   129  			&common.PredicateResult{
   130  				Satisfied: false,
   131  				Values:    []string{"app/special.go", "server/special.go"},
   132  				ConditionsMap: map[string][]string{
   133  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
   134  					"while ignoring": {".*/special\\.go"},
   135  				},
   136  			},
   137  		},
   138  		{
   139  			"ignoreSome",
   140  			[]*pull.File{
   141  				{
   142  					Filename: "app/normal.go",
   143  					Status:   pull.FileDeleted,
   144  				},
   145  				{
   146  					Filename: "server/special.go",
   147  					Status:   pull.FileModified,
   148  				},
   149  			},
   150  			&common.PredicateResult{
   151  				Satisfied: true,
   152  				Values:    []string{"app/normal.go"},
   153  				ConditionsMap: map[string][]string{
   154  					"path patterns":  {"app/.*\\.go", "server/.*\\.go"},
   155  					"while ignoring": {".*/special\\.go"},
   156  				},
   157  			},
   158  		},
   159  	})
   160  }
   161  
   162  func TestOnlyChangedFiles(t *testing.T) {
   163  	p := &OnlyChangedFiles{
   164  		Paths: []common.Regexp{
   165  			common.NewCompiledRegexp(regexp.MustCompile("app/.*\\.go")),
   166  			common.NewCompiledRegexp(regexp.MustCompile("server/.*\\.go")),
   167  		},
   168  	}
   169  
   170  	runFileTests(t, p, []FileTestCase{
   171  		{
   172  			"empty",
   173  			[]*pull.File{},
   174  			&common.PredicateResult{
   175  				Satisfied:       false,
   176  				Values:          []string{},
   177  				ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
   178  			},
   179  		},
   180  		{
   181  			"onlyMatches",
   182  			[]*pull.File{
   183  				{
   184  					Filename: "app/client.go",
   185  					Status:   pull.FileAdded,
   186  				},
   187  				{
   188  					Filename: "server/server.go",
   189  					Status:   pull.FileModified,
   190  				},
   191  			},
   192  			&common.PredicateResult{
   193  				Satisfied:       true,
   194  				Values:          []string{"app/client.go", "server/server.go"},
   195  				ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
   196  			},
   197  		},
   198  		{
   199  			"someMatches",
   200  			[]*pull.File{
   201  				{
   202  					Filename: "app/client.go",
   203  					Status:   pull.FileAdded,
   204  				},
   205  				{
   206  					Filename: "model/user.go",
   207  					Status:   pull.FileModified,
   208  				},
   209  			},
   210  			&common.PredicateResult{
   211  				Satisfied:       false,
   212  				Values:          []string{"model/user.go"},
   213  				ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
   214  			},
   215  		},
   216  		{
   217  			"noMatches",
   218  			[]*pull.File{
   219  				{
   220  					Filename: "model/order.go",
   221  					Status:   pull.FileDeleted,
   222  				},
   223  				{
   224  					Filename: "model/user.go",
   225  					Status:   pull.FileModified,
   226  				},
   227  			},
   228  			&common.PredicateResult{
   229  				Satisfied:       false,
   230  				Values:          []string{"model/order.go"},
   231  				ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
   232  			},
   233  		},
   234  	})
   235  }
   236  
   237  func TestModifiedLines(t *testing.T) {
   238  	p := &ModifiedLines{
   239  		Additions: ComparisonExpr{Op: OpGreaterThan, Value: 100},
   240  		Deletions: ComparisonExpr{Op: OpGreaterThan, Value: 10},
   241  	}
   242  
   243  	runFileTests(t, p, []FileTestCase{
   244  		{
   245  			"empty",
   246  			[]*pull.File{},
   247  			&common.PredicateResult{
   248  				Satisfied:       false,
   249  				Values:          []string{"+0", "-0"},
   250  				ConditionValues: []string{"added lines > 100", "deleted lines > 10"},
   251  			},
   252  		},
   253  		{
   254  			"additions",
   255  			[]*pull.File{
   256  				{Additions: 55},
   257  				{Additions: 10},
   258  				{Additions: 45},
   259  			},
   260  			&common.PredicateResult{
   261  				Satisfied:       true,
   262  				Values:          []string{"+110"},
   263  				ConditionValues: []string{"added lines > 100"},
   264  			},
   265  		},
   266  		{
   267  			"deletions",
   268  			[]*pull.File{
   269  				{Additions: 5},
   270  				{Additions: 10, Deletions: 10},
   271  				{Additions: 5},
   272  				{Deletions: 10},
   273  			},
   274  			&common.PredicateResult{
   275  				Satisfied:       true,
   276  				Values:          []string{"-20"},
   277  				ConditionValues: []string{"deleted lines > 10"},
   278  			},
   279  		},
   280  	})
   281  
   282  	p = &ModifiedLines{
   283  		Total: ComparisonExpr{Op: OpGreaterThan, Value: 100},
   284  	}
   285  
   286  	runFileTests(t, p, []FileTestCase{
   287  		{
   288  			"total",
   289  			[]*pull.File{
   290  				{Additions: 20, Deletions: 20},
   291  				{Additions: 20},
   292  				{Deletions: 20},
   293  				{Additions: 20, Deletions: 20},
   294  			},
   295  			&common.PredicateResult{
   296  				Satisfied:       true,
   297  				Values:          []string{"total 120"},
   298  				ConditionValues: []string{"total modifications > 100"},
   299  			},
   300  		},
   301  	})
   302  }
   303  
   304  func TestComparisonExpr(t *testing.T) {
   305  	tests := map[string]struct {
   306  		Expr   ComparisonExpr
   307  		Value  int64
   308  		Output bool
   309  	}{
   310  		"greaterThanTrue": {
   311  			Expr:   ComparisonExpr{Op: OpGreaterThan, Value: 100},
   312  			Value:  200,
   313  			Output: true,
   314  		},
   315  		"greaterThanFalse": {
   316  			Expr:   ComparisonExpr{Op: OpGreaterThan, Value: 100},
   317  			Value:  50,
   318  			Output: false,
   319  		},
   320  		"lessThanTrue": {
   321  			Expr:   ComparisonExpr{Op: OpLessThan, Value: 100},
   322  			Value:  50,
   323  			Output: true,
   324  		},
   325  		"lessThanFalse": {
   326  			Expr:   ComparisonExpr{Op: OpLessThan, Value: 100},
   327  			Value:  200,
   328  			Output: false,
   329  		},
   330  	}
   331  
   332  	for name, test := range tests {
   333  		t.Run(name, func(t *testing.T) {
   334  			ok := test.Expr.Evaluate(test.Value)
   335  			assert.Equal(t, test.Output, ok, "evaluation was not correct")
   336  		})
   337  	}
   338  
   339  	t.Run("isEmpty", func(t *testing.T) {
   340  		assert.True(t, ComparisonExpr{}.IsEmpty(), "expression was not empty")
   341  		assert.False(t, ComparisonExpr{Op: OpGreaterThan, Value: 100}.IsEmpty(), "expression was empty")
   342  	})
   343  
   344  	parseTests := map[string]struct {
   345  		Input  string
   346  		Output ComparisonExpr
   347  		Err    bool
   348  	}{
   349  		"lessThan": {
   350  			Input:  "<100",
   351  			Output: ComparisonExpr{Op: OpLessThan, Value: 100},
   352  		},
   353  		"greaterThan": {
   354  			Input:  ">100",
   355  			Output: ComparisonExpr{Op: OpGreaterThan, Value: 100},
   356  		},
   357  		"innerSpaces": {
   358  			Input:  "<   35",
   359  			Output: ComparisonExpr{Op: OpLessThan, Value: 35},
   360  		},
   361  		"leadingSpaces": {
   362  			Input:  "   < 35",
   363  			Output: ComparisonExpr{Op: OpLessThan, Value: 35},
   364  		},
   365  		"trailngSpaces": {
   366  			Input:  "< 35   ",
   367  			Output: ComparisonExpr{Op: OpLessThan, Value: 35},
   368  		},
   369  		"invalidOp": {
   370  			Input: "=10",
   371  			Err:   true,
   372  		},
   373  		"invalidValue": {
   374  			Input: "< 10ab",
   375  			Err:   true,
   376  		},
   377  	}
   378  
   379  	for name, test := range parseTests {
   380  		t.Run(name, func(t *testing.T) {
   381  			var expr ComparisonExpr
   382  			err := expr.UnmarshalText([]byte(test.Input))
   383  			if test.Err {
   384  				assert.Error(t, err, "expected error parsing expression, but got nil")
   385  				return
   386  			}
   387  			if assert.NoError(t, err, "unexpected error parsing expression") {
   388  				assert.Equal(t, test.Output, expr, "parsed expression was not correct")
   389  			}
   390  		})
   391  	}
   392  }
   393  
   394  type FileTestCase struct {
   395  	Name                    string
   396  	Files                   []*pull.File
   397  	ExpectedPredicateResult *common.PredicateResult
   398  }
   399  
   400  func runFileTests(t *testing.T, p Predicate, cases []FileTestCase) {
   401  	ctx := context.Background()
   402  
   403  	for _, tc := range cases {
   404  		t.Run(tc.Name, func(t *testing.T) {
   405  			prctx := &pulltest.Context{
   406  				ChangedFilesValue: tc.Files,
   407  			}
   408  
   409  			predicateResult, err := p.Evaluate(ctx, prctx)
   410  			if assert.NoError(t, err, "evaluation failed") {
   411  				assertPredicateResult(t, tc.ExpectedPredicateResult, predicateResult)
   412  			}
   413  		})
   414  	}
   415  }
   416  

View as plain text