github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/manifest/manifest_test.go (about)

     1  package manifest_test
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	toml "github.com/pelletier/go-toml"
    12  
    13  	"github.com/fastly/cli/pkg/env"
    14  	fsterr "github.com/fastly/cli/pkg/errors"
    15  	"github.com/fastly/cli/pkg/manifest"
    16  	"github.com/fastly/cli/pkg/testutil"
    17  	"github.com/fastly/cli/pkg/threadsafe"
    18  )
    19  
    20  func TestManifest(t *testing.T) {
    21  	tests := map[string]struct {
    22  		manifest             string
    23  		valid                bool
    24  		expectedError        error
    25  		wantRemediationError string
    26  		expectedOutput       string
    27  	}{
    28  		"valid: semver": {
    29  			manifest: "fastly-valid-semver.toml",
    30  			valid:    true,
    31  		},
    32  		"valid: integer": {
    33  			manifest: "fastly-valid-integer.toml",
    34  			valid:    true,
    35  		},
    36  		"invalid: missing manifest_version": {
    37  			manifest: "fastly-invalid-missing-version.toml",
    38  			valid:    true, // expect manifest_version to be set to latest version
    39  		},
    40  		"invalid: manifest_version Atoi error": {
    41  			manifest:      "fastly-invalid-unrecognised.toml",
    42  			valid:         false,
    43  			expectedError: fmt.Errorf("error parsing manifest_version 'abc'"),
    44  		},
    45  		"unrecognised: manifest_version exceeded limit": {
    46  			manifest:      "fastly-invalid-version-exceeded.toml",
    47  			valid:         false,
    48  			expectedError: fsterr.ErrUnrecognisedManifestVersion,
    49  		},
    50  		"warning: dictionaries now replaced with config_stores": {
    51  			manifest:       "fastly-warning-dictionaries.toml",
    52  			valid:          true, // we display a warning but we don't exit command execution
    53  			expectedOutput: "WARNING: Your fastly.toml manifest contains `[setup.dictionaries]`",
    54  		},
    55  	}
    56  
    57  	// NOTE: some of the fixture files are overwritten by the application logic
    58  	// and so to ensure future test runs can complete successfully we do an
    59  	// initial read of the data and then write it back to disk once the tests
    60  	// have completed.
    61  
    62  	prefix := filepath.Join("./", "testdata")
    63  
    64  	for _, fpath := range []string{
    65  		"fastly-valid-semver.toml",
    66  		"fastly-valid-integer.toml",
    67  		"fastly-invalid-missing-version.toml",
    68  		"fastly-invalid-unrecognised.toml",
    69  		"fastly-invalid-version-exceeded.toml",
    70  	} {
    71  		path, err := filepath.Abs(filepath.Join(prefix, fpath))
    72  		if err != nil {
    73  			t.Fatal(err)
    74  		}
    75  
    76  		b, err := os.ReadFile(path)
    77  		if err != nil {
    78  			t.Fatal(err)
    79  		}
    80  
    81  		defer func(path string, b []byte) {
    82  			err := os.WriteFile(path, b, 0o600)
    83  			if err != nil {
    84  				t.Fatal(err)
    85  			}
    86  		}(path, b)
    87  	}
    88  
    89  	for name, tc := range tests {
    90  		t.Run(name, func(t *testing.T) {
    91  			var (
    92  				m      manifest.File
    93  				stdout threadsafe.Buffer
    94  			)
    95  			m.SetErrLog(fsterr.Log)
    96  			m.SetOutput(&stdout)
    97  
    98  			path, err := filepath.Abs(filepath.Join(prefix, tc.manifest))
    99  			if err != nil {
   100  				t.Fatal(err)
   101  			}
   102  
   103  			err = m.Read(path)
   104  
   105  			output := stdout.String()
   106  			t.Log(output)
   107  
   108  			// If we expect an invalid config, then assert we get the right error.
   109  			if !tc.valid {
   110  				testutil.AssertErrorContains(t, err, tc.expectedError.Error())
   111  				return
   112  			}
   113  
   114  			// Otherwise, if we expect the manifest to be valid and we get an error,
   115  			// then that's unexpected behaviour.
   116  			if err != nil {
   117  				t.Fatal(err)
   118  			}
   119  
   120  			if m.ManifestVersion != manifest.ManifestLatestVersion {
   121  				t.Fatalf("manifest_version '%d' doesn't match latest '%d'", m.ManifestVersion, manifest.ManifestLatestVersion)
   122  			}
   123  
   124  			if tc.expectedOutput != "" && !strings.Contains(output, tc.expectedOutput) {
   125  				t.Fatalf("got: %s, want: %s", output, tc.expectedOutput)
   126  			}
   127  		})
   128  	}
   129  }
   130  
   131  func TestManifestPrepend(t *testing.T) {
   132  	var (
   133  		manifestBody []byte
   134  		manifestPath string
   135  	)
   136  
   137  	// NOTE: the fixture file "fastly-missing-spec-url.toml" will be
   138  	// overwritten by the test as the internal logic is supposed to add into the
   139  	// manifest a reference to the fastly.toml specification.
   140  	//
   141  	// To ensure future test runs complete successfully we do an initial read of
   142  	// the data and then write it back out when the tests have completed.
   143  	{
   144  		path, err := filepath.Abs(filepath.Join("./", "testdata", "fastly-missing-spec-url.toml"))
   145  		if err != nil {
   146  			t.Fatal(err)
   147  		}
   148  
   149  		manifestBody, err = os.ReadFile(path)
   150  		if err != nil {
   151  			t.Fatal(err)
   152  		}
   153  
   154  		defer func(path string, b []byte) {
   155  			err := os.WriteFile(path, b, 0o600)
   156  			if err != nil {
   157  				t.Fatal(err)
   158  			}
   159  		}(path, manifestBody)
   160  	}
   161  
   162  	// Create temp environment to run test code within.
   163  	{
   164  		wd, err := os.Getwd()
   165  		if err != nil {
   166  			t.Fatal(err)
   167  		}
   168  
   169  		rootdir := testutil.NewEnv(testutil.EnvOpts{
   170  			T: t,
   171  			Write: []testutil.FileIO{
   172  				{Src: string(manifestBody), Dst: "fastly.toml"},
   173  			},
   174  		})
   175  		manifestPath = filepath.Join(rootdir, "fastly.toml")
   176  		defer os.RemoveAll(rootdir)
   177  
   178  		if err := os.Chdir(rootdir); err != nil {
   179  			t.Fatal(err)
   180  		}
   181  		defer func() {
   182  			_ = os.Chdir(wd)
   183  		}()
   184  	}
   185  
   186  	var f manifest.File
   187  	err := f.Read(manifestPath)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	err = f.Write(manifestPath)
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	updatedManifest, err := os.ReadFile(manifestPath)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  	content := string(updatedManifest)
   202  
   203  	if !strings.Contains(content, manifest.SpecIntro) || !strings.Contains(content, manifest.SpecURL) {
   204  		t.Fatal("missing fastly.toml specification reference link")
   205  	}
   206  }
   207  
   208  func TestDataServiceID(t *testing.T) {
   209  	t.Setenv(env.ServiceID, "001")
   210  
   211  	// SourceFlag
   212  	d := manifest.Data{
   213  		Flag: manifest.Flag{ServiceID: "123"},
   214  		File: manifest.File{ServiceID: "456"},
   215  	}
   216  	_, src := d.ServiceID()
   217  	if src != manifest.SourceFlag {
   218  		t.Fatal("expected SourceFlag")
   219  	}
   220  
   221  	// SourceEnv
   222  	d.Flag = manifest.Flag{}
   223  	_, src = d.ServiceID()
   224  	if src != manifest.SourceEnv {
   225  		t.Fatal("expected SourceEnv")
   226  	}
   227  
   228  	// SourceFile
   229  	t.Setenv(env.ServiceID, "")
   230  	_, src = d.ServiceID()
   231  	if src != manifest.SourceFile {
   232  		t.Fatal("expected SourceFile")
   233  	}
   234  }
   235  
   236  // This test validates that manually added changes, such as the toml
   237  // syntax for Viceroy local testing, are not accidentally deleted after
   238  // decoding and encoding flows.
   239  func TestManifestPersistsLocalServerSection(t *testing.T) {
   240  	fpath := filepath.Join("./", "testdata", "fastly-viceroy-update.toml")
   241  
   242  	b, err := os.ReadFile(fpath)
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  
   247  	defer func(fpath string, b []byte) {
   248  		err := os.WriteFile(fpath, b, 0o600)
   249  		if err != nil {
   250  			t.Fatal(err)
   251  		}
   252  	}(fpath, b)
   253  
   254  	original, err := toml.LoadFile(fpath)
   255  	if err != nil {
   256  		t.Fatal(err)
   257  	}
   258  
   259  	ot := original.Get("local_server")
   260  	if ot == nil {
   261  		t.Fatal("expected [local_server] block to exist in fastly.toml but is missing")
   262  	}
   263  
   264  	osid := original.Get("service_id")
   265  	if osid != nil {
   266  		t.Fatal("did not expect service_id key to exist in fastly.toml but is present")
   267  	}
   268  
   269  	var m manifest.File
   270  
   271  	err = m.Read(fpath)
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	m.ServiceID = "a change occurred to the data structure"
   277  
   278  	err = m.Write(fpath)
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  
   283  	latest, err := toml.LoadFile(fpath)
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  
   288  	lsid := latest.Get("service_id")
   289  	if lsid == nil {
   290  		t.Fatal("expected service_id key to exist in fastly.toml but is missing")
   291  	}
   292  
   293  	lt := latest.Get("local_server")
   294  	if lt == nil {
   295  		t.Fatal("expected [local_server] block to exist in fastly.toml but is missing")
   296  	}
   297  
   298  	localTree, ok := lt.(*toml.Tree)
   299  	if !ok {
   300  		t.Fatal("failed to convert 'local' interface{} to toml.Tree")
   301  	}
   302  	originalTree, ok := ot.(*toml.Tree)
   303  	if !ok {
   304  		t.Fatal("failed to convert 'original' interface{} to toml.Tree")
   305  	}
   306  	want, got := originalTree.String(), localTree.String()
   307  	if diff := cmp.Diff(want, got); diff != "" {
   308  		t.Fatalf("testing section between original and updated fastly.toml do not match (-want +got):\n%s", diff)
   309  	}
   310  }