github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/cmd/syft/cli/commands/update_test.go (about) 1 package commands 2 3 import ( 4 "net/http" 5 "net/http/httptest" 6 "testing" 7 8 "github.com/anchore/clio" 9 hashiVersion "github.com/anchore/go-version" 10 "github.com/anchore/syft/cmd/syft/internal" 11 ) 12 13 func TestIsUpdateAvailable(t *testing.T) { 14 tests := []struct { 15 name string 16 buildVersion string 17 latestVersion string 18 code int 19 isAvailable bool 20 newVersion string 21 err bool 22 }{ 23 { 24 name: "equal", 25 buildVersion: "1.0.0", 26 latestVersion: "1.0.0", 27 code: 200, 28 isAvailable: false, 29 newVersion: "", 30 err: false, 31 }, 32 { 33 name: "hasUpdate", 34 buildVersion: "1.0.0", 35 latestVersion: "1.2.0", 36 code: 200, 37 isAvailable: true, 38 newVersion: "1.2.0", 39 err: false, 40 }, 41 { 42 name: "aheadOfLatest", 43 buildVersion: "1.2.0", 44 latestVersion: "1.0.0", 45 code: 200, 46 isAvailable: false, 47 newVersion: "", 48 err: false, 49 }, 50 { 51 name: "EmptyUpdate", 52 buildVersion: "1.0.0", 53 latestVersion: "", 54 code: 200, 55 isAvailable: false, 56 newVersion: "", 57 err: true, 58 }, 59 { 60 name: "GarbageUpdate", 61 buildVersion: "1.0.0", 62 latestVersion: "hdfjksdhfhkj", 63 code: 200, 64 isAvailable: false, 65 newVersion: "", 66 err: true, 67 }, 68 { 69 name: "BadUpdate", 70 buildVersion: "1.0.0", 71 latestVersion: "1.0.", 72 code: 500, 73 isAvailable: false, 74 newVersion: "", 75 err: true, 76 }, 77 { 78 name: "NoBuildVersion", 79 buildVersion: internal.NotProvided, 80 latestVersion: "1.0.0", 81 code: 200, 82 isAvailable: false, 83 newVersion: "", 84 err: false, 85 }, 86 { 87 name: "SnapshotBuildVersion", 88 buildVersion: "2.0.0-SHAPSHOT-a78bf9c", 89 latestVersion: "1.0.0", 90 code: 200, 91 isAvailable: false, 92 newVersion: "", 93 err: false, 94 }, 95 { 96 name: "BadUpdateValidVersion", 97 buildVersion: "1.0.0", 98 latestVersion: "2.0.0", 99 code: 404, 100 isAvailable: false, 101 newVersion: "", 102 err: true, 103 }, 104 } 105 106 for _, test := range tests { 107 t.Run(test.name, func(t *testing.T) { 108 // setup mocks 109 // local... 110 id := clio.Identification{Name: "Syft", Version: test.buildVersion} 111 // remote... 112 handler := http.NewServeMux() 113 handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) { 114 w.WriteHeader(test.code) 115 _, _ = w.Write([]byte(test.latestVersion)) 116 }) 117 mockSrv := httptest.NewServer(handler) 118 latestAppVersionURL.host = mockSrv.URL 119 defer mockSrv.Close() 120 121 isAvailable, newVersion, err := isUpdateAvailable(id) 122 if err != nil && !test.err { 123 t.Fatalf("got error but expected none: %+v", err) 124 } else if err == nil && test.err { 125 t.Fatalf("expected error but got none") 126 } 127 128 if newVersion != test.newVersion { 129 t.Errorf("unexpected NEW version: %+v", newVersion) 130 } 131 132 if isAvailable != test.isAvailable { 133 t.Errorf("unexpected result: %+v", isAvailable) 134 } 135 }) 136 } 137 138 } 139 140 func TestFetchLatestApplicationVersion(t *testing.T) { 141 tests := []struct { 142 name string 143 response string 144 code int 145 err bool 146 id clio.Identification 147 expected *hashiVersion.Version 148 expectedHeaders map[string]string 149 }{ 150 { 151 name: "gocase", 152 response: "1.0.0", 153 code: 200, 154 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 155 expected: hashiVersion.Must(hashiVersion.NewVersion("1.0.0")), 156 expectedHeaders: map[string]string{"User-Agent": "Syft 0.0.0"}, 157 err: false, 158 }, 159 { 160 name: "garbage", 161 response: "garbage", 162 code: 200, 163 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 164 expected: nil, 165 expectedHeaders: nil, 166 err: true, 167 }, 168 { 169 name: "http 500", 170 response: "1.0.0", 171 code: 500, 172 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 173 expected: nil, 174 expectedHeaders: nil, 175 err: true, 176 }, 177 { 178 name: "http 404", 179 response: "1.0.0", 180 code: 404, 181 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 182 expected: nil, 183 expectedHeaders: nil, 184 err: true, 185 }, 186 { 187 name: "empty", 188 response: "", 189 code: 200, 190 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 191 expected: nil, 192 expectedHeaders: nil, 193 err: true, 194 }, 195 { 196 name: "too long", 197 response: "this is really long this is really long this is really long this is really long this is really long this is really long this is really long this is really long ", 198 code: 200, 199 id: clio.Identification{Name: "Syft", Version: "0.0.0"}, 200 expected: nil, 201 expectedHeaders: nil, 202 err: true, 203 }, 204 } 205 206 for _, test := range tests { 207 t.Run(test.name, func(t *testing.T) { 208 // setup mock 209 handler := http.NewServeMux() 210 handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) { 211 if test.expectedHeaders != nil { 212 for headerName, headerValue := range test.expectedHeaders { 213 actualHeader := r.Header.Get(headerName) 214 if actualHeader != headerValue { 215 t.Fatalf("expected header %v=%v but got %v", headerName, headerValue, actualHeader) 216 } 217 } 218 } 219 220 w.WriteHeader(test.code) 221 _, _ = w.Write([]byte(test.response)) 222 }) 223 mockSrv := httptest.NewServer(handler) 224 latestAppVersionURL.host = mockSrv.URL 225 defer mockSrv.Close() 226 227 actual, err := fetchLatestApplicationVersion(test.id) 228 if err != nil && !test.err { 229 t.Fatalf("got error but expected none: %+v", err) 230 } else if err == nil && test.err { 231 t.Fatalf("expected error but got none") 232 } 233 234 if err != nil { 235 return 236 } 237 238 if actual.String() != test.expected.String() { 239 t.Errorf("unexpected version: %+v", actual.String()) 240 } 241 }) 242 } 243 244 }