github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/upsert_test.go (about) 1 package repo_test 2 3 import ( 4 "context" 5 "fmt" 6 "regexp" 7 "testing" 8 "time" 9 10 "github.com/kyma-incubator/compass/components/director/pkg/resource" 11 12 "github.com/stretchr/testify/assert" 13 14 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 15 16 "github.com/lib/pq" 17 18 "github.com/DATA-DOG/go-sqlmock" 19 20 "github.com/kyma-incubator/compass/components/director/internal/repo" 21 "github.com/kyma-incubator/compass/components/director/internal/repo/testdb" 22 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestUpsertGlobal(t *testing.T) { 27 expectedQuery := regexp.QuoteMeta(`INSERT INTO users ( id, tenant_id, first_name, last_name, age ) 28 VALUES ( ?, ?, ?, ?, ? ) ON CONFLICT ( tenant_id, first_name, last_name ) DO UPDATE SET age=EXCLUDED.age`) 29 expectedQueryWithTenantCheck := regexp.QuoteMeta(`INSERT INTO users ( id, tenant_id, first_name, last_name, age ) 30 VALUES ( ?, ?, ?, ?, ? ) ON CONFLICT ( tenant_id, first_name, last_name ) DO UPDATE SET age=EXCLUDED.age WHERE users.tenant_id = ?`) 31 32 sut := repo.NewUpserterGlobal(UserType, "users", []string{"id", "tenant_id", "first_name", "last_name", "age"}, []string{"tenant_id", "first_name", "last_name"}, []string{"age"}) 33 sutWithEmbededTenant := repo.NewUpserterWithEmbeddedTenant(UserType, "users", []string{"id", "tenant_id", "first_name", "last_name", "age"}, []string{"tenant_id", "first_name", "last_name"}, []string{"age"}, "tenant_id") 34 35 t.Run("success", func(t *testing.T) { 36 // GIVEN 37 db, mock := testdb.MockDatabase(t) 38 ctx := persistence.SaveToContext(context.TODO(), db) 39 defer mock.AssertExpectations(t) 40 givenUser := User{ 41 ID: "given_id", 42 Tenant: "given_tenant", 43 FirstName: "given_first_name", 44 LastName: "given_last_name", 45 Age: 55, 46 } 47 48 mock.ExpectExec(expectedQuery). 49 WithArgs("given_id", "given_tenant", "given_first_name", "given_last_name", 55).WillReturnResult(sqlmock.NewResult(1, 1)) 50 // WHEN 51 err := sut.UpsertGlobal(ctx, givenUser) 52 // THEN 53 require.NoError(t, err) 54 }) 55 56 t.Run("returns error when operation on db failed", func(t *testing.T) { 57 // GIVEN 58 db, mock := testdb.MockDatabase(t) 59 ctx := persistence.SaveToContext(context.TODO(), db) 60 defer mock.AssertExpectations(t) 61 givenUser := User{} 62 63 mock.ExpectExec(expectedQuery). 64 WillReturnError(someError()) 65 // WHEN 66 err := sut.UpsertGlobal(ctx, givenUser) 67 // THEN 68 require.EqualError(t, err, "Internal Server Error: Unexpected error while executing SQL query") 69 }) 70 71 t.Run("context properly canceled", func(t *testing.T) { 72 db, mock := testdb.MockDatabase(t) 73 defer mock.AssertExpectations(t) 74 givenUser := User{} 75 76 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 77 defer cancel() 78 79 ctx = persistence.SaveToContext(ctx, db) 80 81 err := sut.UpsertGlobal(ctx, givenUser) 82 83 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 84 }) 85 86 t.Run("returns non unique error", func(t *testing.T) { 87 // GIVEN 88 db, mock := testdb.MockDatabase(t) 89 ctx := persistence.SaveToContext(context.TODO(), db) 90 defer mock.AssertExpectations(t) 91 givenUser := User{} 92 93 mock.ExpectExec(expectedQuery). 94 WillReturnError(&pq.Error{Code: persistence.UniqueViolation}) 95 // WHEN 96 err := sut.UpsertGlobal(ctx, givenUser) 97 // THEN 98 require.True(t, apperrors.IsNotUniqueError(err)) 99 }) 100 101 t.Run("returns error if missing persistence context", func(t *testing.T) { 102 // WHEN 103 err := sut.UpsertGlobal(context.TODO(), User{}) 104 // THEN 105 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 106 }) 107 108 t.Run("returns error if destination is nil", func(t *testing.T) { 109 // WHEN 110 err := sut.UpsertGlobal(context.TODO(), nil) 111 // THEN 112 require.EqualError(t, err, apperrors.NewInternalError("item cannot be nil").Error()) 113 }) 114 115 t.Run("success with embedded tenant", func(t *testing.T) { 116 // GIVEN 117 db, mock := testdb.MockDatabase(t) 118 ctx := persistence.SaveToContext(context.TODO(), db) 119 defer mock.AssertExpectations(t) 120 givenUser := User{ 121 ID: "given_id", 122 Tenant: "given_tenant", 123 FirstName: "given_first_name", 124 LastName: "given_last_name", 125 Age: 55, 126 } 127 128 mock.ExpectExec(expectedQueryWithTenantCheck). 129 WithArgs("given_id", "given_tenant", "given_first_name", "given_last_name", 55, "given_tenant").WillReturnResult(sqlmock.NewResult(1, 1)) 130 // WHEN 131 err := sutWithEmbededTenant.UpsertGlobal(ctx, givenUser) 132 // THEN 133 require.NoError(t, err) 134 }) 135 } 136 137 func TestUpsert(t *testing.T) { 138 expectedQuery := regexp.QuoteMeta(`INSERT INTO apps ( id, name, description ) VALUES ( $1, $2, $3 ) ON CONFLICT ( id ) DO UPDATE SET name=EXCLUDED.name, description=EXCLUDED.description WHERE (apps.id IN (SELECT id FROM tenant_applications WHERE tenant_id = $4 AND owner = true)) RETURNING id;`) 139 140 expectedTenantAccessQuery := regexp.QuoteMeta(fmt.Sprintf("WITH RECURSIVE parents AS (SELECT t1.id, t1.parent FROM business_tenant_mappings t1 WHERE id = ? UNION ALL SELECT t2.id, t2.parent FROM business_tenant_mappings t2 INNER JOIN parents t on t2.id = t.parent) INSERT INTO %s ( %s, %s, %s ) (SELECT parents.id AS tenant_id, ? as id, ? AS owner FROM parents)", "tenant_applications", repo.M2MTenantIDColumn, repo.M2MResourceIDColumn, repo.M2MOwnerColumn)) 141 142 sut := repo.NewUpserter(appTableName, []string{"id", "name", "description"}, []string{"id"}, []string{"name", "description"}) 143 144 resourceType := resource.Application 145 tenant := "tenant" 146 147 t.Run("success", func(t *testing.T) { 148 // GIVEN 149 db, mock := testdb.MockDatabase(t) 150 ctx := persistence.SaveToContext(context.TODO(), db) 151 defer mock.AssertExpectations(t) 152 153 rows := sqlmock.NewRows([]string{"id"}).AddRow(appID) 154 mock.ExpectQuery(expectedQuery). 155 WithArgs(appID, appName, appDescription, tenant).WillReturnRows(rows) 156 mock.ExpectExec(expectedTenantAccessQuery). 157 WithArgs(tenant, appID, true).WillReturnResult(sqlmock.NewResult(1, 1)) 158 // WHEN 159 _, err := sut.Upsert(ctx, resourceType, tenant, fixApp) 160 // THEN 161 require.NoError(t, err) 162 }) 163 164 t.Run("returns error when upsert operation failed", func(t *testing.T) { 165 // GIVEN 166 db, mock := testdb.MockDatabase(t) 167 ctx := persistence.SaveToContext(context.TODO(), db) 168 defer mock.AssertExpectations(t) 169 170 mock.ExpectQuery(expectedQuery). 171 WithArgs(appID, appName, appDescription, tenant).WillReturnError(someError()) 172 // WHEN 173 _, err := sut.Upsert(ctx, resourceType, tenant, fixApp) 174 // THEN 175 require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query") 176 }) 177 178 t.Run("returns error when adding tenant access record failed", func(t *testing.T) { 179 // GIVEN 180 db, mock := testdb.MockDatabase(t) 181 ctx := persistence.SaveToContext(context.TODO(), db) 182 defer mock.AssertExpectations(t) 183 184 rows := sqlmock.NewRows([]string{"id"}).AddRow(appID) 185 mock.ExpectQuery(expectedQuery). 186 WithArgs(appID, appName, appDescription, tenant).WillReturnRows(rows) 187 mock.ExpectExec(expectedTenantAccessQuery). 188 WithArgs(tenant, appID, true).WillReturnError(someError()) 189 // WHEN 190 _, err := sut.Upsert(ctx, resourceType, tenant, fixApp) 191 // THEN 192 require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query") 193 }) 194 195 t.Run("context properly canceled", func(t *testing.T) { 196 db, mock := testdb.MockDatabase(t) 197 defer mock.AssertExpectations(t) 198 199 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 200 defer cancel() 201 202 ctx = persistence.SaveToContext(ctx, db) 203 204 _, err := sut.Upsert(ctx, resourceType, tenant, fixApp) 205 206 require.EqualError(t, err, "Internal Server Error: Maximum processing timeout reached") 207 }) 208 209 t.Run("returns error if missing persistence context", func(t *testing.T) { 210 // WHEN 211 _, err := sut.Upsert(context.TODO(), resourceType, tenant, fixApp) 212 // THEN 213 require.EqualError(t, err, apperrors.NewInternalError("unable to fetch database from context").Error()) 214 }) 215 216 t.Run("returns error if destination is nil", func(t *testing.T) { 217 // WHEN 218 _, err := sut.Upsert(context.TODO(), resourceType, tenant, nil) 219 // THEN 220 require.EqualError(t, err, apperrors.NewInternalError("item cannot be nil").Error()) 221 }) 222 223 t.Run("returns error if the entity does not have accessTable", func(t *testing.T) { 224 // GIVEN 225 db, mock := testdb.MockDatabase(t) 226 ctx := persistence.SaveToContext(context.TODO(), db) 227 defer mock.AssertExpectations(t) 228 229 // WHEN 230 _, err := sut.Upsert(ctx, resource.Tenant, tenant, fixApp) 231 // THEN 232 require.Contains(t, err.Error(), "entity tenant does not have access table") 233 }) 234 } 235 236 func TestUpsertGlobalWhenWrongConfiguration(t *testing.T) { 237 sut := repo.NewUpserterGlobal("users", "UserType", []string{"id", "tenant_id", "column_does_not_exist"}, []string{"id", "tenant_id"}, []string{"column_does_not_exist"}) 238 // GIVEN 239 db, mock := testdb.MockDatabase(t) 240 ctx := persistence.SaveToContext(context.TODO(), db) 241 defer mock.AssertExpectations(t) 242 // WHEN 243 err := sut.UpsertGlobal(ctx, User{}) 244 // THEN 245 require.Error(t, err) 246 assert.Contains(t, err.Error(), "Unexpected error while executing SQL query") 247 }