196 lines
5.0 KiB
Go
196 lines
5.0 KiB
Go
package state
|
||
|
||
import (
|
||
"os"
|
||
"testing"
|
||
|
||
"github.com/voocel/ainovel-cli/domain"
|
||
)
|
||
|
||
func TestSaveAndLoadRunMeta(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
meta := domain.RunMeta{
|
||
StartedAt: "2026-03-07T10:00:00+08:00",
|
||
Provider: "openrouter",
|
||
Style: "fantasy",
|
||
Model: "gpt-4o",
|
||
}
|
||
if err := store.SaveRunMeta(meta); err != nil {
|
||
t.Fatalf("SaveRunMeta: %v", err)
|
||
}
|
||
|
||
loaded, err := store.LoadRunMeta()
|
||
if err != nil {
|
||
t.Fatalf("LoadRunMeta: %v", err)
|
||
}
|
||
if loaded.Style != "fantasy" {
|
||
t.Errorf("style mismatch: %s", loaded.Style)
|
||
}
|
||
if loaded.Provider != "openrouter" {
|
||
t.Errorf("provider mismatch: %s", loaded.Provider)
|
||
}
|
||
if loaded.Model != "gpt-4o" {
|
||
t.Errorf("model mismatch: %s", loaded.Model)
|
||
}
|
||
}
|
||
|
||
func TestLoadRunMeta_Empty(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
meta, err := store.LoadRunMeta()
|
||
if err != nil {
|
||
t.Fatalf("LoadRunMeta on empty: %v", err)
|
||
}
|
||
if meta != nil {
|
||
t.Fatalf("expected nil, got %+v", meta)
|
||
}
|
||
}
|
||
|
||
func TestAppendSteerEntry(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
// 首次追加(meta/run.json 不存在)
|
||
e1 := domain.SteerEntry{Input: "主角改成女性", Timestamp: "2026-03-07T10:01:00+08:00"}
|
||
if err := store.AppendSteerEntry(e1); err != nil {
|
||
t.Fatalf("AppendSteerEntry 1: %v", err)
|
||
}
|
||
|
||
meta, _ := store.LoadRunMeta()
|
||
if len(meta.SteerHistory) != 1 {
|
||
t.Fatalf("expected 1 entry, got %d", len(meta.SteerHistory))
|
||
}
|
||
if meta.SteerHistory[0].Input != "主角改成女性" {
|
||
t.Errorf("input mismatch: %s", meta.SteerHistory[0].Input)
|
||
}
|
||
|
||
// 追加第二条
|
||
e2 := domain.SteerEntry{Input: "加入反转", Timestamp: "2026-03-07T10:02:00+08:00"}
|
||
_ = store.AppendSteerEntry(e2)
|
||
|
||
meta, _ = store.LoadRunMeta()
|
||
if len(meta.SteerHistory) != 2 {
|
||
t.Fatalf("expected 2 entries, got %d", len(meta.SteerHistory))
|
||
}
|
||
}
|
||
|
||
func TestAppendSteerEntry_PreservesExistingMeta(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
// 先保存 RunMeta
|
||
_ = store.SaveRunMeta(domain.RunMeta{
|
||
StartedAt: "2026-03-07T10:00:00+08:00",
|
||
Provider: "openrouter",
|
||
Style: "suspense",
|
||
Model: "gpt-4o",
|
||
})
|
||
|
||
// 追加 Steer 不应覆盖其他字段
|
||
_ = store.AppendSteerEntry(domain.SteerEntry{Input: "test", Timestamp: "now"})
|
||
|
||
meta, _ := store.LoadRunMeta()
|
||
if meta.Style != "suspense" {
|
||
t.Errorf("style should be preserved, got %s", meta.Style)
|
||
}
|
||
if meta.Provider != "openrouter" {
|
||
t.Errorf("provider should be preserved, got %s", meta.Provider)
|
||
}
|
||
if meta.Model != "gpt-4o" {
|
||
t.Errorf("model should be preserved, got %s", meta.Model)
|
||
}
|
||
if len(meta.SteerHistory) != 1 {
|
||
t.Errorf("expected 1 steer entry, got %d", len(meta.SteerHistory))
|
||
}
|
||
}
|
||
|
||
func TestInitRunMeta_PreservesHistory(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
// 先建立带历史的 RunMeta
|
||
_ = store.SaveRunMeta(domain.RunMeta{
|
||
StartedAt: "old",
|
||
Provider: "openai",
|
||
Style: "fantasy",
|
||
Model: "old-model",
|
||
SteerHistory: []domain.SteerEntry{{Input: "历史干预", Timestamp: "ts"}},
|
||
PendingSteer: "待处理",
|
||
})
|
||
|
||
// InitRunMeta 应保留 SteerHistory 和 PendingSteer
|
||
_ = store.InitRunMeta("suspense", "openrouter", "new-model")
|
||
|
||
meta, _ := store.LoadRunMeta()
|
||
if meta.Style != "suspense" {
|
||
t.Errorf("style should be updated, got %s", meta.Style)
|
||
}
|
||
if meta.Provider != "openrouter" {
|
||
t.Errorf("provider should be updated, got %s", meta.Provider)
|
||
}
|
||
if meta.Model != "new-model" {
|
||
t.Errorf("model should be updated, got %s", meta.Model)
|
||
}
|
||
if len(meta.SteerHistory) != 1 || meta.SteerHistory[0].Input != "历史干预" {
|
||
t.Errorf("steer history should be preserved, got %v", meta.SteerHistory)
|
||
}
|
||
if meta.PendingSteer != "待处理" {
|
||
t.Errorf("pending steer should be preserved, got %s", meta.PendingSteer)
|
||
}
|
||
}
|
||
|
||
func TestSetAndClearPendingSteer(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
// 设置 PendingSteer
|
||
if err := store.SetPendingSteer("主角改成女性"); err != nil {
|
||
t.Fatalf("SetPendingSteer: %v", err)
|
||
}
|
||
meta, _ := store.LoadRunMeta()
|
||
if meta.PendingSteer != "主角改成女性" {
|
||
t.Errorf("expected pending steer, got %s", meta.PendingSteer)
|
||
}
|
||
|
||
// 清除
|
||
if err := store.ClearPendingSteer(); err != nil {
|
||
t.Fatalf("ClearPendingSteer: %v", err)
|
||
}
|
||
meta, _ = store.LoadRunMeta()
|
||
if meta.PendingSteer != "" {
|
||
t.Errorf("expected empty pending steer, got %s", meta.PendingSteer)
|
||
}
|
||
}
|
||
|
||
func TestClearPendingSteer_Noop(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
|
||
// 空 meta 上调用不报错
|
||
if err := store.ClearPendingSteer(); err != nil {
|
||
t.Fatalf("ClearPendingSteer on empty: %v", err)
|
||
}
|
||
}
|
||
|
||
func TestSaveCheckpoint(t *testing.T) {
|
||
dir := t.TempDir()
|
||
store := NewStore(dir)
|
||
_ = store.InitProgress("test", 10)
|
||
|
||
if err := store.SaveCheckpoint("ch01-commit"); err != nil {
|
||
t.Fatalf("SaveCheckpoint: %v", err)
|
||
}
|
||
|
||
// 验证 checkpoint 目录下有文件
|
||
entries, err := os.ReadDir(dir + "/meta/checkpoints")
|
||
if err != nil {
|
||
t.Fatalf("read checkpoints dir: %v", err)
|
||
}
|
||
if len(entries) != 1 {
|
||
t.Fatalf("expected 1 checkpoint, got %d", len(entries))
|
||
}
|
||
}
|