123 lines
2.7 KiB
Go
123 lines
2.7 KiB
Go
package store
|
|
|
|
import (
|
|
"errors"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// TestMigrationsFromEmpty 空库 → 最新版本;重复打开幂等(§13 迁移组)。
|
|
func TestMigrationsFromEmpty(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "pn.db")
|
|
s1, err := Open(dbPath, false)
|
|
if err != nil {
|
|
t.Fatalf("首次打开失败: %v", err)
|
|
}
|
|
v1, err := s1.SchemaVersion()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v1 != MaxSchemaVersion {
|
|
t.Fatalf("期望版本 %d 实际 %d", MaxSchemaVersion, v1)
|
|
}
|
|
s1.Close()
|
|
|
|
// 重开:幂等,不再执行迁移
|
|
s2, err := Open(dbPath, false)
|
|
if err != nil {
|
|
t.Fatalf("二次打开失败: %v", err)
|
|
}
|
|
defer s2.Close()
|
|
v2, err := s2.SchemaVersion()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v2 != v1 {
|
|
t.Fatalf("迁移不幂等: %d → %d", v1, v2)
|
|
}
|
|
}
|
|
|
|
// TestMigrationNewerRejected user_version 越界拒绝启动;--allow-newer 放行(§10.4)。
|
|
func TestMigrationNewerRejected(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "pn.db")
|
|
s1, err := Open(dbPath, false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s1.DB().Exec("PRAGMA user_version = 99"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s1.Close()
|
|
|
|
_, err = Open(dbPath, false)
|
|
if !errors.Is(err, ErrSchemaNewer) {
|
|
t.Fatalf("期望 ErrSchemaNewer,实际 %v", err)
|
|
}
|
|
|
|
s2, err := Open(dbPath, true) // --allow-newer
|
|
if err != nil {
|
|
t.Fatalf("allow-newer 应放行: %v", err)
|
|
}
|
|
defer s2.Close()
|
|
}
|
|
|
|
// TestForeignKeysCascade 外键级联生效(image_refs 依赖 foreign_keys=1)。
|
|
func TestForeignKeysCascade(t *testing.T) {
|
|
s := openTestStore(t)
|
|
nid, err := s.CreateNote(&Note{Slug: "casc", Title: "级联", Status: "public",
|
|
Tags: []string{}, CreatedAt: 1, UpdatedAt: 1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
imgID, err := s.UpsertImage("aa", "image/png", 1, []byte{1}, 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.RebuildImageRefs(nid, "+")"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.DeleteNoteForever(nid); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// 级联后图片变孤儿
|
|
orphans, err := s.ListOrphanImages()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, o := range orphans {
|
|
if o.ID == imgID {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("物理删除笔记后其引用关系应被级联删除(图片转为孤儿)")
|
|
}
|
|
}
|
|
|
|
func openTestStore(t *testing.T) *Store {
|
|
t.Helper()
|
|
s, err := Open(filepath.Join(t.TempDir(), "test.db"), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { s.Close() })
|
|
return s
|
|
}
|
|
|
|
func itoa64(v int64) string {
|
|
if v == 0 {
|
|
return "0"
|
|
}
|
|
var b [20]byte
|
|
i := len(b)
|
|
for v > 0 {
|
|
i--
|
|
b[i] = byte('0' + v%10)
|
|
v /= 10
|
|
}
|
|
return string(b[i:])
|
|
}
|