package query import ( "context" "errors" "queryorchestration/internal/database" "queryorchestration/internal/database/repository" queryprocessor "queryorchestration/internal/query/processor" "testing" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/pashagolub/pgxmock/v3" "github.com/stretchr/testify/assert" ) func TestGetUpdator(t *testing.T) { pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) queryType := queryprocessor.Type(queryprocessor.TypeContextFull) updator, err := svc.getUpdator(queryType) assert.Nil(t, err) assert.NotNil(t, updator) queryType = queryprocessor.Type(queryprocessor.TypeJsonExtractor) updator, err = svc.getUpdator(queryType) assert.Nil(t, err) assert.NotNil(t, updator) queryType = queryprocessor.Type(-1) _, err = svc.getUpdator(queryType) assert.NotNil(t, err) } func TestSubmitUpdate(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) config := "{\"path\":\"example_path\"}" q := Query{ ID: uuid.New(), RequiredQueryIDs: &[]uuid.UUID{ uuid.New(), }, Config: &config, ActiveVersion: int32(1), LatestVersion: int32(2), } aV := int32(10) update := &queryprocessor.Update{ ID: q.ID, RequiredQueryIDs: &[]uuid.UUID{ uuid.New(), }, Config: q.Config, ActiveVersion: &aV, } pool.ExpectBeginTx(pgx.TxOptions{}) pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[0]), pgxmock.AnyArg()). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[0]), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: RemoveQueryConfig :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: AddQueryConfig :exec").WithArgs(database.MustToDBUUID(update.ID), []byte(*update.Config), int32(3)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectCommit() err = svc.submitUpdate(ctx, &q, update) assert.Nil(t, err) } func TestSubmitUpdateRollback(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) q := Query{ ID: uuid.New(), ActiveVersion: int32(1), LatestVersion: int32(2), } aV := int32(10) update := &queryprocessor.Update{ ID: q.ID, ActiveVersion: &aV, } pool.ExpectBeginTx(pgx.TxOptions{}) msg := "database failure" pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)). WillReturnError(errors.New(msg)) pool.ExpectCommit() err = svc.submitUpdate(ctx, &q, update) assert.EqualError(t, err, msg) } func TestSubmitUpdateRequiredQueries(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) config := "{\"path\":\"example_path\"}" q := Query{ ID: uuid.New(), RequiredQueryIDs: &[]uuid.UUID{ uuid.New(), uuid.New(), uuid.New(), }, Config: &config, ActiveVersion: int32(1), LatestVersion: int32(2), } update := &queryprocessor.Update{ ID: q.ID, RequiredQueryIDs: &[]uuid.UUID{ uuid.New(), uuid.New(), (*q.RequiredQueryIDs)[0], }, } pool.ExpectBeginTx(pgx.TxOptions{}) pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[0]), pgxmock.AnyArg()). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: AddRequiredQuery :exec").WithArgs(database.MustToDBUUID(update.ID), database.MustToDBUUID((*update.RequiredQueryIDs)[1]), pgxmock.AnyArg()). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[1]), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: RemoveRequiredQuery :exec").WithArgs(pgxmock.AnyArg(), database.MustToDBUUID((*q.RequiredQueryIDs)[2]), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectExec("name: UpdateQuery :exec").WithArgs(q.ActiveVersion, int32(3), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectCommit() err = svc.submitUpdate(ctx, &q, update) assert.Nil(t, err) } func TestSubmitUpdateActiveVersion(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) q := Query{ ID: uuid.New(), ActiveVersion: int32(1), LatestVersion: int32(2), } aV := int32(10) update := &queryprocessor.Update{ ID: q.ID, ActiveVersion: &aV, } pool.ExpectBeginTx(pgx.TxOptions{}) pool.ExpectExec("name: UpdateQuery :exec").WithArgs(aV, int32(3), database.MustToDBUUID(update.ID)). WillReturnResult(pgxmock.NewResult("", 1)) pool.ExpectCommit() err = svc.submitUpdate(ctx, &q, update) assert.Nil(t, err) } func TestSubmitUpdateNoChange(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) q := Query{ ID: uuid.New(), ActiveVersion: int32(1), LatestVersion: int32(2), } update := &queryprocessor.Update{ ID: q.ID, } pool.ExpectBeginTx(pgx.TxOptions{}) pool.ExpectRollback() err = svc.submitUpdate(ctx, &q, update) assert.Nil(t, err) } func TestGetSetDifference(t *testing.T) { commonUUID := uuid.New() listA := []uuid.UUID{uuid.New(), commonUUID} listB := []uuid.UUID{uuid.New(), commonUUID} assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(nil, nil)) assert.ElementsMatch(t, listA, getSetDifference(&listA, nil)) assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(nil, &listB)) assert.ElementsMatch(t, []uuid.UUID{listA[0]}, getSetDifference(&listA, &listB)) assert.ElementsMatch(t, []uuid.UUID{listB[0]}, getSetDifference(&listB, &listA)) assert.ElementsMatch(t, []uuid.UUID{}, getSetDifference(&listA, &listA)) } func TestNormalizeUpdate(t *testing.T) { ctx := context.Background() pool, err := pgxmock.NewPool() if err != nil { t.Fatalf("failed to open pgxmock database: %v", err) } queries := repository.New(pool) db := &database.Connection{ Queries: queries, Pool: pool, } svc := New(db) current := &Query{ ID: uuid.New(), ActiveVersion: int32(1), LatestVersion: int32(2), Type: queryprocessor.TypeJsonExtractor, } cfg := "{}" aV := int32(2) update := &queryprocessor.Update{ ID: current.ID, Config: &cfg, ActiveVersion: &aV, RequiredQueryIDs: &[]uuid.UUID{}, } dbids := database.MustToDBUUIDArray(*update.RequiredQueryIDs) pool.ExpectQuery("name: AllQueriesExist :one").WithArgs(dbids).WillReturnRows( pgxmock.NewRows([]string{"all_exist"}). AddRow(true), ) err = svc.normalizeUpdate(ctx, current, update) assert.Nil(t, err) assert.EqualExportedValues(t, queryprocessor.Update{ ID: current.ID, ActiveVersion: &aV, Config: nil, RequiredQueryIDs: nil, }, *update) }