Merge pull request #999 from trheyi/main
Enhance model loading and error handling in YaoModel
This commit is contained in:
commit
2c8b871c57
4 changed files with 655 additions and 11 deletions
229
dsl/model/cases_test.go
Normal file
229
dsl/model/cases_test.go
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/yaoapp/gou/application"
|
||||||
|
"github.com/yaoapp/gou/model"
|
||||||
|
"github.com/yaoapp/kun/log"
|
||||||
|
"github.com/yaoapp/yao/config"
|
||||||
|
"github.com/yaoapp/yao/data"
|
||||||
|
"github.com/yaoapp/yao/dsl/types"
|
||||||
|
"github.com/yaoapp/yao/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
// systemModels system models
|
||||||
|
var systemModels = map[string]string{
|
||||||
|
"__yao.dsl": "yao/models/dsl.mod.yao",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
// Setup
|
||||||
|
test.Prepare(&testing.T{}, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
// Load system models
|
||||||
|
model.WithCrypt([]byte(fmt.Sprintf(`{"key":"%s"}`, config.Conf.DB.AESKey)), "AES")
|
||||||
|
model.WithCrypt([]byte(`{}`), "PASSWORD")
|
||||||
|
err := loadSystemModels()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Load system models error: %s", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
code := m.Run()
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadSystemModels load system models
|
||||||
|
func loadSystemModels() error {
|
||||||
|
for id, path := range systemModels {
|
||||||
|
content, err := data.Read(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse model
|
||||||
|
var data map[string]interface{}
|
||||||
|
err = application.Parse(path, content, &data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set prefix
|
||||||
|
if table, ok := data["table"].(map[string]interface{}); ok {
|
||||||
|
if name, ok := table["name"].(string); ok {
|
||||||
|
table["name"] = "__yao_" + name
|
||||||
|
content, err = jsoniter.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to marshal model data: %v", err)
|
||||||
|
return fmt.Errorf("failed to marshal model data: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Model
|
||||||
|
mod, err := model.LoadSource(content, id, filepath.Join("__system", path))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("load system model %s error: %s", id, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop table first
|
||||||
|
err = mod.DropTable()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("drop table error: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto migrate
|
||||||
|
err = mod.Migrate(false, model.WithDonotInsertValues(true))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("migrate system model %s error: %s", id, err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanTestData cleans test data from database
|
||||||
|
func cleanTestData() error {
|
||||||
|
m := model.Select("__yao.dsl")
|
||||||
|
err := m.DropTable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = m.Migrate(false, model.WithDonotInsertValues(true))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTestID generates a unique test ID
|
||||||
|
func getTestID() string {
|
||||||
|
return fmt.Sprintf("test_%d", time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCase defines a single test case
|
||||||
|
type TestCase struct {
|
||||||
|
ID string
|
||||||
|
Source string
|
||||||
|
UpdatedSource string
|
||||||
|
Tags []string
|
||||||
|
Label string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestCase creates a new test case
|
||||||
|
func NewTestCase() *TestCase {
|
||||||
|
id := getTestID()
|
||||||
|
return &TestCase{
|
||||||
|
ID: id,
|
||||||
|
Source: fmt.Sprintf(`{
|
||||||
|
"name": "%s",
|
||||||
|
"table": { "name": "%s", "comment": "Test User" },
|
||||||
|
"columns": [
|
||||||
|
{ "name": "id", "type": "ID" },
|
||||||
|
{ "name": "name", "type": "string", "length": 80, "comment": "User Name", "index": true },
|
||||||
|
{ "name": "status", "type": "enum", "option": ["active", "disabled"], "default": "active", "comment": "Status", "index": true }
|
||||||
|
],
|
||||||
|
"tags": ["test_%s"],
|
||||||
|
"label": "Test Label",
|
||||||
|
"description": "Test Description",
|
||||||
|
"option": { "timestamps": true, "soft_deletes": true }
|
||||||
|
}`, id, id, id),
|
||||||
|
UpdatedSource: fmt.Sprintf(`{
|
||||||
|
"name": "%s",
|
||||||
|
"table": { "name": "%s", "comment": "Updated Test User" },
|
||||||
|
"columns": [
|
||||||
|
{ "name": "id", "type": "ID" },
|
||||||
|
{ "name": "name", "type": "string", "length": 80, "comment": "User Name", "index": true },
|
||||||
|
{ "name": "status", "type": "enum", "option": ["active", "disabled", "pending"], "default": "active", "comment": "Status", "index": true }
|
||||||
|
],
|
||||||
|
"tags": ["test_%s", "updated"],
|
||||||
|
"label": "Updated Label",
|
||||||
|
"description": "Updated Description",
|
||||||
|
"option": { "timestamps": true, "soft_deletes": true }
|
||||||
|
}`, id, id, id),
|
||||||
|
Tags: []string{fmt.Sprintf("test_%s", id)},
|
||||||
|
Label: "Test Label",
|
||||||
|
Description: "Test Description",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptions returns creation options
|
||||||
|
func (tc *TestCase) CreateOptions() *types.CreateOptions {
|
||||||
|
return &types.CreateOptions{
|
||||||
|
ID: tc.ID,
|
||||||
|
Source: tc.Source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptions returns update options
|
||||||
|
func (tc *TestCase) UpdateOptions() *types.UpdateOptions {
|
||||||
|
return &types.UpdateOptions{
|
||||||
|
ID: tc.ID,
|
||||||
|
Source: tc.UpdatedSource,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateInfoOptions returns update info options
|
||||||
|
func (tc *TestCase) UpdateInfoOptions() *types.UpdateOptions {
|
||||||
|
return &types.UpdateOptions{
|
||||||
|
ID: tc.ID,
|
||||||
|
Info: &types.Info{
|
||||||
|
Label: "Updated via Info",
|
||||||
|
Tags: []string{"tag1", "info"},
|
||||||
|
Description: "Updated via info field",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOptions returns list options
|
||||||
|
func (tc *TestCase) ListOptions(withSource bool) *types.ListOptions {
|
||||||
|
return &types.ListOptions{
|
||||||
|
Source: withSource,
|
||||||
|
Tags: tc.Tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertInfo verifies if the information is correct
|
||||||
|
func (tc *TestCase) AssertInfo(info *types.Info) bool {
|
||||||
|
if info == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.ID == tc.ID &&
|
||||||
|
info.Label == tc.Label &&
|
||||||
|
len(info.Tags) == len(tc.Tags) &&
|
||||||
|
info.Description == tc.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertUpdatedInfo verifies if the updated information is correct
|
||||||
|
func (tc *TestCase) AssertUpdatedInfo(info *types.Info) bool {
|
||||||
|
if info == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.ID == tc.ID &&
|
||||||
|
info.Label == "Updated Label" &&
|
||||||
|
len(info.Tags) == 2 &&
|
||||||
|
info.Description == "Updated Description"
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertUpdatedInfoViaInfo verifies if the information updated via Info is correct
|
||||||
|
func (tc *TestCase) AssertUpdatedInfoViaInfo(info *types.Info) bool {
|
||||||
|
if info == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.ID == tc.ID &&
|
||||||
|
info.Label == "Updated via Info" &&
|
||||||
|
len(info.Tags) == 2 &&
|
||||||
|
info.Description == "Updated via info field"
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/yaoapp/gou/model"
|
"github.com/yaoapp/gou/model"
|
||||||
|
"github.com/yaoapp/kun/exception"
|
||||||
"github.com/yaoapp/yao/dsl/types"
|
"github.com/yaoapp/yao/dsl/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -65,10 +66,44 @@ func (m *YaoModel) Load(ctx context.Context, options *types.LoadOptions) error {
|
||||||
reset = v.(bool)
|
reset = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
path := types.ToPath(types.TypeModel, options.ID)
|
var mod *model.Model
|
||||||
mod, err := model.LoadSync(path, options.ID)
|
var err error
|
||||||
if err != nil {
|
|
||||||
return err
|
// Case 1: If Source is provided, use LoadSource
|
||||||
|
if options.Source != "" {
|
||||||
|
mod, err = model.LoadSourceSync([]byte(options.Source), options.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if options.Path != "" && options.Store == "fs" {
|
||||||
|
// Case 2: If Path is provided and Store is fs, use LoadSync with Path
|
||||||
|
mod, err = model.LoadSync(options.Path, options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if options.Store == "db" {
|
||||||
|
// Case 3: If Store is db, get Source from DB first
|
||||||
|
if m.db == nil {
|
||||||
|
return fmt.Errorf("db io is required for store type db")
|
||||||
|
}
|
||||||
|
source, exists, err := m.db.Source(options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("model %s not found in database", options.ID)
|
||||||
|
}
|
||||||
|
mod, err = model.LoadSourceSync([]byte(source), options.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case 4: Default case, use LoadSync with ID
|
||||||
|
path := types.ToPath(types.TypeModel, options.ID)
|
||||||
|
mod, err = model.LoadSync(path, options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if migration || reset {
|
if migration || reset {
|
||||||
|
|
@ -99,7 +134,28 @@ func (m *YaoModel) Unload(ctx context.Context, options *types.UnloadOptions) err
|
||||||
dropTable = v.(bool)
|
dropTable = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
mod := model.Select(options.ID)
|
// Try to get model, handle panic
|
||||||
|
var mod *model.Model
|
||||||
|
var err error
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if ex, ok := r.(exception.Exception); ok {
|
||||||
|
if ex.Message == fmt.Sprintf("Model:%s; not found", options.ID) {
|
||||||
|
err = fmt.Errorf("model %s not found", options.ID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
mod = model.Select(options.ID)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if mod == nil {
|
if mod == nil {
|
||||||
return fmt.Errorf("model %s not found", options.ID)
|
return fmt.Errorf("model %s not found", options.ID)
|
||||||
}
|
}
|
||||||
|
|
@ -137,16 +193,50 @@ func (m *YaoModel) Reload(ctx context.Context, options *types.ReloadOptions) err
|
||||||
reset = v.(bool)
|
reset = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload the model
|
var mod *model.Model
|
||||||
path := types.ToPath(types.TypeModel, options.ID)
|
var err error
|
||||||
mod, err := model.LoadSync(path, options.ID)
|
|
||||||
if err != nil {
|
// Case 1: If Source is provided, use LoadSource
|
||||||
return err
|
if options.Source != "" {
|
||||||
|
mod, err = model.LoadSourceSync([]byte(options.Source), options.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if options.Path != "" && options.Store == "fs" {
|
||||||
|
// Case 2: If Path is provided and Store is fs, use LoadSync with Path
|
||||||
|
mod, err = model.LoadSync(options.Path, options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if options.Store == "db" {
|
||||||
|
// Case 3: If Store is db, get Source from DB first
|
||||||
|
if m.db == nil {
|
||||||
|
return fmt.Errorf("db io is required for store type db")
|
||||||
|
}
|
||||||
|
source, exists, err := m.db.Source(options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("model %s not found in database", options.ID)
|
||||||
|
}
|
||||||
|
mod, err = model.LoadSourceSync([]byte(source), options.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case 4: Default case, use LoadSync with ID
|
||||||
|
path := types.ToPath(types.TypeModel, options.ID)
|
||||||
|
mod, err = model.LoadSync(path, options.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if migrate || reset {
|
if migrate || reset {
|
||||||
return mod.Migrate(reset, model.WithDonotInsertValues(true))
|
return mod.Migrate(reset, model.WithDonotInsertValues(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
326
dsl/model/model_test.go
Normal file
326
dsl/model/model_test.go
Normal file
|
|
@ -0,0 +1,326 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/yaoapp/yao/dsl/io"
|
||||||
|
"github.com/yaoapp/yao/dsl/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModelLoad(t *testing.T) {
|
||||||
|
testCase := NewTestCase()
|
||||||
|
fsio := io.NewFS(types.TypeModel)
|
||||||
|
dbio := io.NewDB(types.TypeModel)
|
||||||
|
manager := New("", fsio, dbio)
|
||||||
|
|
||||||
|
// Test Load with nil options
|
||||||
|
err := manager.Load(context.Background(), nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "load options is required")
|
||||||
|
|
||||||
|
// Test Load with empty ID
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "load options id is required")
|
||||||
|
|
||||||
|
// Test Load with Source
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID,
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load from filesystem
|
||||||
|
err = fsio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
path := types.ToPath(types.TypeModel, testCase.ID+"_fs")
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Path: path,
|
||||||
|
Store: "fs",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load from database
|
||||||
|
err = dbio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load with default path (should use filesystem)
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load with migration
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Options: map[string]interface{}{"migration": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load with reset
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Options: map[string]interface{}{"reset": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
err = fsio.Delete(testCase.ID + "_fs")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = dbio.Delete(testCase.ID + "_db")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = cleanTestData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelLoadWithDB(t *testing.T) {
|
||||||
|
testCase := NewTestCase()
|
||||||
|
dbio := io.NewDB(types.TypeModel)
|
||||||
|
manager := New("", nil, dbio)
|
||||||
|
|
||||||
|
// Create model in DB first
|
||||||
|
err := dbio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID,
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load with Store=db
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID,
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load non-existent model from DB
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: "non-existent",
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "not found in database")
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
err = dbio.Delete(testCase.ID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = cleanTestData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelUnload(t *testing.T) {
|
||||||
|
testCase := NewTestCase()
|
||||||
|
fsio := io.NewFS(types.TypeModel)
|
||||||
|
dbio := io.NewDB(types.TypeModel)
|
||||||
|
manager := New("", fsio, dbio)
|
||||||
|
|
||||||
|
// Test Unload with nil options
|
||||||
|
err := manager.Unload(context.Background(), nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unload options is required")
|
||||||
|
|
||||||
|
// Test Unload with empty ID
|
||||||
|
err = manager.Unload(context.Background(), &types.UnloadOptions{})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "unload options id is required")
|
||||||
|
|
||||||
|
// Test Unload non-existent model
|
||||||
|
err = manager.Unload(context.Background(), &types.UnloadOptions{
|
||||||
|
ID: "non-existent",
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "model non-existent not found")
|
||||||
|
|
||||||
|
// Test Unload from filesystem
|
||||||
|
err = fsio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Store: "fs",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Unload(context.Background(), &types.UnloadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Options: map[string]interface{}{"dropTable": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Unload from database
|
||||||
|
err = dbio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Unload(context.Background(), &types.UnloadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Options: map[string]interface{}{"dropTable": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
err = fsio.Delete(testCase.ID + "_fs")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = dbio.Delete(testCase.ID + "_db")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = cleanTestData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelReload(t *testing.T) {
|
||||||
|
testCase := NewTestCase()
|
||||||
|
fsio := io.NewFS(types.TypeModel)
|
||||||
|
dbio := io.NewDB(types.TypeModel)
|
||||||
|
manager := New("", fsio, dbio)
|
||||||
|
|
||||||
|
// Test Reload with nil options
|
||||||
|
err := manager.Reload(context.Background(), nil)
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "reload options is required")
|
||||||
|
|
||||||
|
// Test Reload with empty ID
|
||||||
|
err = manager.Reload(context.Background(), &types.ReloadOptions{})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "reload options id is required")
|
||||||
|
|
||||||
|
// Test Reload from filesystem
|
||||||
|
err = fsio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Store: "fs",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Reload(context.Background(), &types.ReloadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Store: "fs",
|
||||||
|
Options: map[string]interface{}{"migrate": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Reload from database
|
||||||
|
err = dbio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Reload(context.Background(), &types.ReloadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Store: "db",
|
||||||
|
Options: map[string]interface{}{"migrate": true},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
err = fsio.Delete(testCase.ID + "_fs")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = dbio.Delete(testCase.ID + "_db")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = cleanTestData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelLoaded(t *testing.T) {
|
||||||
|
testCase := NewTestCase()
|
||||||
|
fsio := io.NewFS(types.TypeModel)
|
||||||
|
dbio := io.NewDB(types.TypeModel)
|
||||||
|
manager := New("", fsio, dbio)
|
||||||
|
|
||||||
|
// Test Load from filesystem
|
||||||
|
err := fsio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_fs",
|
||||||
|
Store: "fs",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Load from database
|
||||||
|
err = dbio.Create(&types.CreateOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Source: testCase.Source,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = manager.Load(context.Background(), &types.LoadOptions{
|
||||||
|
ID: testCase.ID + "_db",
|
||||||
|
Store: "db",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test Loaded
|
||||||
|
infos, err := manager.Loaded(context.Background())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, infos)
|
||||||
|
assert.Contains(t, infos, testCase.ID+"_fs")
|
||||||
|
assert.Contains(t, infos, testCase.ID+"_db")
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
err = fsio.Delete(testCase.ID + "_fs")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = dbio.Delete(testCase.ID + "_db")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = cleanTestData()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelValidate(t *testing.T) {
|
||||||
|
manager := New("", nil, nil)
|
||||||
|
|
||||||
|
// Test Validate
|
||||||
|
valid, messages := manager.Validate(context.Background(), "test source")
|
||||||
|
assert.True(t, valid)
|
||||||
|
assert.Empty(t, messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelExecute(t *testing.T) {
|
||||||
|
manager := New("", nil, nil)
|
||||||
|
|
||||||
|
// Test Execute
|
||||||
|
result, err := manager.Execute(context.Background(), "test_id", "test_method")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Contains(t, err.Error(), "Not implemented")
|
||||||
|
assert.Nil(t, result)
|
||||||
|
}
|
||||||
|
|
@ -145,7 +145,6 @@ type LoadOptions struct {
|
||||||
type UnloadOptions struct {
|
type UnloadOptions struct {
|
||||||
ID string
|
ID string
|
||||||
Path string
|
Path string
|
||||||
Source string
|
|
||||||
Store StoreType
|
Store StoreType
|
||||||
Options map[string]interface{}
|
Options map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue