github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/storage/driver/sql_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 http://www.apache.org/licenses/LICENSE-2.0 7 Unless required by applicable law or agreed to in writing, software 8 distributed under the License is distributed on an "AS IS" BASIS, 9 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 See the License for the specific language governing permissions and 11 limitations under the License. 12 */ 13 14 package driver 15 16 import ( 17 "fmt" 18 "reflect" 19 "regexp" 20 "testing" 21 "time" 22 23 sqlmock "github.com/DATA-DOG/go-sqlmock" 24 25 rspb "github.com/stefanmcshane/helm/pkg/release" 26 ) 27 28 func TestSQLName(t *testing.T) { 29 sqlDriver, _ := newTestFixtureSQL(t) 30 if sqlDriver.Name() != SQLDriverName { 31 t.Errorf("Expected name to be %s, got %s", SQLDriverName, sqlDriver.Name()) 32 } 33 } 34 35 func TestSQLGet(t *testing.T) { 36 vers := int(1) 37 name := "smug-pigeon" 38 namespace := "default" 39 key := testKey(name, vers) 40 rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) 41 42 body, _ := encodeRelease(rel) 43 44 sqlDriver, mock := newTestFixtureSQL(t) 45 46 query := fmt.Sprintf( 47 regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 AND %s = $2"), 48 sqlReleaseTableBodyColumn, 49 sqlReleaseTableName, 50 sqlReleaseTableKeyColumn, 51 sqlReleaseTableNamespaceColumn, 52 ) 53 54 mock. 55 ExpectQuery(query). 56 WithArgs(key, namespace). 57 WillReturnRows( 58 mock.NewRows([]string{ 59 sqlReleaseTableBodyColumn, 60 }).AddRow( 61 body, 62 ), 63 ).RowsWillBeClosed() 64 65 got, err := sqlDriver.Get(key) 66 if err != nil { 67 t.Fatalf("Failed to get release: %v", err) 68 } 69 70 if !reflect.DeepEqual(rel, got) { 71 t.Errorf("Expected release {%v}, got {%v}", rel, got) 72 } 73 74 if err := mock.ExpectationsWereMet(); err != nil { 75 t.Errorf("sql expectations weren't met: %v", err) 76 } 77 } 78 79 func TestSQLList(t *testing.T) { 80 body1, _ := encodeRelease(releaseStub("key-1", 1, "default", rspb.StatusUninstalled)) 81 body2, _ := encodeRelease(releaseStub("key-2", 1, "default", rspb.StatusUninstalled)) 82 body3, _ := encodeRelease(releaseStub("key-3", 1, "default", rspb.StatusDeployed)) 83 body4, _ := encodeRelease(releaseStub("key-4", 1, "default", rspb.StatusDeployed)) 84 body5, _ := encodeRelease(releaseStub("key-5", 1, "default", rspb.StatusSuperseded)) 85 body6, _ := encodeRelease(releaseStub("key-6", 1, "default", rspb.StatusSuperseded)) 86 87 sqlDriver, mock := newTestFixtureSQL(t) 88 89 for i := 0; i < 3; i++ { 90 query := fmt.Sprintf( 91 "SELECT %s FROM %s WHERE %s = $1 AND %s = $2", 92 sqlReleaseTableBodyColumn, 93 sqlReleaseTableName, 94 sqlReleaseTableOwnerColumn, 95 sqlReleaseTableNamespaceColumn, 96 ) 97 98 mock. 99 ExpectQuery(regexp.QuoteMeta(query)). 100 WithArgs(sqlReleaseDefaultOwner, sqlDriver.namespace). 101 WillReturnRows( 102 mock.NewRows([]string{ 103 sqlReleaseTableBodyColumn, 104 }). 105 AddRow(body1). 106 AddRow(body2). 107 AddRow(body3). 108 AddRow(body4). 109 AddRow(body5). 110 AddRow(body6), 111 ).RowsWillBeClosed() 112 } 113 114 // list all deleted releases 115 del, err := sqlDriver.List(func(rel *rspb.Release) bool { 116 return rel.Info.Status == rspb.StatusUninstalled 117 }) 118 // check 119 if err != nil { 120 t.Errorf("Failed to list deleted: %v", err) 121 } 122 if len(del) != 2 { 123 t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) 124 } 125 126 // list all deployed releases 127 dpl, err := sqlDriver.List(func(rel *rspb.Release) bool { 128 return rel.Info.Status == rspb.StatusDeployed 129 }) 130 // check 131 if err != nil { 132 t.Errorf("Failed to list deployed: %v", err) 133 } 134 if len(dpl) != 2 { 135 t.Errorf("Expected 2 deployed, got %d:\n%v\n", len(dpl), dpl) 136 } 137 138 // list all superseded releases 139 ssd, err := sqlDriver.List(func(rel *rspb.Release) bool { 140 return rel.Info.Status == rspb.StatusSuperseded 141 }) 142 // check 143 if err != nil { 144 t.Errorf("Failed to list superseded: %v", err) 145 } 146 if len(ssd) != 2 { 147 t.Errorf("Expected 2 superseded, got %d:\n%v\n", len(ssd), ssd) 148 } 149 150 if err := mock.ExpectationsWereMet(); err != nil { 151 t.Errorf("sql expectations weren't met: %v", err) 152 } 153 } 154 155 func TestSqlCreate(t *testing.T) { 156 vers := 1 157 name := "smug-pigeon" 158 namespace := "default" 159 key := testKey(name, vers) 160 rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) 161 162 sqlDriver, mock := newTestFixtureSQL(t) 163 body, _ := encodeRelease(rel) 164 165 query := fmt.Sprintf( 166 "INSERT INTO %s (%s,%s,%s,%s,%s,%s,%s,%s,%s) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", 167 sqlReleaseTableName, 168 sqlReleaseTableKeyColumn, 169 sqlReleaseTableTypeColumn, 170 sqlReleaseTableBodyColumn, 171 sqlReleaseTableNameColumn, 172 sqlReleaseTableNamespaceColumn, 173 sqlReleaseTableVersionColumn, 174 sqlReleaseTableStatusColumn, 175 sqlReleaseTableOwnerColumn, 176 sqlReleaseTableCreatedAtColumn, 177 ) 178 179 mock.ExpectBegin() 180 mock. 181 ExpectExec(regexp.QuoteMeta(query)). 182 WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). 183 WillReturnResult(sqlmock.NewResult(1, 1)) 184 mock.ExpectCommit() 185 186 if err := sqlDriver.Create(key, rel); err != nil { 187 t.Fatalf("failed to create release with key %s: %v", key, err) 188 } 189 190 if err := mock.ExpectationsWereMet(); err != nil { 191 t.Errorf("sql expectations weren't met: %v", err) 192 } 193 } 194 195 func TestSqlCreateAlreadyExists(t *testing.T) { 196 vers := 1 197 name := "smug-pigeon" 198 namespace := "default" 199 key := testKey(name, vers) 200 rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) 201 202 sqlDriver, mock := newTestFixtureSQL(t) 203 body, _ := encodeRelease(rel) 204 205 insertQuery := fmt.Sprintf( 206 "INSERT INTO %s (%s,%s,%s,%s,%s,%s,%s,%s,%s) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", 207 sqlReleaseTableName, 208 sqlReleaseTableKeyColumn, 209 sqlReleaseTableTypeColumn, 210 sqlReleaseTableBodyColumn, 211 sqlReleaseTableNameColumn, 212 sqlReleaseTableNamespaceColumn, 213 sqlReleaseTableVersionColumn, 214 sqlReleaseTableStatusColumn, 215 sqlReleaseTableOwnerColumn, 216 sqlReleaseTableCreatedAtColumn, 217 ) 218 219 // Insert fails (primary key already exists) 220 mock.ExpectBegin() 221 mock. 222 ExpectExec(regexp.QuoteMeta(insertQuery)). 223 WithArgs(key, sqlReleaseDefaultType, body, rel.Name, rel.Namespace, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix())). 224 WillReturnError(fmt.Errorf("dialect dependent SQL error")) 225 226 selectQuery := fmt.Sprintf( 227 regexp.QuoteMeta("SELECT %s FROM %s WHERE %s = $1 AND %s = $2"), 228 sqlReleaseTableKeyColumn, 229 sqlReleaseTableName, 230 sqlReleaseTableKeyColumn, 231 sqlReleaseTableNamespaceColumn, 232 ) 233 234 // Let's check that we do make sure the error is due to a release already existing 235 mock. 236 ExpectQuery(selectQuery). 237 WithArgs(key, namespace). 238 WillReturnRows( 239 mock.NewRows([]string{ 240 sqlReleaseTableKeyColumn, 241 }).AddRow( 242 key, 243 ), 244 ).RowsWillBeClosed() 245 mock.ExpectRollback() 246 247 if err := sqlDriver.Create(key, rel); err == nil { 248 t.Fatalf("failed to create release with key %s: %v", key, err) 249 } 250 251 if err := mock.ExpectationsWereMet(); err != nil { 252 t.Errorf("sql expectations weren't met: %v", err) 253 } 254 } 255 256 func TestSqlUpdate(t *testing.T) { 257 vers := 1 258 name := "smug-pigeon" 259 namespace := "default" 260 key := testKey(name, vers) 261 rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) 262 263 sqlDriver, mock := newTestFixtureSQL(t) 264 body, _ := encodeRelease(rel) 265 266 query := fmt.Sprintf( 267 "UPDATE %s SET %s = $1, %s = $2, %s = $3, %s = $4, %s = $5, %s = $6 WHERE %s = $7 AND %s = $8", 268 sqlReleaseTableName, 269 sqlReleaseTableBodyColumn, 270 sqlReleaseTableNameColumn, 271 sqlReleaseTableVersionColumn, 272 sqlReleaseTableStatusColumn, 273 sqlReleaseTableOwnerColumn, 274 sqlReleaseTableModifiedAtColumn, 275 sqlReleaseTableKeyColumn, 276 sqlReleaseTableNamespaceColumn, 277 ) 278 279 mock. 280 ExpectExec(regexp.QuoteMeta(query)). 281 WithArgs(body, rel.Name, int(rel.Version), rel.Info.Status.String(), sqlReleaseDefaultOwner, int(time.Now().Unix()), key, namespace). 282 WillReturnResult(sqlmock.NewResult(0, 1)) 283 284 if err := sqlDriver.Update(key, rel); err != nil { 285 t.Fatalf("failed to update release with key %s: %v", key, err) 286 } 287 288 if err := mock.ExpectationsWereMet(); err != nil { 289 t.Errorf("sql expectations weren't met: %v", err) 290 } 291 } 292 293 func TestSqlQuery(t *testing.T) { 294 // Reflect actual use cases in ../storage.go 295 labelSetUnknown := map[string]string{ 296 "name": "smug-pigeon", 297 "owner": sqlReleaseDefaultOwner, 298 "status": "unknown", 299 } 300 labelSetDeployed := map[string]string{ 301 "name": "smug-pigeon", 302 "owner": sqlReleaseDefaultOwner, 303 "status": "deployed", 304 } 305 labelSetAll := map[string]string{ 306 "name": "smug-pigeon", 307 "owner": sqlReleaseDefaultOwner, 308 } 309 310 supersededRelease := releaseStub("smug-pigeon", 1, "default", rspb.StatusSuperseded) 311 supersededReleaseBody, _ := encodeRelease(supersededRelease) 312 deployedRelease := releaseStub("smug-pigeon", 2, "default", rspb.StatusDeployed) 313 deployedReleaseBody, _ := encodeRelease(deployedRelease) 314 315 // Let's actually start our test 316 sqlDriver, mock := newTestFixtureSQL(t) 317 318 query := fmt.Sprintf( 319 "SELECT %s FROM %s WHERE %s = $1 AND %s = $2 AND %s = $3 AND %s = $4", 320 sqlReleaseTableBodyColumn, 321 sqlReleaseTableName, 322 sqlReleaseTableNameColumn, 323 sqlReleaseTableOwnerColumn, 324 sqlReleaseTableStatusColumn, 325 sqlReleaseTableNamespaceColumn, 326 ) 327 328 mock. 329 ExpectQuery(regexp.QuoteMeta(query)). 330 WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "unknown", "default"). 331 WillReturnRows( 332 mock.NewRows([]string{ 333 sqlReleaseTableBodyColumn, 334 }), 335 ).RowsWillBeClosed() 336 337 mock. 338 ExpectQuery(regexp.QuoteMeta(query)). 339 WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "deployed", "default"). 340 WillReturnRows( 341 mock.NewRows([]string{ 342 sqlReleaseTableBodyColumn, 343 }).AddRow( 344 deployedReleaseBody, 345 ), 346 ).RowsWillBeClosed() 347 348 query = fmt.Sprintf( 349 "SELECT %s FROM %s WHERE %s = $1 AND %s = $2 AND %s = $3", 350 sqlReleaseTableBodyColumn, 351 sqlReleaseTableName, 352 sqlReleaseTableNameColumn, 353 sqlReleaseTableOwnerColumn, 354 sqlReleaseTableNamespaceColumn, 355 ) 356 357 mock. 358 ExpectQuery(regexp.QuoteMeta(query)). 359 WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "default"). 360 WillReturnRows( 361 mock.NewRows([]string{ 362 sqlReleaseTableBodyColumn, 363 }).AddRow( 364 supersededReleaseBody, 365 ).AddRow( 366 deployedReleaseBody, 367 ), 368 ).RowsWillBeClosed() 369 370 _, err := sqlDriver.Query(labelSetUnknown) 371 if err == nil { 372 t.Errorf("Expected error {%v}, got nil", ErrReleaseNotFound) 373 } else if err != ErrReleaseNotFound { 374 t.Fatalf("failed to query for unknown smug-pigeon release: %v", err) 375 } 376 377 results, err := sqlDriver.Query(labelSetDeployed) 378 if err != nil { 379 t.Fatalf("failed to query for deployed smug-pigeon release: %v", err) 380 } 381 382 for _, res := range results { 383 if !reflect.DeepEqual(res, deployedRelease) { 384 t.Errorf("Expected release {%v}, got {%v}", deployedRelease, res) 385 } 386 } 387 388 results, err = sqlDriver.Query(labelSetAll) 389 if err != nil { 390 t.Fatalf("failed to query release history for smug-pigeon: %v", err) 391 } 392 393 if len(results) != 2 { 394 t.Errorf("expected a resultset of size 2, got %d", len(results)) 395 } 396 397 for _, res := range results { 398 if !reflect.DeepEqual(res, deployedRelease) && !reflect.DeepEqual(res, supersededRelease) { 399 t.Errorf("Expected release {%v} or {%v}, got {%v}", deployedRelease, supersededRelease, res) 400 } 401 } 402 403 if err := mock.ExpectationsWereMet(); err != nil { 404 t.Errorf("sql expectations weren't met: %v", err) 405 } 406 } 407 408 func TestSqlDelete(t *testing.T) { 409 vers := 1 410 name := "smug-pigeon" 411 namespace := "default" 412 key := testKey(name, vers) 413 rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) 414 415 body, _ := encodeRelease(rel) 416 417 sqlDriver, mock := newTestFixtureSQL(t) 418 419 selectQuery := fmt.Sprintf( 420 "SELECT %s FROM %s WHERE %s = $1 AND %s = $2", 421 sqlReleaseTableBodyColumn, 422 sqlReleaseTableName, 423 sqlReleaseTableKeyColumn, 424 sqlReleaseTableNamespaceColumn, 425 ) 426 427 mock.ExpectBegin() 428 mock. 429 ExpectQuery(regexp.QuoteMeta(selectQuery)). 430 WithArgs(key, namespace). 431 WillReturnRows( 432 mock.NewRows([]string{ 433 sqlReleaseTableBodyColumn, 434 }).AddRow( 435 body, 436 ), 437 ).RowsWillBeClosed() 438 439 deleteQuery := fmt.Sprintf( 440 "DELETE FROM %s WHERE %s = $1 AND %s = $2", 441 sqlReleaseTableName, 442 sqlReleaseTableKeyColumn, 443 sqlReleaseTableNamespaceColumn, 444 ) 445 446 mock. 447 ExpectExec(regexp.QuoteMeta(deleteQuery)). 448 WithArgs(key, namespace). 449 WillReturnResult(sqlmock.NewResult(0, 1)) 450 mock.ExpectCommit() 451 452 deletedRelease, err := sqlDriver.Delete(key) 453 if err := mock.ExpectationsWereMet(); err != nil { 454 t.Errorf("sql expectations weren't met: %v", err) 455 } 456 if err != nil { 457 t.Fatalf("failed to delete release with key %q: %v", key, err) 458 } 459 460 if !reflect.DeepEqual(rel, deletedRelease) { 461 t.Errorf("Expected release {%v}, got {%v}", rel, deletedRelease) 462 } 463 }