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 }