mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-12 01:24:12 +08:00
- 聚合表级失败并保留已确认提交计数,避免任务误报成功 - 绑定分析上下文与实际同步模式,阻止陈旧或无效配置执行 - 加固全量覆盖及同端点分页写入,补充跨库与失败回归测试 Refs #846
124 lines
3.9 KiB
Go
124 lines
3.9 KiB
Go
package sync
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"GoNavi-Wails/internal/connection"
|
|
)
|
|
|
|
type directImportIntegrityTargetDB struct {
|
|
fakeQuerySyncTargetDB
|
|
getColumnsErr error
|
|
execErr error
|
|
execs []string
|
|
}
|
|
|
|
func (d *directImportIntegrityTargetDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
|
|
if d.getColumnsErr != nil {
|
|
return nil, d.getColumnsErr
|
|
}
|
|
return d.fakeQuerySyncTargetDB.GetColumns(dbName, tableName)
|
|
}
|
|
|
|
func (d *directImportIntegrityTargetDB) Exec(query string) (int64, error) {
|
|
d.execs = append(d.execs, query)
|
|
if d.execErr != nil {
|
|
return 0, d.execErr
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func (d *directImportIntegrityTargetDB) clearedTarget() bool {
|
|
for _, query := range d.execs {
|
|
upper := strings.ToUpper(query)
|
|
if strings.Contains(upper, "TRUNCATE TABLE") || strings.Contains(upper, "DELETE FROM") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestDirectImportFullOverwriteValidatesTargetColumnsBeforeClearing(t *testing.T) {
|
|
engine := &SyncEngine{}
|
|
source := &fakeMigrationDB{}
|
|
target := &directImportIntegrityTargetDB{getColumnsErr: errors.New("metadata unavailable")}
|
|
config := SyncConfig{
|
|
JobID: "direct-import-columns",
|
|
Mode: "full_overwrite",
|
|
SourceConfig: connection.ConnectionConfig{Type: "mysql", Host: "source", Database: "src"},
|
|
TargetConfig: connection.ConnectionConfig{Type: "mysql", Host: "target", Database: "dst"},
|
|
}
|
|
plan := SchemaMigrationPlan{
|
|
SourceQueryTable: "src.users",
|
|
TargetQueryTable: "dst.users",
|
|
TargetSchema: "dst",
|
|
TargetTable: "users",
|
|
TargetTableExists: true,
|
|
}
|
|
sourceCols := []connection.ColumnDefinition{{Name: "id", Type: "bigint", Key: "PRI"}}
|
|
|
|
handled, inserted, err := engine.tryApplyDirectImportInPages(
|
|
config, &SyncResult{}, 0, 1, "users", source, target, plan,
|
|
sourceCols, nil, TableOptions{Insert: true}, "mysql", "mysql", "users",
|
|
)
|
|
if !handled {
|
|
t.Fatal("direct import should handle the request")
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), "metadata unavailable") {
|
|
t.Fatalf("expected target column metadata error, got %v", err)
|
|
}
|
|
if inserted != 0 {
|
|
t.Fatalf("expected no inserted rows, got %d", inserted)
|
|
}
|
|
if target.clearedTarget() {
|
|
t.Fatalf("target table was cleared before column validation: %v", target.execs)
|
|
}
|
|
}
|
|
|
|
func TestDirectImportFullOverwriteStopsBeforeClearingWhenAutoAddColumnFails(t *testing.T) {
|
|
engine := &SyncEngine{}
|
|
source := &fakeMigrationDB{}
|
|
target := &directImportIntegrityTargetDB{execErr: errors.New("add column rejected")}
|
|
config := SyncConfig{
|
|
JobID: "direct-import-auto-add",
|
|
Mode: "full_overwrite",
|
|
AutoAddColumns: true,
|
|
SourceConfig: connection.ConnectionConfig{Type: "mysql", Host: "source", Database: "src"},
|
|
TargetConfig: connection.ConnectionConfig{Type: "mysql", Host: "target", Database: "dst"},
|
|
}
|
|
plan := SchemaMigrationPlan{
|
|
SourceQueryTable: "src.users",
|
|
TargetQueryTable: "dst.users",
|
|
TargetSchema: "dst",
|
|
TargetTable: "users",
|
|
TargetTableExists: true,
|
|
}
|
|
sourceCols := []connection.ColumnDefinition{
|
|
{Name: "id", Type: "bigint", Key: "PRI"},
|
|
{Name: "name", Type: "varchar(128)"},
|
|
}
|
|
targetCols := []connection.ColumnDefinition{{Name: "id", Type: "bigint", Key: "PRI"}}
|
|
|
|
handled, inserted, err := engine.tryApplyDirectImportInPages(
|
|
config, &SyncResult{}, 0, 1, "users", source, target, plan,
|
|
sourceCols, targetCols, TableOptions{Insert: true}, "mysql", "mysql", "users",
|
|
)
|
|
if !handled {
|
|
t.Fatal("direct import should handle the request")
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), "add column rejected") {
|
|
t.Fatalf("expected auto-add column error, got %v", err)
|
|
}
|
|
if inserted != 0 {
|
|
t.Fatalf("expected no inserted rows, got %d", inserted)
|
|
}
|
|
if len(target.appliedChanges.Inserts) != 0 {
|
|
t.Fatalf("rows were applied after auto-add failure: %+v", target.appliedChanges.Inserts)
|
|
}
|
|
if target.clearedTarget() {
|
|
t.Fatalf("target table was cleared after auto-add failure: %v", target.execs)
|
|
}
|
|
}
|