feat(db): 新增 0002_memory 迁移;迁移测试改用嵌入式迁移集合(数量无关,避免卡死后续迁移)

This commit is contained in:
Aska committed 2026-09-06 05:12:42 +08:00
1 parent 0a936a3202
commit 285e1e9911
3 files changed
+52 -12

No files matched your search

+30 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"errors"
"slices"
"testing"
)
@@ -41,6 +42,20 @@ func registeredMigrations(t *testing.T, ctx context.Context, db *sql.DB) []strin
return ids
}
// upMigrationIDs returns the ids of the embedded up-migrations in apply order.
func upMigrationIDs(t *testing.T) []string {
t.Helper()
migs, err := loadMigrations()
if err != nil {
t.Fatalf("loadMigrations: %v", err)
}
ids := make([]string, len(migs))
for i, m := range migs {
ids[i] = m.id
}
return ids
}
// appMetaExists reports whether the app_meta table is present.
func appMetaExists(t *testing.T, ctx context.Context, db *sql.DB) bool {
t.Helper()
@@ -60,15 +75,16 @@ func TestMigrateUp_FirstApply_正常(t *testing.T) {
t.Fatalf("MigrateUp first call: %v", err)
}
// Applies every embedded up-migration, in order.
ids := registeredMigrations(t, ctx, db)
if len(ids) != 1 || ids[0] != "0001_init" {
t.Fatalf("schema_migrations = %v, want exactly [0001_init]", ids)
if want := upMigrationIDs(t); !slices.Equal(ids, want) {
t.Fatalf("schema_migrations = %v, want %v", ids, want)
}
if !appMetaExists(t, ctx, db) {
t.Fatal("app_meta table should exist after MigrateUp")
}
// applied_at must be recorded on the single row.
// applied_at must be recorded on the first migration row.
var appliedAt string
if err := db.QueryRowContext(ctx, "SELECT applied_at FROM schema_migrations WHERE id = ?", "0001_init").Scan(&appliedAt); err != nil {
t.Fatalf("read applied_at: %v", err)
@@ -87,13 +103,14 @@ func TestMigrateUp_Idempotent_正常(t *testing.T) {
t.Fatalf("MigrateUp first call: %v", err)
}
// Second call must not re-execute or duplicate the record.
// Second call must not re-execute or duplicate the records.
if err := MigrateUp(ctx, db, testLogger()); err != nil {
t.Fatalf("MigrateUp second call: %v", err)
}
want := upMigrationIDs(t)
ids := registeredMigrations(t, ctx, db)
if len(ids) != 1 {
t.Fatalf("schema_migrations rows = %d, want 1 (idempotent), ids=%v", len(ids), ids)
if !slices.Equal(ids, want) {
t.Fatalf("idempotent: schema_migrations = %v, want %v", ids, want)
}
if !appMetaExists(t, ctx, db) {
t.Fatal("app_meta should still exist after idempotent re-run")
@@ -105,16 +122,17 @@ func TestMigrateDown_ThenReUp_正常(t *testing.T) {
ctx := context.Background()
db := openMemory(t)
want := upMigrationIDs(t)
if err := MigrateUp(ctx, db, testLogger()); err != nil {
t.Fatalf("MigrateUp: %v", err)
}
// Roll back the single applied migration.
if err := MigrateDown(ctx, db, 1, testLogger()); err != nil {
t.Fatalf("MigrateDown: %v", err)
// Roll back every applied migration.
if err := MigrateDown(ctx, db, len(want), testLogger()); err != nil {
t.Fatalf("MigrateDown(all): %v", err)
}
if appMetaExists(t, ctx, db) {
t.Fatal("app_meta should be dropped after MigrateDown")
t.Fatal("app_meta should be dropped after MigrateDown(all)")
}
if ids := registeredMigrations(t, ctx, db); len(ids) != 0 {
t.Fatalf("schema_migrations should be empty after rollback, got %v", ids)
@@ -127,8 +145,8 @@ func TestMigrateDown_ThenReUp_正常(t *testing.T) {
if !appMetaExists(t, ctx, db) {
t.Fatal("app_meta should exist again after re-MigrateUp")
}
if ids := registeredMigrations(t, ctx, db); len(ids) != 1 || ids[0] != "0001_init" {
t.Fatalf("schema_migrations = %v, want exactly [0001_init]", ids)
if ids := registeredMigrations(t, ctx, db); !slices.Equal(ids, want) {
t.Fatalf("schema_migrations = %v, want %v", ids, want)
}
}
@@ -0,0 +1 @@
DROP TABLE IF EXISTS memory_items;
+21
View File
@@ -0,0 +1,21 @@
-- M2: long-term memory items (DevDocs/design/03 §0/§3).
-- Canonical schema reference; internal/runtime/memory reuses this table.
CREATE TABLE IF NOT EXISTS memory_items (
id TEXT PRIMARY KEY,
persona_id TEXT NOT NULL,
role_id TEXT NOT NULL DEFAULT '',
scope TEXT NOT NULL,
kind TEXT NOT NULL,
content TEXT NOT NULL,
tags TEXT NOT NULL DEFAULT '[]',
confidence REAL NOT NULL DEFAULT 1.0,
source_conversation TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
decay_weight REAL NOT NULL DEFAULT 1.0
);
CREATE INDEX IF NOT EXISTS idx_memory_items_persona_id
ON memory_items (persona_id);
CREATE INDEX IF NOT EXISTS idx_memory_items_persona_id_created_at
ON memory_items (persona_id, created_at);