github.com/MetalBlockchain/metalgo@v1.11.9/config/config_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package config
     5  
     6  import (
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/spf13/pflag"
    16  	"github.com/spf13/viper"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/MetalBlockchain/metalgo/chains"
    20  	"github.com/MetalBlockchain/metalgo/ids"
    21  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowball"
    22  	"github.com/MetalBlockchain/metalgo/subnets"
    23  )
    24  
    25  const chainConfigFilenameExtention = ".ex"
    26  
    27  func TestGetChainConfigsFromFiles(t *testing.T) {
    28  	tests := map[string]struct {
    29  		configs  map[string]string
    30  		upgrades map[string]string
    31  		expected map[string]chains.ChainConfig
    32  	}{
    33  		"no chain configs": {
    34  			configs:  map[string]string{},
    35  			upgrades: map[string]string{},
    36  			expected: map[string]chains.ChainConfig{},
    37  		},
    38  		"valid chain-id": {
    39  			configs:  map[string]string{"yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp": "hello", "2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm": "world"},
    40  			upgrades: map[string]string{"yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp": "helloUpgrades"},
    41  			expected: func() map[string]chains.ChainConfig {
    42  				m := map[string]chains.ChainConfig{}
    43  				id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
    44  				require.NoError(t, err)
    45  				m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
    46  
    47  				id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
    48  				require.NoError(t, err)
    49  				m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
    50  
    51  				return m
    52  			}(),
    53  		},
    54  		"valid alias": {
    55  			configs:  map[string]string{"C": "hello", "X": "world"},
    56  			upgrades: map[string]string{"C": "upgradess"},
    57  			expected: func() map[string]chains.ChainConfig {
    58  				m := map[string]chains.ChainConfig{}
    59  				m["C"] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("upgradess")}
    60  				m["X"] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
    61  
    62  				return m
    63  			}(),
    64  		},
    65  	}
    66  
    67  	for name, test := range tests {
    68  		t.Run(name, func(t *testing.T) {
    69  			require := require.New(t)
    70  			root := t.TempDir()
    71  			configJSON := fmt.Sprintf(`{%q: %q}`, ChainConfigDirKey, root)
    72  			configFile := setupConfigJSON(t, root, configJSON)
    73  			chainsDir := root
    74  			// Create custom configs
    75  			for key, value := range test.configs {
    76  				chainDir := filepath.Join(chainsDir, key)
    77  				setupFile(t, chainDir, chainConfigFileName+chainConfigFilenameExtention, value)
    78  			}
    79  			for key, value := range test.upgrades {
    80  				chainDir := filepath.Join(chainsDir, key)
    81  				setupFile(t, chainDir, chainUpgradeFileName+chainConfigFilenameExtention, value)
    82  			}
    83  
    84  			v := setupViper(configFile)
    85  
    86  			// Parse config
    87  			require.Equal(root, v.GetString(ChainConfigDirKey))
    88  			chainConfigs, err := getChainConfigs(v)
    89  			require.NoError(err)
    90  			require.Equal(test.expected, chainConfigs)
    91  		})
    92  	}
    93  }
    94  
    95  func TestGetChainConfigsDirNotExist(t *testing.T) {
    96  	tests := map[string]struct {
    97  		structure   string
    98  		file        map[string]string
    99  		expectedErr error
   100  		expected    map[string]chains.ChainConfig
   101  	}{
   102  		"cdir not exist": {
   103  			structure:   "/",
   104  			file:        map[string]string{"config.ex": "noeffect"},
   105  			expectedErr: errCannotReadDirectory,
   106  			expected:    nil,
   107  		},
   108  		"cdir is file ": {
   109  			structure:   "/",
   110  			file:        map[string]string{"cdir": "noeffect"},
   111  			expectedErr: errCannotReadDirectory,
   112  			expected:    nil,
   113  		},
   114  		"chain subdir not exist": {
   115  			structure:   "/cdir/",
   116  			file:        map[string]string{"config.ex": "noeffect"},
   117  			expectedErr: nil,
   118  			expected:    map[string]chains.ChainConfig{},
   119  		},
   120  		"full structure": {
   121  			structure:   "/cdir/C/",
   122  			file:        map[string]string{"config.ex": "hello"},
   123  			expectedErr: nil,
   124  			expected:    map[string]chains.ChainConfig{"C": {Config: []byte("hello"), Upgrade: []byte(nil)}},
   125  		},
   126  	}
   127  
   128  	for name, test := range tests {
   129  		t.Run(name, func(t *testing.T) {
   130  			require := require.New(t)
   131  			root := t.TempDir()
   132  			chainConfigDir := filepath.Join(root, "cdir")
   133  			configJSON := fmt.Sprintf(`{%q: %q}`, ChainConfigDirKey, chainConfigDir)
   134  			configFile := setupConfigJSON(t, root, configJSON)
   135  
   136  			dirToCreate := filepath.Join(root, test.structure)
   137  			require.NoError(os.MkdirAll(dirToCreate, 0o700))
   138  
   139  			for key, value := range test.file {
   140  				setupFile(t, dirToCreate, key, value)
   141  			}
   142  			v := setupViper(configFile)
   143  
   144  			// Parse config
   145  			require.Equal(chainConfigDir, v.GetString(ChainConfigDirKey))
   146  
   147  			// don't read with getConfigFromViper since it's very slow.
   148  			chainConfigs, err := getChainConfigs(v)
   149  			require.ErrorIs(err, test.expectedErr)
   150  			require.Equal(test.expected, chainConfigs)
   151  		})
   152  	}
   153  }
   154  
   155  func TestSetChainConfigDefaultDir(t *testing.T) {
   156  	require := require.New(t)
   157  	root := t.TempDir()
   158  	// changes internal package variable, since using defaultDir (under user home) is risky.
   159  	defaultChainConfigDir = filepath.Join(root, "cdir")
   160  	configFilePath := setupConfigJSON(t, root, "{}")
   161  
   162  	v := setupViper(configFilePath)
   163  	require.Equal(defaultChainConfigDir, v.GetString(ChainConfigDirKey))
   164  
   165  	chainsDir := filepath.Join(defaultChainConfigDir, "C")
   166  	setupFile(t, chainsDir, chainConfigFileName+chainConfigFilenameExtention, "helloworld")
   167  	chainConfigs, err := getChainConfigs(v)
   168  	require.NoError(err)
   169  	expected := map[string]chains.ChainConfig{"C": {Config: []byte("helloworld"), Upgrade: []byte(nil)}}
   170  	require.Equal(expected, chainConfigs)
   171  }
   172  
   173  func TestGetChainConfigsFromFlags(t *testing.T) {
   174  	tests := map[string]struct {
   175  		fullConfigs map[string]chains.ChainConfig
   176  		expected    map[string]chains.ChainConfig
   177  	}{
   178  		"no chain configs": {
   179  			fullConfigs: map[string]chains.ChainConfig{},
   180  			expected:    map[string]chains.ChainConfig{},
   181  		},
   182  		"valid chain-id": {
   183  			fullConfigs: func() map[string]chains.ChainConfig {
   184  				m := map[string]chains.ChainConfig{}
   185  				id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
   186  				require.NoError(t, err)
   187  				m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
   188  
   189  				id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
   190  				require.NoError(t, err)
   191  				m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
   192  
   193  				return m
   194  			}(),
   195  			expected: func() map[string]chains.ChainConfig {
   196  				m := map[string]chains.ChainConfig{}
   197  				id1, err := ids.FromString("yH8D7ThNJkxmtkuv2jgBa4P1Rn3Qpr4pPr7QYNfcdoS6k6HWp")
   198  				require.NoError(t, err)
   199  				m[id1.String()] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("helloUpgrades")}
   200  
   201  				id2, err := ids.FromString("2JVSBoinj9C2J33VntvzYtVJNZdN2NKiwwKjcumHUWEb5DbBrm")
   202  				require.NoError(t, err)
   203  				m[id2.String()] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
   204  
   205  				return m
   206  			}(),
   207  		},
   208  		"valid alias": {
   209  			fullConfigs: map[string]chains.ChainConfig{
   210  				"C": {Config: []byte("hello"), Upgrade: []byte("upgradess")},
   211  				"X": {Config: []byte("world"), Upgrade: []byte(nil)},
   212  			},
   213  			expected: func() map[string]chains.ChainConfig {
   214  				m := map[string]chains.ChainConfig{}
   215  				m["C"] = chains.ChainConfig{Config: []byte("hello"), Upgrade: []byte("upgradess")}
   216  				m["X"] = chains.ChainConfig{Config: []byte("world"), Upgrade: []byte(nil)}
   217  
   218  				return m
   219  			}(),
   220  		},
   221  	}
   222  
   223  	for name, test := range tests {
   224  		t.Run(name, func(t *testing.T) {
   225  			require := require.New(t)
   226  			jsonMaps, err := json.Marshal(test.fullConfigs)
   227  			require.NoError(err)
   228  			encodedFileContent := base64.StdEncoding.EncodeToString(jsonMaps)
   229  
   230  			// build viper config
   231  			v := setupViperFlags()
   232  			v.Set(ChainConfigContentKey, encodedFileContent)
   233  
   234  			// Parse config
   235  			chainConfigs, err := getChainConfigs(v)
   236  			require.NoError(err)
   237  			require.Equal(test.expected, chainConfigs)
   238  		})
   239  	}
   240  }
   241  
   242  func TestGetVMAliasesFromFile(t *testing.T) {
   243  	tests := map[string]struct {
   244  		givenJSON   string
   245  		expected    map[ids.ID][]string
   246  		expectedErr error
   247  	}{
   248  		"wrong vm id": {
   249  			givenJSON:   `{"wrongVmId": ["vm1","vm2"]}`,
   250  			expected:    nil,
   251  			expectedErr: errUnmarshalling,
   252  		},
   253  		"vm id": {
   254  			givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"],
   255  										"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU": ["vm3", "vm4"] }`,
   256  			expected: func() map[ids.ID][]string {
   257  				m := map[ids.ID][]string{}
   258  				id1, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   259  				id2, _ := ids.FromString("Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU")
   260  				m[id1] = []string{"vm1", "vm2"}
   261  				m[id2] = []string{"vm3", "vm4"}
   262  				return m
   263  			}(),
   264  			expectedErr: nil,
   265  		},
   266  	}
   267  
   268  	for name, test := range tests {
   269  		t.Run(name, func(t *testing.T) {
   270  			require := require.New(t)
   271  			root := t.TempDir()
   272  			aliasPath := filepath.Join(root, "aliases.json")
   273  			configJSON := fmt.Sprintf(`{%q: %q}`, VMAliasesFileKey, aliasPath)
   274  			configFilePath := setupConfigJSON(t, root, configJSON)
   275  			setupFile(t, root, "aliases.json", test.givenJSON)
   276  			v := setupViper(configFilePath)
   277  			vmAliases, err := getVMAliases(v)
   278  			require.ErrorIs(err, test.expectedErr)
   279  			require.Equal(test.expected, vmAliases)
   280  		})
   281  	}
   282  }
   283  
   284  func TestGetVMAliasesFromFlag(t *testing.T) {
   285  	tests := map[string]struct {
   286  		givenJSON   string
   287  		expected    map[ids.ID][]string
   288  		expectedErr error
   289  	}{
   290  		"wrong vm id": {
   291  			givenJSON:   `{"wrongVmId": ["vm1","vm2"]}`,
   292  			expected:    nil,
   293  			expectedErr: errUnmarshalling,
   294  		},
   295  		"vm id": {
   296  			givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"],
   297  										"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU": ["vm3", "vm4"] }`,
   298  			expected: func() map[ids.ID][]string {
   299  				m := map[ids.ID][]string{}
   300  				id1, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   301  				id2, _ := ids.FromString("Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU")
   302  				m[id1] = []string{"vm1", "vm2"}
   303  				m[id2] = []string{"vm3", "vm4"}
   304  				return m
   305  			}(),
   306  			expectedErr: nil,
   307  		},
   308  	}
   309  
   310  	for name, test := range tests {
   311  		t.Run(name, func(t *testing.T) {
   312  			require := require.New(t)
   313  			encodedFileContent := base64.StdEncoding.EncodeToString([]byte(test.givenJSON))
   314  
   315  			// build viper config
   316  			v := setupViperFlags()
   317  			v.Set(VMAliasesContentKey, encodedFileContent)
   318  
   319  			vmAliases, err := getVMAliases(v)
   320  			require.ErrorIs(err, test.expectedErr)
   321  			require.Equal(test.expected, vmAliases)
   322  		})
   323  	}
   324  }
   325  
   326  func TestGetVMAliasesDefaultDir(t *testing.T) {
   327  	require := require.New(t)
   328  	root := t.TempDir()
   329  	// changes internal package variable, since using defaultDir (under user home) is risky.
   330  	defaultVMAliasFilePath = filepath.Join(root, "aliases.json")
   331  	configFilePath := setupConfigJSON(t, root, "{}")
   332  
   333  	v := setupViper(configFilePath)
   334  	require.Equal(defaultVMAliasFilePath, v.GetString(VMAliasesFileKey))
   335  
   336  	setupFile(t, root, "aliases.json", `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": ["vm1","vm2"]}`)
   337  	vmAliases, err := getVMAliases(v)
   338  	require.NoError(err)
   339  
   340  	expected := map[ids.ID][]string{}
   341  	id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   342  	expected[id] = []string{"vm1", "vm2"}
   343  	require.Equal(expected, vmAliases)
   344  }
   345  
   346  func TestGetVMAliasesDirNotExists(t *testing.T) {
   347  	require := require.New(t)
   348  	root := t.TempDir()
   349  	aliasPath := "/not/exists"
   350  	// set it explicitly
   351  	configJSON := fmt.Sprintf(`{%q: %q}`, VMAliasesFileKey, aliasPath)
   352  	configFilePath := setupConfigJSON(t, root, configJSON)
   353  	v := setupViper(configFilePath)
   354  	vmAliases, err := getVMAliases(v)
   355  	require.ErrorIs(err, errFileDoesNotExist)
   356  	require.Nil(vmAliases)
   357  
   358  	// do not set it explicitly
   359  	configJSON = "{}"
   360  	configFilePath = setupConfigJSON(t, root, configJSON)
   361  	v = setupViper(configFilePath)
   362  	vmAliases, err = getVMAliases(v)
   363  	require.Nil(vmAliases)
   364  	require.NoError(err)
   365  }
   366  
   367  func TestGetSubnetConfigsFromFile(t *testing.T) {
   368  	subnetID, err := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   369  	require.NoError(t, err)
   370  
   371  	tests := map[string]struct {
   372  		fileName    string
   373  		givenJSON   string
   374  		testF       func(*require.Assertions, map[ids.ID]subnets.Config)
   375  		expectedErr error
   376  	}{
   377  		"wrong config": {
   378  			fileName:  "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
   379  			givenJSON: `thisisnotjson`,
   380  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   381  				require.Nil(given)
   382  			},
   383  			expectedErr: errUnmarshalling,
   384  		},
   385  		"subnet is not tracked": {
   386  			fileName:  "Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU.json",
   387  			givenJSON: `{"validatorOnly": true}`,
   388  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   389  				require.Empty(given)
   390  			},
   391  			expectedErr: nil,
   392  		},
   393  		"wrong extension": {
   394  			fileName:  "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.yaml",
   395  			givenJSON: `{"validatorOnly": true}`,
   396  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   397  				require.Empty(given)
   398  			},
   399  			expectedErr: nil,
   400  		},
   401  		"invalid consensus parameters": {
   402  			fileName:  "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
   403  			givenJSON: `{"consensusParameters":{"k": 111, "alphaPreference":1234} }`,
   404  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   405  				require.Nil(given)
   406  			},
   407  			expectedErr: snowball.ErrParametersInvalid,
   408  		},
   409  		"correct config": {
   410  			fileName:  "2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i.json",
   411  			givenJSON: `{"validatorOnly": true, "consensusParameters":{"alphaConfidence":16} }`,
   412  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   413  				id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   414  				config, ok := given[id]
   415  				require.True(ok)
   416  
   417  				require.True(config.ValidatorOnly)
   418  				require.Equal(16, config.ConsensusParameters.AlphaConfidence)
   419  				// must still respect defaults
   420  				require.Equal(20, config.ConsensusParameters.K)
   421  			},
   422  			expectedErr: nil,
   423  		},
   424  	}
   425  
   426  	for name, test := range tests {
   427  		t.Run(name, func(t *testing.T) {
   428  			require := require.New(t)
   429  
   430  			root := t.TempDir()
   431  			subnetPath := filepath.Join(root, "subnets")
   432  
   433  			configJSON := fmt.Sprintf(`{%q: %q}`, SubnetConfigDirKey, subnetPath)
   434  			configFilePath := setupConfigJSON(t, root, configJSON)
   435  
   436  			setupFile(t, subnetPath, test.fileName, test.givenJSON)
   437  
   438  			v := setupViper(configFilePath)
   439  			subnetConfigs, err := getSubnetConfigs(v, []ids.ID{subnetID})
   440  			require.ErrorIs(err, test.expectedErr)
   441  			if test.expectedErr != nil {
   442  				return
   443  			}
   444  			test.testF(require, subnetConfigs)
   445  		})
   446  	}
   447  }
   448  
   449  func TestGetSubnetConfigsFromFlags(t *testing.T) {
   450  	subnetID, err := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   451  	require.NoError(t, err)
   452  
   453  	tests := map[string]struct {
   454  		givenJSON   string
   455  		testF       func(*require.Assertions, map[ids.ID]subnets.Config)
   456  		expectedErr error
   457  	}{
   458  		"no configs": {
   459  			givenJSON: `{}`,
   460  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   461  				require.Empty(given)
   462  			},
   463  			expectedErr: nil,
   464  		},
   465  		"entry with no config": {
   466  			givenJSON: `{"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i":{}}`,
   467  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   468  				require.Len(given, 1)
   469  				id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   470  				config, ok := given[id]
   471  				require.True(ok)
   472  				// should respect defaults
   473  				require.Equal(20, config.ConsensusParameters.K)
   474  			},
   475  			expectedErr: nil,
   476  		},
   477  		"subnet is not tracked": {
   478  			givenJSON: `{"Gmt4fuNsGJAd2PX86LBvycGaBpgCYKbuULdCLZs3SEs1Jx1LU":{"validatorOnly":true}}`,
   479  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   480  				require.Empty(given)
   481  			},
   482  			expectedErr: nil,
   483  		},
   484  		"invalid consensus parameters": {
   485  			givenJSON: `{
   486  				"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
   487  					"consensusParameters": {
   488  						"k": 111,
   489  						"alphaPreference": 1234
   490  					}
   491  				}
   492  			}`,
   493  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   494  				require.Empty(given)
   495  			},
   496  			expectedErr: snowball.ErrParametersInvalid,
   497  		},
   498  		"correct config": {
   499  			givenJSON: `{
   500  				"2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i": {
   501  					"consensusParameters": {
   502  						"k": 30,
   503  						"alphaPreference": 16,
   504  						"alphaConfidence": 20
   505  					},
   506  					"validatorOnly": true
   507  				}
   508  			}`,
   509  			testF: func(require *require.Assertions, given map[ids.ID]subnets.Config) {
   510  				id, _ := ids.FromString("2Ctt6eGAeo4MLqTmGa7AdRecuVMPGWEX9wSsCLBYrLhX4a394i")
   511  				config, ok := given[id]
   512  				require.True(ok)
   513  				require.True(config.ValidatorOnly)
   514  				require.Equal(16, config.ConsensusParameters.AlphaPreference)
   515  				require.Equal(20, config.ConsensusParameters.AlphaConfidence)
   516  				require.Equal(30, config.ConsensusParameters.K)
   517  				// must still respect defaults
   518  				require.Equal(256, config.ConsensusParameters.MaxOutstandingItems)
   519  			},
   520  			expectedErr: nil,
   521  		},
   522  	}
   523  
   524  	for name, test := range tests {
   525  		t.Run(name, func(t *testing.T) {
   526  			require := require.New(t)
   527  
   528  			encodedFileContent := base64.StdEncoding.EncodeToString([]byte(test.givenJSON))
   529  
   530  			// build viper config
   531  			v := setupViperFlags()
   532  			v.Set(SubnetConfigContentKey, encodedFileContent)
   533  
   534  			subnetConfigs, err := getSubnetConfigs(v, []ids.ID{subnetID})
   535  			require.ErrorIs(err, test.expectedErr)
   536  			if test.expectedErr != nil {
   537  				return
   538  			}
   539  			test.testF(require, subnetConfigs)
   540  		})
   541  	}
   542  }
   543  
   544  // setups config json file and writes content
   545  func setupConfigJSON(t *testing.T, rootPath string, value string) string {
   546  	configFilePath := filepath.Join(rootPath, "config.json")
   547  	require.NoError(t, os.WriteFile(configFilePath, []byte(value), 0o600))
   548  	return configFilePath
   549  }
   550  
   551  // setups file creates necessary path and writes value to it.
   552  func setupFile(t *testing.T, path string, fileName string, value string) {
   553  	require := require.New(t)
   554  
   555  	require.NoError(os.MkdirAll(path, 0o700))
   556  	filePath := filepath.Join(path, fileName)
   557  	require.NoError(os.WriteFile(filePath, []byte(value), 0o600))
   558  }
   559  
   560  func setupViperFlags() *viper.Viper {
   561  	v := viper.New()
   562  	fs := BuildFlagSet()
   563  	pflag.Parse()
   564  	if err := v.BindPFlags(fs); err != nil {
   565  		log.Fatal(err)
   566  	}
   567  	return v
   568  }
   569  
   570  func setupViper(configFilePath string) *viper.Viper {
   571  	v := setupViperFlags()
   572  	v.SetConfigFile(configFilePath)
   573  	if err := v.ReadInConfig(); err != nil {
   574  		log.Fatal(err)
   575  	}
   576  	return v
   577  }