875 lines
24 KiB
Go
875 lines
24 KiB
Go
/*
|
|
* Copyright 2025 coze-dev Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"go.uber.org/mock/gomock"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/coze-dev/coze-studio/backend/api/model/crossdomain/database"
|
|
"github.com/coze-dev/coze-studio/backend/api/model/table"
|
|
entity2 "github.com/coze-dev/coze-studio/backend/domain/memory/database/entity"
|
|
"github.com/coze-dev/coze-studio/backend/domain/memory/database/internal/dal"
|
|
"github.com/coze-dev/coze-studio/backend/domain/memory/database/repository"
|
|
"github.com/coze-dev/coze-studio/backend/infra/contract/rdb"
|
|
rdb2 "github.com/coze-dev/coze-studio/backend/infra/impl/rdb"
|
|
mock "github.com/coze-dev/coze-studio/backend/internal/mock/infra/contract/idgen"
|
|
storageMock "github.com/coze-dev/coze-studio/backend/internal/mock/infra/contract/storage"
|
|
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
|
"github.com/coze-dev/coze-studio/backend/pkg/lang/slices"
|
|
)
|
|
|
|
func setupTestEnv(t *testing.T) (*gorm.DB, rdb.RDB, *mock.MockIDGenerator, repository.DraftDAO, repository.OnlineDAO, Database) {
|
|
dsn := "root:root@tcp(127.0.0.1:3306)/opencoze?charset=utf8mb4&parseTime=True&loc=Local"
|
|
if os.Getenv("CI_JOB_NAME") != "" {
|
|
dsn = strings.ReplaceAll(dsn, "127.0.0.1", "mysql")
|
|
}
|
|
gormDB, err := gorm.Open(mysql.Open(dsn))
|
|
assert.NoError(t, err)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
idGen := mock.NewMockIDGenerator(ctrl)
|
|
|
|
baseID := time.Now().UnixNano()
|
|
idGen.EXPECT().GenID(gomock.Any()).DoAndReturn(func(ctx context.Context) (int64, error) {
|
|
id := baseID
|
|
baseID++
|
|
return id, nil
|
|
}).AnyTimes()
|
|
|
|
idGen.EXPECT().GenMultiIDs(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, count int) ([]int64, error) {
|
|
ids := make([]int64, count)
|
|
for i := 0; i < count; i++ {
|
|
ids[i] = baseID
|
|
baseID++
|
|
}
|
|
return ids, nil
|
|
}).AnyTimes()
|
|
|
|
rdbService := rdb2.NewService(gormDB, idGen)
|
|
draftDAO := dal.NewDraftDatabaseDAO(gormDB, idGen)
|
|
onlineDAO := dal.NewOnlineDatabaseDAO(gormDB, idGen)
|
|
|
|
mockStorage := storageMock.NewMockStorage(ctrl)
|
|
mockStorage.EXPECT().GetObjectUrl(gomock.Any(), gomock.Any()).Return("URL_ADDRESS", nil).AnyTimes()
|
|
mockStorage.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return([]byte("test text"), nil).AnyTimes()
|
|
mockStorage.EXPECT().PutObject(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
|
|
|
|
dbService := NewService(rdbService, gormDB, idGen, mockStorage, nil)
|
|
|
|
return gormDB, rdbService, idGen, draftDAO, onlineDAO, dbService
|
|
}
|
|
|
|
func cleanupTestEnv(t *testing.T, db *gorm.DB, additionalTables ...string) {
|
|
sqlDB, err := db.DB()
|
|
assert.NoError(t, err)
|
|
|
|
daosToClean := []string{"online_database_info", "draft_database_info"}
|
|
for _, tableName := range daosToClean {
|
|
_, err := sqlDB.Exec(fmt.Sprintf("DELETE FROM `%s` WHERE 1=1", tableName))
|
|
if err != nil {
|
|
t.Logf("Failed to clean table %s: %v", tableName, err)
|
|
}
|
|
}
|
|
|
|
rows, err := sqlDB.Query("SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name LIKE 'table_%'")
|
|
assert.NoError(t, err, "Failed to query tables")
|
|
defer rows.Close()
|
|
|
|
var tablesToDrop []string
|
|
for rows.Next() {
|
|
var tableName string
|
|
if err := rows.Scan(&tableName); err != nil {
|
|
t.Logf("Error scanning table name: %v", err)
|
|
continue
|
|
}
|
|
tablesToDrop = append(tablesToDrop, tableName)
|
|
}
|
|
|
|
tablesToDrop = append(tablesToDrop, additionalTables...)
|
|
|
|
for _, tableName := range tablesToDrop {
|
|
_, err := sqlDB.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName))
|
|
if err != nil {
|
|
t.Logf("Failed to drop table %s: %v", tableName, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreateDatabase(t *testing.T) {
|
|
gormDB, _, _, _, onlineDAO, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
req := &CreateDatabaseRequest{
|
|
Database: &database.Database{
|
|
SpaceID: 1,
|
|
CreatorID: 1001,
|
|
|
|
TableName: "test_db_create",
|
|
FieldList: []*database.FieldItem{
|
|
{
|
|
Name: "id_custom",
|
|
Type: table.FieldItemType_Number,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "name",
|
|
Type: table.FieldItemType_Text,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "score",
|
|
Type: table.FieldItemType_Float,
|
|
},
|
|
{
|
|
Name: "date",
|
|
Type: table.FieldItemType_Date,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
resp, err := dbService.CreateDatabase(context.Background(), req)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.NotNil(t, resp.Database)
|
|
|
|
assert.Equal(t, req.Database.TableName, resp.Database.TableName)
|
|
assert.Equal(t, req.Database.TableType, resp.Database.TableType)
|
|
assert.NotEmpty(t, resp.Database.ActualTableName)
|
|
assert.Len(t, resp.Database.FieldList, 4)
|
|
|
|
savedDB, err := onlineDAO.Get(context.Background(), resp.Database.ID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, resp.Database.ID, savedDB.ID)
|
|
assert.Equal(t, resp.Database.TableName, savedDB.TableName)
|
|
}
|
|
|
|
func TestUpdateDatabase(t *testing.T) {
|
|
gormDB, _, _, _, onlineDAO, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
resp, err := createDatabase(dbService)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
|
|
databaseInfo := &entity2.Database{
|
|
ID: resp.Database.ID,
|
|
|
|
SpaceID: 1,
|
|
CreatorID: 1001,
|
|
TableName: "test_db_update",
|
|
TableType: ptr.Of(table.TableType_OnlineTable),
|
|
FieldList: []*database.FieldItem{
|
|
{
|
|
Name: "age",
|
|
Type: table.FieldItemType_Float,
|
|
MustRequired: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
updateReq := &UpdateDatabaseRequest{
|
|
Database: databaseInfo,
|
|
}
|
|
|
|
res, err := dbService.UpdateDatabase(context.Background(), updateReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, res)
|
|
|
|
updatedDB, err := onlineDAO.Get(context.Background(), databaseInfo.ID)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, updatedDB.FieldList, 1)
|
|
}
|
|
|
|
func TestDeleteDatabase(t *testing.T) {
|
|
gormDB, _, _, draftDAO, onlineDAO, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
resp, err := createDatabase(dbService)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
|
|
deleteReq := &DeleteDatabaseRequest{
|
|
ID: resp.Database.ID,
|
|
}
|
|
|
|
err = dbService.DeleteDatabase(context.Background(), deleteReq)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
_, err = draftDAO.Get(context.Background(), resp.Database.ID)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not found")
|
|
|
|
_, err = onlineDAO.Get(context.Background(), resp.Database.ID)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not found")
|
|
}
|
|
|
|
func TestListDatabase(t *testing.T) {
|
|
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
for i := 0; i < 3; i++ {
|
|
resp, err := createDatabase(dbService)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
}
|
|
|
|
spaceID := int64(1)
|
|
tableType := table.TableType_OnlineTable
|
|
listReq := &ListDatabaseRequest{
|
|
SpaceID: &spaceID,
|
|
TableType: tableType,
|
|
Limit: 2,
|
|
}
|
|
resp, err := dbService.ListDatabase(context.Background(), listReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.GreaterOrEqual(t, len(resp.Databases), 2)
|
|
|
|
listReq = &ListDatabaseRequest{
|
|
SpaceID: &spaceID,
|
|
TableType: tableType,
|
|
Limit: 2,
|
|
Offset: 2,
|
|
}
|
|
resp, err = dbService.ListDatabase(context.Background(), listReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.GreaterOrEqual(t, len(resp.Databases), 1)
|
|
}
|
|
|
|
func createDatabase(dbService Database) (*CreateDatabaseResponse, error) {
|
|
req := &CreateDatabaseRequest{
|
|
Database: &entity2.Database{
|
|
SpaceID: 1,
|
|
CreatorID: 1001,
|
|
|
|
TableName: "test_db_table_01",
|
|
FieldList: []*database.FieldItem{
|
|
{
|
|
Name: "id_custom",
|
|
Type: table.FieldItemType_Number,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "name",
|
|
Type: table.FieldItemType_Text,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "score",
|
|
Type: table.FieldItemType_Float,
|
|
},
|
|
{
|
|
Name: "date",
|
|
Type: table.FieldItemType_Date,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return dbService.CreateDatabase(context.Background(), req)
|
|
}
|
|
|
|
func TestCRUDDatabaseRecord(t *testing.T) {
|
|
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
resp, err := createDatabase(dbService)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
|
|
addRecordReq := &AddDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
UserID: 1001,
|
|
Records: []map[string]string{
|
|
{
|
|
"id_custom": "1",
|
|
"name": "John Doe",
|
|
"score": "80.5",
|
|
"date": "2025-01-01 00:00:00",
|
|
},
|
|
{
|
|
"id_custom": "2",
|
|
"name": "Jane Smith",
|
|
"score": "90.5",
|
|
"date": "2025-01-01 01:00:00",
|
|
"bstudio_id": "1",
|
|
},
|
|
},
|
|
}
|
|
|
|
err = dbService.AddDatabaseRecord(context.Background(), addRecordReq)
|
|
assert.NoError(t, err)
|
|
|
|
listRecordReq := &ListDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
UserID: 1001,
|
|
Limit: 50,
|
|
}
|
|
|
|
listResp, err := dbService.ListDatabaseRecord(context.Background(), listRecordReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, listResp)
|
|
assert.True(t, len(listResp.Records) == 2)
|
|
|
|
foundJohn := false
|
|
foundSmith := false
|
|
bsID := ""
|
|
for _, record := range listResp.Records {
|
|
if record["name"] == "John Doe" && record["score"] == "80.5" {
|
|
foundJohn = true
|
|
bsID = record[database.DefaultIDColName]
|
|
}
|
|
if record["name"] == "Jane Smith" && record["score"] == "90.5" {
|
|
foundSmith = true
|
|
}
|
|
}
|
|
assert.True(t, foundJohn, "John Doe record not found")
|
|
assert.True(t, foundSmith, "Jane Smith record not found")
|
|
|
|
updateRecordReq := &UpdateDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
UserID: 1001,
|
|
Records: []map[string]string{
|
|
{
|
|
database.DefaultIDColName: bsID,
|
|
"name": "John Updated",
|
|
"score": "90",
|
|
},
|
|
},
|
|
}
|
|
|
|
err = dbService.UpdateDatabaseRecord(context.Background(), updateRecordReq)
|
|
assert.NoError(t, err)
|
|
|
|
listReq := &ListDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
Limit: 50,
|
|
UserID: 1001,
|
|
}
|
|
listRespAfterUpdate, err := dbService.ListDatabaseRecord(context.Background(), listReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, listRespAfterUpdate)
|
|
assert.True(t, len(listRespAfterUpdate.Records) > 0)
|
|
|
|
foundJohnUpdate := false
|
|
for _, record := range listRespAfterUpdate.Records {
|
|
if record[database.DefaultIDColName] == bsID {
|
|
foundJohnUpdate = true
|
|
assert.Equal(t, "90", record["score"])
|
|
}
|
|
}
|
|
assert.True(t, foundJohnUpdate, "John Doe update record not found")
|
|
|
|
deleteRecordReq := &DeleteDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
UserID: 1001,
|
|
Records: []map[string]string{
|
|
{
|
|
database.DefaultIDColName: bsID,
|
|
},
|
|
},
|
|
}
|
|
|
|
err = dbService.DeleteDatabaseRecord(context.Background(), deleteRecordReq)
|
|
assert.NoError(t, err)
|
|
|
|
listRecordAfterDeleteReq := &ListDatabaseRecordRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
Limit: 50,
|
|
UserID: 1001,
|
|
}
|
|
listRespAfterDelete, err := dbService.ListDatabaseRecord(context.Background(), listRecordAfterDeleteReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, listRespAfterDelete)
|
|
assert.Equal(t, len(listRespAfterDelete.Records), 1)
|
|
}
|
|
|
|
func TestExecuteSQLWithOperations(t *testing.T) {
|
|
gormDB, _, _, _, _, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
resp, err := createDatabase(dbService)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
|
|
fieldMap := slices.ToMap(resp.Database.FieldList, func(e *database.FieldItem) (string, *database.FieldItem) {
|
|
return e.Name, e
|
|
})
|
|
|
|
upsertRows := []*database.UpsertRow{
|
|
{
|
|
Records: []*database.Record{
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Records: []*database.Record{
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
executeInsertReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Insert,
|
|
UpsertRows: upsertRows,
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("111"),
|
|
},
|
|
{
|
|
Value: ptr.Of("Alice"),
|
|
},
|
|
{
|
|
Value: ptr.Of("85.5"),
|
|
},
|
|
{
|
|
Value: ptr.Of("112"),
|
|
},
|
|
{
|
|
Value: ptr.Of("Bob"),
|
|
},
|
|
{
|
|
Value: ptr.Of("90.5"),
|
|
},
|
|
},
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
}
|
|
|
|
insertResp, err := dbService.ExecuteSQL(context.Background(), executeInsertReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, insertResp)
|
|
assert.NotNil(t, insertResp.RowsAffected)
|
|
assert.Equal(t, 2, len(insertResp.Records))
|
|
assert.Equal(t, 1, len(insertResp.Records[0]))
|
|
assert.Equal(t, int64(2), *insertResp.RowsAffected)
|
|
|
|
limit := int64(10)
|
|
selectFields := &database.SelectFieldList{
|
|
FieldID: []string{strconv.FormatInt(fieldMap["name"].AlterID, 10), strconv.FormatInt(fieldMap["score"].AlterID, 10)},
|
|
}
|
|
|
|
executeSelectReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Select,
|
|
// SelectFieldList: selectFields,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
OrderByList: []database.OrderBy{
|
|
{
|
|
Field: "id_custom",
|
|
Direction: table.SortDirection_Desc,
|
|
},
|
|
},
|
|
}
|
|
|
|
selectResp, err := dbService.ExecuteSQL(context.Background(), executeSelectReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, selectResp)
|
|
assert.NotNil(t, selectResp.Records)
|
|
assert.True(t, len(selectResp.Records) == 2)
|
|
assert.Equal(t, string(selectResp.Records[0]["name"].([]uint8)), "Bob")
|
|
assert.NotNil(t, selectResp.Records[0][database.DefaultUidDisplayColName])
|
|
assert.NotNil(t, selectResp.Records[0][database.DefaultIDDisplayColName])
|
|
assert.NotNil(t, selectResp.Records[0][database.DefaultCreateTimeDisplayColName])
|
|
|
|
executeNotNullSelectReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Select,
|
|
SelectFieldList: selectFields,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
OrderByList: []database.OrderBy{
|
|
{
|
|
Field: "id_custom",
|
|
Direction: table.SortDirection_Desc,
|
|
},
|
|
},
|
|
Condition: &database.ComplexCondition{
|
|
Conditions: []*database.Condition{
|
|
{
|
|
Left: "name",
|
|
Operation: database.Operation_IS_NOT_NULL,
|
|
},
|
|
},
|
|
Logic: database.Logic_And,
|
|
},
|
|
}
|
|
|
|
selectNotNullResp, err := dbService.ExecuteSQL(context.Background(), executeNotNullSelectReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, selectNotNullResp)
|
|
assert.NotNil(t, selectNotNullResp.Records)
|
|
assert.True(t, len(selectNotNullResp.Records) == 2)
|
|
assert.Equal(t, string(selectNotNullResp.Records[0]["name"].([]uint8)), "Bob")
|
|
|
|
executeNullSelectReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Select,
|
|
SelectFieldList: selectFields,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
OrderByList: []database.OrderBy{
|
|
{
|
|
Field: "id_custom",
|
|
Direction: table.SortDirection_Desc,
|
|
},
|
|
},
|
|
Condition: &database.ComplexCondition{
|
|
Conditions: []*database.Condition{
|
|
{
|
|
Left: "name",
|
|
Operation: database.Operation_IS_NULL,
|
|
},
|
|
},
|
|
Logic: database.Logic_And,
|
|
},
|
|
}
|
|
|
|
selectNullResp, err := dbService.ExecuteSQL(context.Background(), executeNullSelectReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, selectNullResp)
|
|
assert.True(t, len(selectNullResp.Records) == 0)
|
|
|
|
executeINSelectReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Select,
|
|
SelectFieldList: selectFields,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
OrderByList: []database.OrderBy{
|
|
{
|
|
Field: "id_custom",
|
|
Direction: table.SortDirection_Desc,
|
|
},
|
|
},
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("Alice"),
|
|
},
|
|
{
|
|
Value: ptr.Of("Bob"),
|
|
},
|
|
},
|
|
Condition: &database.ComplexCondition{
|
|
Conditions: []*database.Condition{
|
|
{
|
|
Left: "name",
|
|
Operation: database.Operation_IN,
|
|
Right: "(?,?)",
|
|
},
|
|
},
|
|
Logic: database.Logic_And,
|
|
},
|
|
}
|
|
selectINResp, err := dbService.ExecuteSQL(context.Background(), executeINSelectReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, selectINResp)
|
|
assert.True(t, len(selectINResp.Records) == 2)
|
|
|
|
updateRows := []*database.UpsertRow{
|
|
{
|
|
Records: []*database.Record{
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["id_custom"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["name"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
{
|
|
FieldId: strconv.FormatInt(fieldMap["score"].AlterID, 10),
|
|
FieldValue: "?",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
executeUpdateReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Update,
|
|
UpsertRows: updateRows,
|
|
Limit: &limit,
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("111"),
|
|
},
|
|
{
|
|
Value: ptr.Of("Alice2"),
|
|
},
|
|
{
|
|
Value: ptr.Of("99"),
|
|
},
|
|
{
|
|
Value: ptr.Of("111"),
|
|
},
|
|
},
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
Condition: &database.ComplexCondition{
|
|
Conditions: []*database.Condition{
|
|
{
|
|
Left: "id_custom",
|
|
Operation: database.Operation_EQUAL,
|
|
Right: "?",
|
|
},
|
|
},
|
|
Logic: database.Logic_And,
|
|
},
|
|
}
|
|
|
|
updateResp, err := dbService.ExecuteSQL(context.Background(), executeUpdateReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, updateResp)
|
|
assert.NotNil(t, updateResp.RowsAffected)
|
|
|
|
executeDeleteReq := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Delete,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("111"),
|
|
},
|
|
},
|
|
Condition: &database.ComplexCondition{
|
|
Conditions: []*database.Condition{
|
|
{
|
|
Left: "id_custom",
|
|
Operation: database.Operation_EQUAL,
|
|
Right: "?",
|
|
},
|
|
},
|
|
Logic: database.Logic_And,
|
|
},
|
|
}
|
|
|
|
dResp, err := dbService.ExecuteSQL(context.Background(), executeDeleteReq)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, dResp)
|
|
assert.NotNil(t, dResp.RowsAffected)
|
|
|
|
selectCustom := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Custom,
|
|
Limit: &limit,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
SQL: ptr.Of(fmt.Sprintf("SELECT * FROM %s WHERE score > ?", "test_db_table_01")),
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("85"),
|
|
},
|
|
},
|
|
}
|
|
|
|
selectCustomResp, err := dbService.ExecuteSQL(context.Background(), selectCustom)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, selectCustomResp)
|
|
assert.NotNil(t, selectCustomResp.Records)
|
|
assert.True(t, len(selectCustomResp.Records) > 0)
|
|
|
|
// Test custom SQL UPDATE
|
|
updateCustom := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Custom,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
SQL: ptr.Of(fmt.Sprintf("UPDATE %s SET name = 'Bob Updated' WHERE id_custom = ?", "test_db_table_01")),
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("112"),
|
|
},
|
|
},
|
|
}
|
|
|
|
updateCustomResp, err := dbService.ExecuteSQL(context.Background(), updateCustom)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, updateCustomResp)
|
|
assert.NotNil(t, updateCustomResp.RowsAffected)
|
|
assert.Equal(t, *updateCustomResp.RowsAffected, int64(1))
|
|
|
|
// Test custom SQL DELETE
|
|
deleteCustom := &ExecuteSQLRequest{
|
|
DatabaseID: resp.Database.ID,
|
|
TableType: table.TableType_OnlineTable,
|
|
OperateType: database.OperateType_Custom,
|
|
UserID: "1001",
|
|
SpaceID: 1,
|
|
SQL: ptr.Of(fmt.Sprintf("DELETE FROM %s WHERE id_custom = ?", "test_db_table_01")),
|
|
SQLParams: []*database.SQLParamVal{
|
|
{
|
|
Value: ptr.Of("112"),
|
|
},
|
|
},
|
|
}
|
|
|
|
deleteCustomResp, err := dbService.ExecuteSQL(context.Background(), deleteCustom)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, deleteCustomResp)
|
|
assert.NotNil(t, deleteCustomResp.RowsAffected)
|
|
assert.Equal(t, *deleteCustomResp.RowsAffected, int64(1))
|
|
}
|
|
|
|
func TestDeleteDatabaseByAppID(t *testing.T) {
|
|
gormDB, _, _, draftDAO, onlineDAO, dbService := setupTestEnv(t)
|
|
defer cleanupTestEnv(t, gormDB)
|
|
|
|
appID := int64(123456)
|
|
dbCount := 3
|
|
var onlineIDs []int64
|
|
var draftIDs []int64
|
|
var physicalTables []string
|
|
|
|
for i := 0; i < dbCount; i++ {
|
|
dbName := fmt.Sprintf("test_appid_db_%d_%d", appID, i)
|
|
req := &CreateDatabaseRequest{
|
|
Database: &entity2.Database{
|
|
AppID: appID,
|
|
SpaceID: 1,
|
|
CreatorID: 1001,
|
|
TableName: dbName,
|
|
FieldList: []*database.FieldItem{
|
|
{
|
|
Name: "id_custom",
|
|
Type: table.FieldItemType_Number,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "name",
|
|
Type: table.FieldItemType_Text,
|
|
MustRequired: true,
|
|
},
|
|
{
|
|
Name: "score",
|
|
Type: table.FieldItemType_Float,
|
|
},
|
|
{
|
|
Name: "date",
|
|
Type: table.FieldItemType_Date,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
resp, err := dbService.CreateDatabase(context.Background(), req)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, resp)
|
|
assert.NotNil(t, resp.Database)
|
|
assert.Equal(t, appID, resp.Database.AppID)
|
|
assert.NotEmpty(t, resp.Database.ActualTableName)
|
|
physicalTables = append(physicalTables, resp.Database.ActualTableName)
|
|
if resp.Database.ID != 0 {
|
|
onlineIDs = append(onlineIDs, resp.Database.ID)
|
|
}
|
|
if resp.Database.DraftID != nil {
|
|
draftIDs = append(draftIDs, *resp.Database.DraftID)
|
|
}
|
|
}
|
|
|
|
for _, id := range onlineIDs {
|
|
_, err := onlineDAO.Get(context.Background(), id)
|
|
assert.NoError(t, err)
|
|
}
|
|
for _, id := range draftIDs {
|
|
_, err := draftDAO.Get(context.Background(), id)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
_, err := dbService.DeleteDatabaseByAppID(context.Background(), &DeleteDatabaseByAppIDRequest{AppID: appID})
|
|
assert.NoError(t, err)
|
|
|
|
for _, id := range onlineIDs {
|
|
_, err := onlineDAO.Get(context.Background(), id)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not found")
|
|
}
|
|
for _, id := range draftIDs {
|
|
_, err := draftDAO.Get(context.Background(), id)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not found")
|
|
}
|
|
|
|
sqlDB, err := gormDB.DB()
|
|
assert.NoError(t, err)
|
|
for _, tableName := range physicalTables {
|
|
var cnt int
|
|
err := sqlDB.QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '%s'", tableName)).Scan(&cnt)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, cnt, "physical table %s should be deleted", tableName)
|
|
}
|
|
}
|