Files
ainovel-clients/state/world_rules_test.go
voocel 27bd85ef90 init
2026-03-07 21:25:55 +08:00

141 lines
4.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package state
import (
"os"
"path/filepath"
"testing"
"github.com/voocel/ainovel-cli/domain"
)
func TestSaveAndLoadWorldRules(t *testing.T) {
dir := t.TempDir()
store := NewStore(dir)
rules := []domain.WorldRule{
{Category: "magic", Rule: "法术消耗精神力", Boundary: "精神力耗尽会昏迷"},
{Category: "magic", Rule: "禁咒需要三人合力", Boundary: "单人强行施放会死亡"},
{Category: "society", Rule: "贵族拥有领地裁判权", Boundary: "不得越权审判其他领地居民"},
}
if err := store.SaveWorldRules(rules); err != nil {
t.Fatalf("SaveWorldRules: %v", err)
}
// 验证 JSON 文件存在
if _, err := os.Stat(filepath.Join(dir, "world_rules.json")); err != nil {
t.Fatalf("world_rules.json not created: %v", err)
}
// 验证 Markdown 文件存在
if _, err := os.Stat(filepath.Join(dir, "world_rules.md")); err != nil {
t.Fatalf("world_rules.md not created: %v", err)
}
loaded, err := store.LoadWorldRules()
if err != nil {
t.Fatalf("LoadWorldRules: %v", err)
}
if len(loaded) != 3 {
t.Fatalf("expected 3 rules, got %d", len(loaded))
}
if loaded[0].Category != "magic" || loaded[0].Rule != "法术消耗精神力" {
t.Errorf("first rule mismatch: %+v", loaded[0])
}
if loaded[2].Category != "society" {
t.Errorf("third rule category mismatch: %+v", loaded[2])
}
}
func TestLoadWorldRules_Empty(t *testing.T) {
dir := t.TempDir()
store := NewStore(dir)
rules, err := store.LoadWorldRules()
if err != nil {
t.Fatalf("LoadWorldRules on empty dir: %v", err)
}
if rules != nil {
t.Fatalf("expected nil for missing file, got %v", rules)
}
}
func TestSaveWorldRules_Overwrite(t *testing.T) {
dir := t.TempDir()
store := NewStore(dir)
v1 := []domain.WorldRule{{Category: "old", Rule: "旧规则", Boundary: "旧边界"}}
if err := store.SaveWorldRules(v1); err != nil {
t.Fatalf("SaveWorldRules v1: %v", err)
}
v2 := []domain.WorldRule{
{Category: "new", Rule: "新规则A", Boundary: "新边界A"},
{Category: "new", Rule: "新规则B", Boundary: "新边界B"},
}
if err := store.SaveWorldRules(v2); err != nil {
t.Fatalf("SaveWorldRules v2: %v", err)
}
loaded, err := store.LoadWorldRules()
if err != nil {
t.Fatalf("LoadWorldRules: %v", err)
}
if len(loaded) != 2 {
t.Fatalf("expected 2 rules after overwrite, got %d", len(loaded))
}
if loaded[0].Category != "new" {
t.Errorf("expected new category, got %s", loaded[0].Category)
}
}
func TestRenderWorldRules(t *testing.T) {
rules := []domain.WorldRule{
{Category: "magic", Rule: "法术消耗精神力", Boundary: "精神力耗尽会昏迷"},
{Category: "society", Rule: "贵族有裁判权", Boundary: ""},
{Category: "magic", Rule: "禁咒需三人", Boundary: "单人施放会死"},
}
md := renderWorldRules(rules)
// 验证标题
if got := md[:len("# 世界观规则")]; got != "# 世界观规则" {
t.Errorf("missing title, got: %s", got)
}
// 验证分组magic 应该出现在 society 之前(按输入顺序)
magicPos := indexOf(md, "## magic")
societyPos := indexOf(md, "## society")
if magicPos < 0 || societyPos < 0 {
t.Fatalf("missing category headers in:\n%s", md)
}
if magicPos >= societyPos {
t.Errorf("magic should appear before society")
}
// 验证边界渲染:有 boundary 的条目应包含"边界"字样
if indexOf(md, "边界:精神力耗尽会昏迷") < 0 {
t.Errorf("missing boundary in:\n%s", md)
}
// 验证无 boundary 的条目不输出边界行
if indexOf(md, "边界:\n") >= 0 {
t.Errorf("empty boundary should not be rendered")
}
}
func TestRenderWorldRules_EmptyCategoryFallback(t *testing.T) {
rules := []domain.WorldRule{
{Category: "", Rule: "无分类规则", Boundary: "边界"},
}
md := renderWorldRules(rules)
if indexOf(md, "## other") < 0 {
t.Errorf("empty category should fall back to 'other', got:\n%s", md)
}
}
func indexOf(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}