github.com/minio/console@v1.4.1/pkg/utils/parity_test.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package utils
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/minio/pkg/v3/ellipses"
    24  )
    25  
    26  func TestGetDivisibleSize(t *testing.T) {
    27  	testCases := []struct {
    28  		totalSizes []uint64
    29  		result     uint64
    30  	}{
    31  		{[]uint64{24, 32, 16}, 8},
    32  		{[]uint64{32, 8, 4}, 4},
    33  		{[]uint64{8, 8, 8}, 8},
    34  		{[]uint64{24}, 24},
    35  	}
    36  
    37  	for _, testCase := range testCases {
    38  		testCase := testCase
    39  		t.Run("", func(_ *testing.T) {
    40  			gotGCD := getDivisibleSize(testCase.totalSizes)
    41  			if testCase.result != gotGCD {
    42  				t.Errorf("Expected %v, got %v", testCase.result, gotGCD)
    43  			}
    44  		})
    45  	}
    46  }
    47  
    48  // Test tests calculating set indexes.
    49  func TestGetSetIndexes(t *testing.T) {
    50  	testCases := []struct {
    51  		args       []string
    52  		totalSizes []uint64
    53  		indexes    [][]uint64
    54  		success    bool
    55  	}{
    56  		// Invalid inputs.
    57  		{
    58  			[]string{"data{1...3}"},
    59  			[]uint64{3},
    60  			nil,
    61  			false,
    62  		},
    63  		{
    64  			[]string{"data/controller1/export{1...2}, data/controller2/export{1...4}, data/controller3/export{1...8}"},
    65  			[]uint64{2, 4, 8},
    66  			nil,
    67  			false,
    68  		},
    69  		{
    70  			[]string{"data{1...17}/export{1...52}"},
    71  			[]uint64{14144},
    72  			nil,
    73  			false,
    74  		},
    75  		// Valid inputs.
    76  		{
    77  			[]string{"data{1...27}"},
    78  			[]uint64{27},
    79  			[][]uint64{{9, 9, 9}},
    80  			true,
    81  		},
    82  		{
    83  			[]string{"http://host{1...3}/data{1...180}"},
    84  			[]uint64{540},
    85  			[][]uint64{{15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}},
    86  			true,
    87  		},
    88  		{
    89  			[]string{"http://host{1...2}.rack{1...4}/data{1...180}"},
    90  			[]uint64{1440},
    91  			[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
    92  			true,
    93  		},
    94  		{
    95  			[]string{"http://host{1...2}/data{1...180}"},
    96  			[]uint64{360},
    97  			[][]uint64{{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}},
    98  			true,
    99  		},
   100  		{
   101  			[]string{"data/controller1/export{1...4}, data/controller2/export{1...8}, data/controller3/export{1...12}"},
   102  			[]uint64{4, 8, 12},
   103  			[][]uint64{{4}, {4, 4}, {4, 4, 4}},
   104  			true,
   105  		},
   106  		{
   107  			[]string{"data{1...64}"},
   108  			[]uint64{64},
   109  			[][]uint64{{16, 16, 16, 16}},
   110  			true,
   111  		},
   112  		{
   113  			[]string{"data{1...24}"},
   114  			[]uint64{24},
   115  			[][]uint64{{12, 12}},
   116  			true,
   117  		},
   118  		{
   119  			[]string{"data/controller{1...11}/export{1...8}"},
   120  			[]uint64{88},
   121  			[][]uint64{{11, 11, 11, 11, 11, 11, 11, 11}},
   122  			true,
   123  		},
   124  		{
   125  			[]string{"data{1...4}"},
   126  			[]uint64{4},
   127  			[][]uint64{{4}},
   128  			true,
   129  		},
   130  		{
   131  			[]string{"data/controller1/export{1...10}, data/controller2/export{1...10}, data/controller3/export{1...10}"},
   132  			[]uint64{10, 10, 10},
   133  			[][]uint64{{10}, {10}, {10}},
   134  			true,
   135  		},
   136  		{
   137  			[]string{"data{1...16}/export{1...52}"},
   138  			[]uint64{832},
   139  			[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
   140  			true,
   141  		},
   142  	}
   143  
   144  	for _, testCase := range testCases {
   145  		testCase := testCase
   146  		t.Run("", func(_ *testing.T) {
   147  			argPatterns := make([]ellipses.ArgPattern, len(testCase.args))
   148  			for i, arg := range testCase.args {
   149  				patterns, err := ellipses.FindEllipsesPatterns(arg)
   150  				if err != nil {
   151  					t.Fatalf("Unexpected failure %s", err)
   152  				}
   153  				argPatterns[i] = patterns
   154  			}
   155  			gotIndexes, err := getSetIndexes(testCase.args, testCase.totalSizes, argPatterns)
   156  			if err != nil && testCase.success {
   157  				t.Errorf("Expected success but failed instead %s", err)
   158  			}
   159  			if err == nil && !testCase.success {
   160  				t.Errorf("Expected failure but passed instead")
   161  			}
   162  			if !reflect.DeepEqual(testCase.indexes, gotIndexes) {
   163  				t.Errorf("Expected %v, got %v", testCase.indexes, gotIndexes)
   164  			}
   165  		})
   166  	}
   167  }
   168  
   169  // Test tests possible parities returned for any input args
   170  func TestPossibleParities(t *testing.T) {
   171  	testCases := []struct {
   172  		arg      string
   173  		parities []string
   174  		success  bool
   175  	}{
   176  		// Tests invalid inputs.
   177  		{
   178  			"...",
   179  			nil,
   180  			false,
   181  		},
   182  		// No range specified.
   183  		{
   184  			"{...}",
   185  			nil,
   186  			false,
   187  		},
   188  		// Invalid range.
   189  		{
   190  			"http://minio{2...3}/export/set{1...0}",
   191  			nil,
   192  			false,
   193  		},
   194  		// Range cannot be smaller than 4 minimum.
   195  		{
   196  			"/export{1..2}",
   197  			nil,
   198  			false,
   199  		},
   200  		// Unsupported characters.
   201  		{
   202  			"/export/test{1...2O}",
   203  			nil,
   204  			false,
   205  		},
   206  		// Tests valid inputs.
   207  		{
   208  			"{1...27}",
   209  			[]string{"EC:4", "EC:3", "EC:2"},
   210  			true,
   211  		},
   212  		{
   213  			"/export/set{1...64}",
   214  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   215  			true,
   216  		},
   217  		// Valid input for distributed setup.
   218  		{
   219  			"http://minio{2...3}/export/set{1...64}",
   220  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   221  			true,
   222  		},
   223  		// Supporting some advanced cases.
   224  		{
   225  			"http://minio{1...64}.mydomain.net/data",
   226  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   227  			true,
   228  		},
   229  		{
   230  			"http://rack{1...4}.mydomain.minio{1...16}/data",
   231  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   232  			true,
   233  		},
   234  		// Supporting kubernetes cases.
   235  		{
   236  			"http://minio{0...15}.mydomain.net/data{0...1}",
   237  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   238  			true,
   239  		},
   240  		// No host regex, just disks.
   241  		{
   242  			"http://server1/data{1...32}",
   243  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   244  			true,
   245  		},
   246  		// No host regex, just disks with two position numerics.
   247  		{
   248  			"http://server1/data{01...32}",
   249  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   250  			true,
   251  		},
   252  		// More than 2 ellipses are supported as well.
   253  		{
   254  			"http://minio{2...3}/export/set{1...64}/test{1...2}",
   255  			[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
   256  			true,
   257  		},
   258  		// More than 1 ellipses per argument for standalone setup.
   259  		{
   260  			"/export{1...10}/disk{1...10}",
   261  			[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
   262  			true,
   263  		},
   264  		// IPv6 ellipses with hexadecimal expansion
   265  		{
   266  			"http://[2001:3984:3989::{1...a}]/disk{1...10}",
   267  			[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
   268  			true,
   269  		},
   270  		// IPv6 ellipses with hexadecimal expansion with 3 position numerics.
   271  		{
   272  			"http://[2001:3984:3989::{001...00a}]/disk{1...10}",
   273  			[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
   274  			true,
   275  		},
   276  	}
   277  
   278  	for _, testCase := range testCases {
   279  		testCase := testCase
   280  		t.Run("", func(_ *testing.T) {
   281  			gotPs, err := PossibleParityValues(testCase.arg)
   282  			if err != nil && testCase.success {
   283  				t.Errorf("Expected success but failed instead %s", err)
   284  			}
   285  			if err == nil && !testCase.success {
   286  				t.Errorf("Expected failure but passed instead")
   287  			}
   288  			if !reflect.DeepEqual(testCase.parities, gotPs) {
   289  				t.Errorf("Expected %v, got %v", testCase.parities, gotPs)
   290  			}
   291  		})
   292  	}
   293  }