github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/config/toml_test.go (about)

     1  package config
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"path/filepath"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/gnolang/gno/tm2/pkg/testutils"
    12  	"github.com/pelletier/go-toml"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func ensureFiles(t *testing.T, rootDir string, files ...string) {
    18  	t.Helper()
    19  
    20  	for _, f := range files {
    21  		p := filepath.Join(rootDir, f)
    22  		_, err := os.Stat(p)
    23  		assert.Nil(t, err, p)
    24  	}
    25  }
    26  
    27  func TestEnsureRoot(t *testing.T) {
    28  	t.Parallel()
    29  
    30  	// setup temp dir for test
    31  	tmpDir := t.TempDir()
    32  
    33  	// create root dir
    34  	throwaway := DefaultConfig()
    35  	throwaway.SetRootDir(tmpDir)
    36  	require.NoError(t, throwaway.EnsureDirs())
    37  
    38  	configPath := filepath.Join(tmpDir, defaultConfigPath)
    39  	require.NoError(t, WriteConfigFile(configPath, throwaway))
    40  
    41  	// make sure config is set properly
    42  	data, err := os.ReadFile(filepath.Join(tmpDir, defaultConfigPath))
    43  	require.Nil(t, err)
    44  
    45  	require.True(t, checkConfig(string(data)))
    46  
    47  	ensureFiles(t, tmpDir, DefaultDBDir)
    48  }
    49  
    50  func TestEnsureTestRoot(t *testing.T) {
    51  	t.Parallel()
    52  
    53  	testName := "ensureTestRoot"
    54  
    55  	// create root dir
    56  	cfg, _ := ResetTestRoot(testName)
    57  	defer os.RemoveAll(cfg.RootDir)
    58  	rootDir := cfg.RootDir
    59  
    60  	// make sure config is set properly
    61  	data, err := os.ReadFile(filepath.Join(rootDir, defaultConfigPath))
    62  	require.Nil(t, err)
    63  
    64  	require.True(t, checkConfig(string(data)))
    65  
    66  	// TODO: make sure the cfg returned and testconfig are the same!
    67  	baseConfig := DefaultBaseConfig()
    68  	ensureFiles(
    69  		t,
    70  		rootDir,
    71  		"genesis.json",
    72  		DefaultDBDir,
    73  		baseConfig.PrivValidatorKey,
    74  		baseConfig.PrivValidatorState,
    75  	)
    76  }
    77  
    78  func checkConfig(configFile string) bool {
    79  	var valid bool
    80  
    81  	// list of words we expect in the config
    82  	elems := []string{
    83  		"moniker",
    84  		"seeds",
    85  		"proxy_app",
    86  		"fast_sync",
    87  		"create_empty_blocks",
    88  		"peer",
    89  		"timeout",
    90  		"broadcast",
    91  		"send",
    92  		"addr",
    93  		"wal",
    94  		"propose",
    95  		"max",
    96  	}
    97  	for _, e := range elems {
    98  		if !strings.Contains(configFile, e) {
    99  			valid = false
   100  		} else {
   101  			valid = true
   102  		}
   103  	}
   104  	return valid
   105  }
   106  
   107  func TestTOML_LoadConfig(t *testing.T) {
   108  	t.Parallel()
   109  
   110  	t.Run("config does not exist", func(t *testing.T) {
   111  		t.Parallel()
   112  
   113  		cfg, loadErr := LoadConfigFile("dummy-path")
   114  
   115  		assert.Error(t, loadErr)
   116  		assert.Nil(t, cfg)
   117  	})
   118  
   119  	t.Run("config is not valid toml", func(t *testing.T) {
   120  		t.Parallel()
   121  
   122  		// Create config file
   123  		configFile, cleanup := testutils.NewTestFile(t)
   124  		t.Cleanup(cleanup)
   125  
   126  		// Write invalid TOML
   127  		_, writeErr := configFile.WriteString("invalid TOML")
   128  		require.NoError(t, writeErr)
   129  
   130  		cfg, loadErr := LoadConfigFile(configFile.Name())
   131  
   132  		assert.Error(t, loadErr)
   133  		assert.Nil(t, cfg)
   134  	})
   135  
   136  	t.Run("valid config", func(t *testing.T) {
   137  		t.Parallel()
   138  
   139  		// Create config file
   140  		configFile, cleanup := testutils.NewTestFile(t)
   141  		t.Cleanup(cleanup)
   142  
   143  		// Create the default config
   144  		defaultConfig := DefaultConfig()
   145  
   146  		// Marshal the default config
   147  		defaultConfigRaw, marshalErr := toml.Marshal(defaultConfig)
   148  		require.NoError(t, marshalErr)
   149  
   150  		// Write valid TOML
   151  		_, writeErr := configFile.Write(defaultConfigRaw)
   152  		require.NoError(t, writeErr)
   153  
   154  		cfg, loadErr := LoadConfigFile(configFile.Name())
   155  		require.NoError(t, loadErr)
   156  
   157  		assert.EqualValues(t, defaultConfig.BaseConfig, cfg.BaseConfig)
   158  		assert.EqualValues(t, defaultConfig.RPC, cfg.RPC)
   159  		assert.EqualValues(t, defaultConfig.P2P, cfg.P2P)
   160  		assert.EqualValues(t, defaultConfig.Mempool, cfg.Mempool)
   161  		assert.EqualValues(t, defaultConfig.Consensus, cfg.Consensus)
   162  		assert.Equal(t, defaultConfig.TxEventStore.EventStoreType, cfg.TxEventStore.EventStoreType)
   163  		assert.Empty(t, defaultConfig.TxEventStore.Params, cfg.TxEventStore.Params)
   164  	})
   165  }
   166  
   167  func TestTOML_ConfigComments(t *testing.T) {
   168  	t.Parallel()
   169  
   170  	collectCommentTags := func(v reflect.Value) []string {
   171  		var (
   172  			comments    = make([]string, 0)
   173  			structStack = []reflect.Value{v}
   174  		)
   175  
   176  		// Descend on and parse all child fields
   177  		for len(structStack) > 0 {
   178  			structVal := structStack[len(structStack)-1]
   179  			structStack = structStack[:len(structStack)-1]
   180  
   181  			// Process all fields of the struct
   182  			for i := 0; i < structVal.NumField(); i++ {
   183  				fieldVal := structVal.Field(i)
   184  				fieldType := structVal.Type().Field(i)
   185  
   186  				// If the field is a struct, push it onto the stack for further processing
   187  				if fieldVal.Kind() == reflect.Struct {
   188  					structStack = append(structStack, fieldVal)
   189  
   190  					continue
   191  				}
   192  
   193  				// Collect the comment tag value from the field
   194  				if commentTag := fieldType.Tag.Get("comment"); commentTag != "" {
   195  					comments = append(comments, commentTag)
   196  				}
   197  			}
   198  		}
   199  
   200  		return comments
   201  	}
   202  
   203  	cleanComments := func(original string) string {
   204  		return strings.ReplaceAll(original, "#", "")
   205  	}
   206  
   207  	// Create test config file
   208  	configFile, cleanup := testutils.NewTestFile(t)
   209  	t.Cleanup(cleanup)
   210  
   211  	// Create the default config
   212  	defaultConfig := DefaultConfig()
   213  
   214  	// Write valid TOML
   215  	require.NoError(t, WriteConfigFile(configFile.Name(), defaultConfig))
   216  
   217  	// Collect config comments
   218  	comments := collectCommentTags(reflect.ValueOf(*defaultConfig))
   219  	require.NotEmpty(t, comments)
   220  
   221  	// Read the entire config file
   222  	rawConfig, err := io.ReadAll(configFile)
   223  	require.NoError(t, err)
   224  
   225  	// Verify TOML comments are present
   226  	content := cleanComments(string(rawConfig))
   227  	for _, comment := range comments {
   228  		assert.Contains(t, content, cleanComments(comment))
   229  	}
   230  }