126 lines
4.9 KiB
Go
126 lines
4.9 KiB
Go
package main
|
||
|
||
import "testing"
|
||
|
||
// TestWayfarerQuestDefsStructuralIntegrity проверяет каждый из
|
||
// реальных (не тестовых) квестов на внутреннюю согласованность:
|
||
// все NextStep указывают на существующие шаги, все CargoIdx либо -1
|
||
// (не используется), либо валидный индекс в WayfarerCommodities, и
|
||
// у каждого квеста есть хотя бы один способ дойти до Terminal-выбора
|
||
// от стартового шага.
|
||
func TestWayfarerQuestDefsStructuralIntegrity(t *testing.T) {
|
||
if len(WayfarerQuestDefs) != 8 {
|
||
t.Fatalf("ожидалось 8 квестов, получено %d", len(WayfarerQuestDefs))
|
||
}
|
||
|
||
seenKeys := map[string]bool{}
|
||
for _, q := range WayfarerQuestDefs {
|
||
if q.Key == "" || q.TitleKey == "" || q.BriefKey == "" {
|
||
t.Errorf("квест с пустыми обязательными ключами: %+v", q)
|
||
}
|
||
if seenKeys[q.Key] {
|
||
t.Errorf("дублирующийся ключ квеста: %s", q.Key)
|
||
}
|
||
seenKeys[q.Key] = true
|
||
|
||
if len(q.Steps) == 0 {
|
||
t.Errorf("квест %s: должен иметь хотя бы один шаг", q.Key)
|
||
}
|
||
|
||
reachesTerminal := false
|
||
for stepIdx, step := range q.Steps {
|
||
if step.TextKey == "" {
|
||
t.Errorf("квест %s, шаг %d: пустой TextKey", q.Key, stepIdx)
|
||
}
|
||
if len(step.Choices) == 0 {
|
||
t.Errorf("квест %s, шаг %d: нет ни одного варианта выбора", q.Key, stepIdx)
|
||
}
|
||
for choiceIdx, c := range step.Choices {
|
||
if c.LabelKey == "" {
|
||
t.Errorf("квест %s, шаг %d, выбор %d: пустой LabelKey", q.Key, stepIdx, choiceIdx)
|
||
}
|
||
if c.CargoIdx != -1 && (c.CargoIdx < 0 || c.CargoIdx >= len(WayfarerCommodities)) {
|
||
t.Errorf("квест %s, шаг %d, выбор %d: недопустимый CargoIdx %d", q.Key, stepIdx, choiceIdx, c.CargoIdx)
|
||
}
|
||
if c.Terminal {
|
||
if c.ResultKey == "" {
|
||
t.Errorf("квест %s, шаг %d, выбор %d: terminal-выбор без ResultKey", q.Key, stepIdx, choiceIdx)
|
||
}
|
||
reachesTerminal = true
|
||
} else {
|
||
if c.NextStep < 0 || c.NextStep >= len(q.Steps) {
|
||
t.Errorf("квест %s, шаг %d, выбор %d: NextStep %d вне диапазона шагов (0..%d)",
|
||
q.Key, stepIdx, choiceIdx, c.NextStep, len(q.Steps)-1)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if !reachesTerminal {
|
||
t.Errorf("квест %s: ни один выбор во всём квесте не завершает его (нет ни одного Terminal)", q.Key)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestWayfarerQuestDefsAllTranslated проверяет, что для каждого
|
||
// ключа перевода, используемого в квестах, есть запись в таблице
|
||
// translations на обоих языках (это ловит опечатки в ключах и
|
||
// забытые переводы).
|
||
func TestWayfarerQuestDefsAllTranslated(t *testing.T) {
|
||
checkKey := func(key string) {
|
||
t.Helper()
|
||
if key == "" {
|
||
return
|
||
}
|
||
entry, ok := translations[key]
|
||
if !ok {
|
||
t.Errorf("отсутствует перевод для ключа %q", key)
|
||
return
|
||
}
|
||
if entry.ru == "" || entry.en == "" {
|
||
t.Errorf("перевод для ключа %q неполный: ru=%q en=%q", key, entry.ru, entry.en)
|
||
}
|
||
}
|
||
|
||
for _, q := range WayfarerQuestDefs {
|
||
checkKey(q.TitleKey)
|
||
checkKey(q.BriefKey)
|
||
for _, step := range q.Steps {
|
||
checkKey(step.TextKey)
|
||
for _, c := range step.Choices {
|
||
checkKey(c.LabelKey)
|
||
checkKey(c.ResultKey)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestWayfarerQuestDefsPlayableToCompletion проверяет, что каждый
|
||
// квест реально проходим от начала до какого-либо Terminal-выбора,
|
||
// просто выбирая первый не-terminal вариант на каждом шаге (а если
|
||
// таких нет — берёт первый попавшийся) — ловит зависания/бесконечные
|
||
// циклы в ручной разметке шагов.
|
||
func TestWayfarerQuestDefsPlayableToCompletion(t *testing.T) {
|
||
for _, q := range WayfarerQuestDefs {
|
||
s := NewWayfarerShip(WayfarerSystemID{})
|
||
if err := s.StartQuest(q.Key); err != nil {
|
||
t.Fatalf("квест %s: не удалось начать: %v", q.Key, err)
|
||
}
|
||
const maxSteps = 20
|
||
for i := 0; i < maxSteps && s.ActiveQuest != nil; i++ {
|
||
step, _ := s.CurrentQuestStep()
|
||
choiceIdx := 0
|
||
for idx, c := range step.Choices {
|
||
if !c.Terminal {
|
||
choiceIdx = idx
|
||
break
|
||
}
|
||
}
|
||
if _, err := s.ChooseQuestOption(choiceIdx); err != nil {
|
||
t.Fatalf("квест %s: неожиданная ошибка выбора на шаге: %v", q.Key, err)
|
||
}
|
||
}
|
||
if s.ActiveQuest != nil {
|
||
t.Errorf("квест %s: не завершился за %d шагов (возможен бесконечный цикл)", q.Key, maxSteps)
|
||
}
|
||
}
|
||
}
|