github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/ruler/mapper_test.go (about)

     1  package ruler
     2  
     3  import (
     4  	"net/url"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/go-kit/log"
     9  	"github.com/go-kit/log/level"
    10  	"github.com/prometheus/prometheus/pkg/rulefmt"
    11  	"github.com/spf13/afero"
    12  	"github.com/stretchr/testify/require"
    13  	"gopkg.in/yaml.v3"
    14  )
    15  
    16  var (
    17  	testUser = "user1"
    18  
    19  	fileOneEncoded = url.PathEscape("file /one")
    20  	fileTwoEncoded = url.PathEscape("file /two")
    21  
    22  	fileOnePath = "/rules/user1/" + fileOneEncoded
    23  	fileTwoPath = "/rules/user1/" + fileTwoEncoded
    24  
    25  	specialCharFile        = "+A_/ReallyStrange<>NAME:SPACE/?"
    26  	specialCharFileEncoded = url.PathEscape(specialCharFile)
    27  	specialCharFilePath    = "/rules/user1/" + specialCharFileEncoded
    28  
    29  	initialRuleSet           map[string][]rulefmt.RuleGroup
    30  	outOfOrderRuleSet        map[string][]rulefmt.RuleGroup
    31  	updatedRuleSet           map[string][]rulefmt.RuleGroup
    32  	twoFilesRuleSet          map[string][]rulefmt.RuleGroup
    33  	twoFilesUpdatedRuleSet   map[string][]rulefmt.RuleGroup
    34  	twoFilesDeletedRuleSet   map[string][]rulefmt.RuleGroup
    35  	specialCharactersRuleSet map[string][]rulefmt.RuleGroup
    36  )
    37  
    38  func setupRuleSets() {
    39  	recordNode := yaml.Node{}
    40  	recordNode.SetString("example_rule")
    41  	exprNode := yaml.Node{}
    42  	exprNode.SetString("example_expr")
    43  	recordNodeUpdated := yaml.Node{}
    44  	recordNodeUpdated.SetString("example_ruleupdated")
    45  	exprNodeUpdated := yaml.Node{}
    46  	exprNodeUpdated.SetString("example_exprupdated")
    47  	initialRuleSet = map[string][]rulefmt.RuleGroup{
    48  		"file /one": {
    49  			{
    50  				Name: "rulegroup_one",
    51  				Rules: []rulefmt.RuleNode{
    52  					{
    53  						Record: recordNode,
    54  						Expr:   exprNode,
    55  					},
    56  				},
    57  			},
    58  			{
    59  				Name: "rulegroup_two",
    60  				Rules: []rulefmt.RuleNode{
    61  					{
    62  						Record: recordNode,
    63  						Expr:   exprNode,
    64  					},
    65  				},
    66  			},
    67  		},
    68  	}
    69  	outOfOrderRuleSet = map[string][]rulefmt.RuleGroup{
    70  		"file /one": {
    71  			{
    72  				Name: "rulegroup_two",
    73  				Rules: []rulefmt.RuleNode{
    74  					{
    75  						Record: recordNode,
    76  						Expr:   exprNode,
    77  					},
    78  				},
    79  			},
    80  			{
    81  				Name: "rulegroup_one",
    82  				Rules: []rulefmt.RuleNode{
    83  					{
    84  						Record: recordNode,
    85  						Expr:   exprNode,
    86  					},
    87  				},
    88  			},
    89  		},
    90  	}
    91  	updatedRuleSet = map[string][]rulefmt.RuleGroup{
    92  		"file /one": {
    93  			{
    94  				Name: "rulegroup_one",
    95  				Rules: []rulefmt.RuleNode{
    96  					{
    97  						Record: recordNode,
    98  						Expr:   exprNode,
    99  					},
   100  				},
   101  			},
   102  			{
   103  				Name: "rulegroup_two",
   104  				Rules: []rulefmt.RuleNode{
   105  					{
   106  						Record: recordNode,
   107  						Expr:   exprNode,
   108  					},
   109  				},
   110  			},
   111  			{
   112  				Name: "rulegroup_three",
   113  				Rules: []rulefmt.RuleNode{
   114  					{
   115  						Record: recordNode,
   116  						Expr:   exprNode,
   117  					},
   118  				},
   119  			},
   120  		},
   121  	}
   122  	twoFilesRuleSet = map[string][]rulefmt.RuleGroup{
   123  		"file /one": {
   124  			{
   125  				Name: "rulegroup_one",
   126  				Rules: []rulefmt.RuleNode{
   127  					{
   128  						Record: recordNode,
   129  						Expr:   exprNode,
   130  					},
   131  				},
   132  			},
   133  			{
   134  				Name: "rulegroup_two",
   135  				Rules: []rulefmt.RuleNode{
   136  					{
   137  						Record: recordNode,
   138  						Expr:   exprNode,
   139  					},
   140  				},
   141  			},
   142  		},
   143  		"file /two": {
   144  			{
   145  				Name: "rulegroup_one",
   146  				Rules: []rulefmt.RuleNode{
   147  					{
   148  						Record: recordNode,
   149  						Expr:   exprNode,
   150  					},
   151  				},
   152  			},
   153  		},
   154  	}
   155  	twoFilesUpdatedRuleSet = map[string][]rulefmt.RuleGroup{
   156  		"file /one": {
   157  			{
   158  				Name: "rulegroup_one",
   159  				Rules: []rulefmt.RuleNode{
   160  					{
   161  						Record: recordNode,
   162  						Expr:   exprNode,
   163  					},
   164  				},
   165  			},
   166  			{
   167  				Name: "rulegroup_two",
   168  				Rules: []rulefmt.RuleNode{
   169  					{
   170  						Record: recordNode,
   171  						Expr:   exprNode,
   172  					},
   173  				},
   174  			},
   175  		},
   176  		"file /two": {
   177  			{
   178  				Name: "rulegroup_one",
   179  				Rules: []rulefmt.RuleNode{
   180  					{
   181  						Record: recordNodeUpdated,
   182  						Expr:   exprNodeUpdated,
   183  					},
   184  				},
   185  			},
   186  		},
   187  	}
   188  	twoFilesDeletedRuleSet = map[string][]rulefmt.RuleGroup{
   189  		"file /one": {
   190  			{
   191  				Name: "rulegroup_one",
   192  				Rules: []rulefmt.RuleNode{
   193  					{
   194  						Record: recordNode,
   195  						Expr:   exprNode,
   196  					},
   197  				},
   198  			},
   199  			{
   200  				Name: "rulegroup_two",
   201  				Rules: []rulefmt.RuleNode{
   202  					{
   203  						Record: recordNode,
   204  						Expr:   exprNode,
   205  					},
   206  				},
   207  			},
   208  		},
   209  	}
   210  	specialCharactersRuleSet = map[string][]rulefmt.RuleGroup{
   211  		specialCharFile: {
   212  			{
   213  				Name: "rulegroup_one",
   214  				Rules: []rulefmt.RuleNode{
   215  					{
   216  						Record: recordNode,
   217  						Expr:   exprNode,
   218  					},
   219  				},
   220  			},
   221  		},
   222  	}
   223  }
   224  
   225  func Test_mapper_MapRules(t *testing.T) {
   226  	l := log.NewLogfmtLogger(os.Stdout)
   227  	l = level.NewFilter(l, level.AllowInfo())
   228  	setupRuleSets()
   229  	m := &mapper{
   230  		Path:   "/rules",
   231  		FS:     afero.NewMemMapFs(),
   232  		logger: l,
   233  	}
   234  
   235  	t.Run("basic rulegroup", func(t *testing.T) {
   236  		updated, files, err := m.MapRules(testUser, initialRuleSet)
   237  		require.True(t, updated)
   238  		require.Len(t, files, 1)
   239  		require.Equal(t, fileOnePath, files[0])
   240  		require.NoError(t, err)
   241  
   242  		exists, err := afero.Exists(m.FS, fileOnePath)
   243  		require.True(t, exists)
   244  		require.NoError(t, err)
   245  	})
   246  
   247  	t.Run("identical rulegroup", func(t *testing.T) {
   248  		updated, files, err := m.MapRules(testUser, initialRuleSet)
   249  		require.False(t, updated)
   250  		require.Len(t, files, 1)
   251  		require.NoError(t, err)
   252  
   253  		exists, err := afero.Exists(m.FS, fileOnePath)
   254  		require.True(t, exists)
   255  		require.NoError(t, err)
   256  	})
   257  
   258  	t.Run("out of order identical rulegroup", func(t *testing.T) {
   259  		updated, files, err := m.MapRules(testUser, outOfOrderRuleSet)
   260  		require.False(t, updated)
   261  		require.Len(t, files, 1)
   262  		require.NoError(t, err)
   263  
   264  		exists, err := afero.Exists(m.FS, fileOnePath)
   265  		require.True(t, exists)
   266  		require.NoError(t, err)
   267  	})
   268  
   269  	t.Run("updated rulegroup", func(t *testing.T) {
   270  		updated, files, err := m.MapRules(testUser, updatedRuleSet)
   271  		require.True(t, updated)
   272  		require.Len(t, files, 1)
   273  		require.Equal(t, fileOnePath, files[0])
   274  		require.NoError(t, err)
   275  
   276  		exists, err := afero.Exists(m.FS, fileOnePath)
   277  		require.True(t, exists)
   278  		require.NoError(t, err)
   279  	})
   280  }
   281  
   282  func Test_mapper_MapRulesMultipleFiles(t *testing.T) {
   283  	l := log.NewLogfmtLogger(os.Stdout)
   284  	l = level.NewFilter(l, level.AllowInfo())
   285  	setupRuleSets()
   286  	m := &mapper{
   287  		Path:   "/rules",
   288  		FS:     afero.NewMemMapFs(),
   289  		logger: l,
   290  	}
   291  
   292  	t.Run("basic rulegroup", func(t *testing.T) {
   293  		updated, files, err := m.MapRules(testUser, initialRuleSet)
   294  		require.True(t, updated)
   295  		require.Len(t, files, 1)
   296  		require.Equal(t, fileOnePath, files[0])
   297  		require.NoError(t, err)
   298  
   299  		exists, err := afero.Exists(m.FS, fileOnePath)
   300  		require.True(t, exists)
   301  		require.NoError(t, err)
   302  	})
   303  
   304  	t.Run("add a file", func(t *testing.T) {
   305  		updated, files, err := m.MapRules(testUser, twoFilesRuleSet)
   306  		require.True(t, updated)
   307  		require.Len(t, files, 2)
   308  		require.True(t, sliceContains(t, fileOnePath, files))
   309  		require.True(t, sliceContains(t, fileTwoPath, files))
   310  		require.NoError(t, err)
   311  
   312  		exists, err := afero.Exists(m.FS, fileOnePath)
   313  		require.True(t, exists)
   314  		require.NoError(t, err)
   315  		exists, err = afero.Exists(m.FS, fileTwoPath)
   316  		require.True(t, exists)
   317  		require.NoError(t, err)
   318  	})
   319  
   320  	t.Run("update one file", func(t *testing.T) {
   321  		updated, files, err := m.MapRules(testUser, twoFilesUpdatedRuleSet)
   322  		require.True(t, updated)
   323  		require.Len(t, files, 2)
   324  		require.True(t, sliceContains(t, fileOnePath, files))
   325  		require.True(t, sliceContains(t, fileTwoPath, files))
   326  		require.NoError(t, err)
   327  
   328  		exists, err := afero.Exists(m.FS, fileOnePath)
   329  		require.True(t, exists)
   330  		require.NoError(t, err)
   331  		exists, err = afero.Exists(m.FS, fileTwoPath)
   332  		require.True(t, exists)
   333  		require.NoError(t, err)
   334  	})
   335  
   336  	t.Run("delete one file", func(t *testing.T) {
   337  		updated, files, err := m.MapRules(testUser, twoFilesDeletedRuleSet)
   338  		require.True(t, updated)
   339  		require.Len(t, files, 1)
   340  		require.Equal(t, fileOnePath, files[0])
   341  		require.NoError(t, err)
   342  
   343  		exists, err := afero.Exists(m.FS, fileOnePath)
   344  		require.True(t, exists)
   345  		require.NoError(t, err)
   346  		exists, err = afero.Exists(m.FS, fileTwoPath)
   347  		require.False(t, exists)
   348  		require.NoError(t, err)
   349  	})
   350  
   351  }
   352  
   353  func Test_mapper_MapRulesSpecialCharNamespace(t *testing.T) {
   354  	l := log.NewLogfmtLogger(os.Stdout)
   355  	l = level.NewFilter(l, level.AllowInfo())
   356  	setupRuleSets()
   357  	m := &mapper{
   358  		Path:   "/rules",
   359  		FS:     afero.NewMemMapFs(),
   360  		logger: l,
   361  	}
   362  
   363  	t.Run("create special characters rulegroup", func(t *testing.T) {
   364  		updated, files, err := m.MapRules(testUser, specialCharactersRuleSet)
   365  		require.NoError(t, err)
   366  		require.True(t, updated)
   367  		require.Len(t, files, 1)
   368  		require.Equal(t, specialCharFilePath, files[0])
   369  
   370  		exists, err := afero.Exists(m.FS, specialCharFilePath)
   371  		require.NoError(t, err)
   372  		require.True(t, exists)
   373  	})
   374  
   375  	t.Run("delete special characters rulegroup", func(t *testing.T) {
   376  		updated, files, err := m.MapRules(testUser, map[string][]rulefmt.RuleGroup{})
   377  		require.NoError(t, err)
   378  		require.True(t, updated)
   379  		require.Len(t, files, 0)
   380  
   381  		exists, err := afero.Exists(m.FS, specialCharFilePath)
   382  		require.NoError(t, err)
   383  		require.False(t, exists)
   384  	})
   385  }
   386  
   387  func sliceContains(t *testing.T, find string, in []string) bool {
   388  	t.Helper()
   389  
   390  	for _, s := range in {
   391  		if find == s {
   392  			return true
   393  		}
   394  	}
   395  
   396  	return false
   397  }