Files
MyGoNavi/internal/sync/direct_import_integrity_test.go
2026-08-08 07:22:42 +08:00

165 lines
5.5 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",
Content: "both",
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)
}
}
func TestDirectImportDataOnlyRejectsAutoAddColumnsBeforeClearing(t *testing.T) {
engine := &SyncEngine{}
source := &fakeMigrationDB{}
target := &directImportIntegrityTargetDB{}
config := SyncConfig{
JobID: "direct-import-data-only",
Content: "data",
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(), "仅同步数据") {
t.Fatalf("expected data-only auto-add rejection, got %v", err)
}
if inserted != 0 || len(target.execs) != 0 || target.clearedTarget() {
t.Fatalf("data-only direct import modified the target: inserted=%d exec=%v", inserted, target.execs)
}
}