102 lines
2.6 KiB
Go
102 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"agent-system-backend/internal/store"
|
|
)
|
|
|
|
// 新库 schema 预置的单行表:必须在 copyOrder 内、确由迁移预置、不计入
|
|
// businessRows,且复制时由源库行替换(而非 ON CONFLICT 保留目标预置行)。
|
|
|
|
func openFreshStore(t *testing.T, name string) *store.Store {
|
|
t.Helper()
|
|
st, err := store.OpenSQLite(filepath.Join(t.TempDir(), name))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { st.Close() })
|
|
return st
|
|
}
|
|
|
|
func tableColumns(t *testing.T, db *sql.DB, table string) []string {
|
|
t.Helper()
|
|
rows, err := db.Query(`SELECT name FROM pragma_table_info(?) ORDER BY cid`, table)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer rows.Close()
|
|
var cols []string
|
|
for rows.Next() {
|
|
var c string
|
|
if err := rows.Scan(&c); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cols = append(cols, c)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(cols) == 0 {
|
|
t.Fatalf("%s 无列", table)
|
|
}
|
|
return cols
|
|
}
|
|
|
|
func TestSchemaSeededSingletonsAreSeededAndCopied(t *testing.T) {
|
|
inOrder := map[string]bool{}
|
|
for _, tbl := range copyOrder {
|
|
inOrder[tbl] = true
|
|
}
|
|
fresh := openFreshStore(t, "fresh.db").DB()
|
|
for tbl := range schemaSeededSingletons {
|
|
if !inOrder[tbl] {
|
|
t.Errorf("%s 不在 copyOrder 内", tbl)
|
|
}
|
|
var n int
|
|
if err := fresh.QueryRow(`SELECT COUNT(*) FROM "` + tbl + `"`).Scan(&n); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 1 {
|
|
t.Errorf("%s 新库预置行数=%d,期望 1", tbl, n)
|
|
}
|
|
}
|
|
n, err := businessRows(fresh)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 0 {
|
|
t.Fatalf("新库 businessRows=%d,预置单行表不得计为业务数据", n)
|
|
}
|
|
}
|
|
|
|
func TestCopyReplacesSeededAuthorityState(t *testing.T) {
|
|
src := openFreshStore(t, "src.db").DB()
|
|
if _, err := src.Exec(`UPDATE authority_state SET authority_epoch=7, catalog_version=9 WHERE id=1`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dst := openFreshStore(t, "dst.db").DB()
|
|
cols := tableColumns(t, src, "authority_state")
|
|
tx, err := dst.Begin()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer tx.Rollback()
|
|
if err := copyRowsInTx(tx, src, "authority_state", cols, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var count int
|
|
var epoch, catalog int64
|
|
if err := dst.QueryRow(`SELECT COUNT(*), MAX(authority_epoch), MAX(catalog_version) FROM authority_state`).Scan(&count, &epoch, &catalog); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 1 || epoch != 7 || catalog != 9 {
|
|
t.Fatalf("目标 authority_state 未被源行替换:count=%d epoch=%d catalog=%d", count, epoch, catalog)
|
|
}
|
|
}
|