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  }