github.com/9elements/firmware-action/action@v0.0.0-20240514065043-044ed91c9ed8/recipes/config_test.go (about)

     1  // SPDX-License-Identifier: MIT
     2  
     3  // Package recipes / coreboot
     4  package recipes
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  func TestValidateConfig(t *testing.T) {
    17  	commonDummy := CommonOpts{
    18  		SdkURL:    "ghcr.io/9elements/firmware-action/coreboot_4.19:main",
    19  		RepoPath:  "dummy/dir/",
    20  		OutputDir: "dummy/dir/",
    21  	}
    22  
    23  	testCases := []struct {
    24  		name    string
    25  		wantErr error
    26  		opts    Config
    27  	}{
    28  		{
    29  			name:    "completely empty",
    30  			wantErr: nil,
    31  			opts:    Config{},
    32  		},
    33  		{
    34  			name:    "empty coreboot opts",
    35  			wantErr: nil,
    36  			opts: Config{
    37  				Coreboot: map[string]CorebootOpts{},
    38  			},
    39  		},
    40  		{
    41  			name:    "missing required coreboot opts",
    42  			wantErr: ErrFailedValidation,
    43  			opts: Config{
    44  				Coreboot: map[string]CorebootOpts{
    45  					"coreboot-A": {},
    46  				},
    47  			},
    48  		},
    49  		{
    50  			name:    "required coreboot opts present",
    51  			wantErr: nil,
    52  			opts: Config{
    53  				Coreboot: map[string]CorebootOpts{
    54  					"coreboot-A": {
    55  						CommonOpts:    commonDummy,
    56  						DefconfigPath: "dummy",
    57  					},
    58  				},
    59  			},
    60  		},
    61  		{
    62  			name:    "required coreboot opts present 2",
    63  			wantErr: nil,
    64  			opts: Config{
    65  				Coreboot: map[string]CorebootOpts{
    66  					"coreboot-A": {
    67  						CommonOpts:    commonDummy,
    68  						DefconfigPath: "dummy",
    69  						Blobs:         CorebootBlobs{},
    70  					},
    71  				},
    72  			},
    73  		},
    74  		{
    75  			name:    "missing common opts",
    76  			wantErr: ErrFailedValidation,
    77  			opts: Config{
    78  				Coreboot: map[string]CorebootOpts{
    79  					"coreboot-A": {
    80  						CommonOpts:    CommonOpts{},
    81  						DefconfigPath: "dummy",
    82  						Blobs:         CorebootBlobs{},
    83  					},
    84  				},
    85  			},
    86  		},
    87  	}
    88  	for _, tc := range testCases {
    89  		t.Run(tc.name, func(t *testing.T) {
    90  			err := ValidateConfig(tc.opts)
    91  			assert.ErrorIs(t, err, tc.wantErr)
    92  		})
    93  	}
    94  }
    95  
    96  func TestConfigReadAndWrite(t *testing.T) {
    97  	configOriginal := Config{
    98  		Coreboot: map[string]CorebootOpts{},
    99  		Linux:    map[string]LinuxOpts{},
   100  		Edk2:     map[string]Edk2Opts{},
   101  	}
   102  
   103  	tmpDir := t.TempDir()
   104  	configFilepath := filepath.Join(tmpDir, "test.json")
   105  
   106  	// Write
   107  	err := WriteConfig(configFilepath, &configOriginal)
   108  	assert.NoError(t, err)
   109  
   110  	// Read
   111  	configNew, err := ReadConfig(configFilepath)
   112  	assert.NoError(t, err)
   113  
   114  	// Compare
   115  	equal := cmp.Equal(&configOriginal, configNew)
   116  	if !equal {
   117  		fmt.Println(cmp.Diff(configOriginal, configNew))
   118  		assert.True(t, equal, "written and read configuration are not equal")
   119  	}
   120  }
   121  
   122  func TestConfigEnvVars(t *testing.T) {
   123  	commonDummy := CommonOpts{
   124  		SdkURL:    "ghcr.io/9elements/firmware-action/coreboot_4.19:main",
   125  		RepoPath:  "dummy/dir/",
   126  		OutputDir: "dummy/dir/",
   127  	}
   128  
   129  	testCases := []struct {
   130  		name             string
   131  		wantErr          error
   132  		url              string
   133  		urlExpected      string
   134  		repoPath         string
   135  		repoPathExpected string
   136  		envVars          map[string]string
   137  	}{
   138  		{
   139  			name:             "no env vars",
   140  			wantErr:          nil,
   141  			url:              commonDummy.SdkURL,
   142  			urlExpected:      commonDummy.SdkURL,
   143  			repoPath:         commonDummy.RepoPath,
   144  			repoPathExpected: commonDummy.RepoPath,
   145  			envVars:          map[string]string{},
   146  		},
   147  		{
   148  			name:             "env vars",
   149  			wantErr:          nil,
   150  			url:              "ghcr.io/$TEST_ENV_VAR/coreboot_4.19:main",
   151  			urlExpected:      commonDummy.SdkURL,
   152  			repoPath:         commonDummy.RepoPath,
   153  			repoPathExpected: commonDummy.RepoPath,
   154  			envVars: map[string]string{
   155  				"TEST_ENV_VAR": "9elements/firmware-action",
   156  			},
   157  		},
   158  		{
   159  			name:             "env vars with brackets",
   160  			wantErr:          nil,
   161  			url:              "ghcr.io/${TEST_ENV_VAR}/coreboot_4.19:main",
   162  			urlExpected:      commonDummy.SdkURL,
   163  			repoPath:         commonDummy.RepoPath,
   164  			repoPathExpected: commonDummy.RepoPath,
   165  			envVars: map[string]string{
   166  				"TEST_ENV_VAR": "9elements/firmware-action",
   167  			},
   168  		},
   169  		{
   170  			name:             "multiple env vars in multiple entries",
   171  			wantErr:          nil,
   172  			url:              "ghcr.io/${TEST_ENV_VAR_PROJECT}/${TEST_ENV_VAR_SDK}:${TEST_ENV_VAR_VERSION}",
   173  			urlExpected:      commonDummy.SdkURL,
   174  			repoPath:         "${TEST_ENV_VAR_REPOPATH}",
   175  			repoPathExpected: commonDummy.RepoPath,
   176  			envVars: map[string]string{
   177  				"TEST_ENV_VAR_PROJECT":  "9elements/firmware-action",
   178  				"TEST_ENV_VAR_VERSION":  "main",
   179  				"TEST_ENV_VAR_SDK":      "coreboot_4.19",
   180  				"TEST_ENV_VAR_REPOPATH": "dummy/dir/",
   181  			},
   182  		},
   183  	}
   184  	for _, tc := range testCases {
   185  		t.Run(tc.name, func(t *testing.T) {
   186  			opts := Config{
   187  				Coreboot: map[string]CorebootOpts{
   188  					"coreboot-A": {
   189  						CommonOpts: CommonOpts{
   190  							SdkURL:    tc.url,
   191  							RepoPath:  "dummy/dir/",
   192  							OutputDir: "dummy/dir/",
   193  						},
   194  						DefconfigPath: "dummy",
   195  					},
   196  				},
   197  			}
   198  			for key, value := range tc.envVars {
   199  				os.Setenv(key, value)
   200  				defer os.Unsetenv(key)
   201  				fmt.Printf("Setting %s = %s\n", key, value)
   202  			}
   203  
   204  			// Write and read config
   205  			// The read function handles the expansion of environment variables
   206  			tmpDir := t.TempDir()
   207  			configFilepath := filepath.Join(tmpDir, "test.json")
   208  			// Write
   209  			err := WriteConfig(configFilepath, &opts)
   210  			assert.NoError(t, err)
   211  			// Read
   212  			optsConverted, err := ReadConfig(configFilepath)
   213  			assert.NoError(t, err)
   214  
   215  			// err = ValidateConfig(optsConverted)
   216  			assert.ErrorIs(t, err, tc.wantErr)
   217  			assert.Equal(t, tc.urlExpected, optsConverted.Coreboot["coreboot-A"].SdkURL)
   218  			assert.Equal(t, tc.repoPathExpected, optsConverted.Coreboot["coreboot-A"].RepoPath)
   219  		})
   220  	}
   221  }
   222  
   223  func TestOffsetToLineNumber(t *testing.T) {
   224  	testCases := []struct {
   225  		name      string
   226  		input     string
   227  		offset    int
   228  		line      int
   229  		character int
   230  		wantErr   error
   231  	}{
   232  		{
   233  			name:    "empty string",
   234  			input:   "",
   235  			offset:  1,
   236  			wantErr: ErrVerboseJSON,
   237  		},
   238  		{
   239  			name:      "1 line, offset 0",
   240  			input:     "dummy line",
   241  			offset:    0,
   242  			line:      1,
   243  			character: 1,
   244  			wantErr:   nil,
   245  		},
   246  		{
   247  			name:      "1 line, offset 1",
   248  			input:     "dummy line",
   249  			offset:    1,
   250  			line:      1,
   251  			character: 2,
   252  			wantErr:   nil,
   253  		},
   254  		{
   255  			name: "2 lines, offset in 1st line",
   256  			// offset:  012345 6789
   257  			input:     "dummy\nline",
   258  			offset:    1,
   259  			line:      1,
   260  			character: 2,
   261  			wantErr:   nil,
   262  		},
   263  		{
   264  			name: "2 lines, offset end of 1st line",
   265  			// offset:  012345 6789
   266  			input:     "dummy\nline",
   267  			offset:    4,
   268  			line:      1,
   269  			character: 5,
   270  			wantErr:   nil,
   271  		},
   272  		{
   273  			name:      "2 lines, offset in 2nd line",
   274  			input:     "dummy\nline",
   275  			offset:    7,
   276  			line:      2,
   277  			character: 2,
   278  			wantErr:   nil,
   279  		},
   280  		{
   281  			name: "2 lines, offset in 2nd line",
   282  			// offset:  0 1 2 3 4567
   283  			input:     "\n\n\n\nline",
   284  			offset:    7,
   285  			line:      5,
   286  			character: 4,
   287  			wantErr:   nil,
   288  		},
   289  	}
   290  	for _, tc := range testCases {
   291  		t.Run(tc.name, func(t *testing.T) {
   292  			line, character, err := offsetToLineNumber(tc.input, tc.offset)
   293  			assert.ErrorIs(t, err, tc.wantErr)
   294  			if err != nil {
   295  				// no need to continue on error
   296  				return
   297  			}
   298  			assert.Equal(t, tc.line, line, "line is wrong")
   299  			assert.Equal(t, tc.character, character, "character is wrong")
   300  		})
   301  	}
   302  }