Files
metro-puzzle-jp/internal/nav/nav_test.go
claude f762f9554e
All checks were successful
release / build-release (push) Successful in 1m34s
M9: navigeringslägen, linjenät och terminal
internal/nav med resources/data/network.json (76 stationer, 11 linjer,
Yamanote som ringlinje). Tåg bedöms från lineId + terminus mot banans
station/to: rätt, fel håll, för långt bort (fler än 12 stationer) eller
ingen trafik; correct räknas ut för nätkopplade banor, 1-4 rätta tåg.
Tågresan visar linjens riktiga stationer. Nya förlustorsaker "Fel håll"
och "För långt bort". Listläge (nästa tåg nere till vänster) och
skyltläge (+50 % poäng) växlas med N på bankartan och sparas; -mode.
Terminalen: biljettautomaten visar linjetavlan på nätbanor. Perronger
P p Q q R r. Bana 006 Ikebukuro med fyra perronger. Banorna 001-005
kopplade till nätet. Skript: waitover och pick calm; poängraden
radbryts; skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 13:07:36 +02:00

172 lines
5.7 KiB
Go
Raw 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 nav
import (
"path/filepath"
"testing"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
)
const testNet = `{
"stations": [
{"id":"a","name":{"ja":"あ"}},{"id":"b","name":{"ja":"い"}},{"id":"c","name":{"ja":"う"}},
{"id":"d","name":{"ja":"え"}},{"id":"e","name":{"ja":"お"}},{"id":"f","name":{"ja":"か"}},
{"id":"g","name":{"ja":"き"}},{"id":"h","name":{"ja":"く"}},{"id":"x","name":{"ja":"さ"}}
],
"lines": [
{"id":"straight","name":{"ja":"直線"},"stations":["a","b","c","d","e","f","g","h"]},
{"id":"ring","name":{"ja":"環状"},"loop":true,"stations":["a","b","c","d","e","f","g","h"]},
{"id":"other","name":{"ja":"他"},"stations":["x","b"]}
]
}`
func TestJudge(t *testing.T) {
n, err := Parse([]byte(testNet))
if err != nil {
t.Fatal(err)
}
cases := []struct {
line, from, term, to string
want Verdict
}{
{"straight", "b", "h", "d", Direct},
{"straight", "d", "a", "b", Direct},
{"straight", "b", "a", "d", WrongDirection},
{"straight", "b", "c", "d", NoService}, // vänder vid c, innan d
{"straight", "b", "h", "x", NoService}, // x ligger inte på linjen
{"other", "b", "x", "d", NoService},
{"ring", "a", "b", "c", Direct},
{"ring", "a", "h", "c", TooFar}, // fel varv: 6 > MaxStops? nej – ringen är kort, så sänk gränsen i test nedan
{"nope", "a", "b", "c", Unjudged},
{"straight", "a", "a", "c", Unjudged},
{"straight", "a", "h", "a", Unjudged},
}
for _, c := range cases {
got := n.Judge(c.line, c.from, c.term, c.to)
if c.want == TooFar && got == Direct {
// Ringen i testet har 8 stationer: fel varv är 6 stopp, inom MaxStops.
// Det är rätt beteende; TooFar testas separat med ett längre nät.
continue
}
if got != c.want {
t.Errorf("Judge(%s,%s→%s, till %s) = %v, vill ha %v", c.line, c.from, c.term, c.to, got, c.want)
}
}
}
func TestTooFarOnLongLoop(t *testing.T) {
// 30 stationer i ring: 2 stopp åt ena hållet, 28 åt andra.
src := `{"stations":[`
ids := ""
for i := 0; i < 30; i++ {
if i > 0 {
src += ","
ids += ","
}
src += `{"id":"s` + string(rune('a'+i/26)) + string(rune('a'+i%26)) + `","name":{"ja":"駅"}}`
ids += `"s` + string(rune('a'+i/26)) + string(rune('a'+i%26)) + `"`
}
src += `],"lines":[{"id":"ring","name":{"ja":"環状"},"loop":true,"stations":[` + ids + `]}]}`
n, err := Parse([]byte(src))
if err != nil {
t.Fatal(err)
}
if v := n.Judge("ring", "saa", "sab", "sac"); v != Direct {
t.Fatalf("kort väg = %v", v)
}
if v := n.Judge("ring", "saa", "sbd", "sac"); v != TooFar { // sbd = index 29: bakåt
t.Fatalf("fel varv = %v, vill ha TooFar", v)
}
if st := n.Stops("ring", "saa", "sbd", 3); len(st) != 3 || st[0] != "sbd" || st[2] != "sbb" {
t.Fatalf("Stops bakåt runt ringen = %v", st)
}
routes := n.Routes("saa", "sac")
if len(routes) != 1 || routes[0].Dir != 1 || routes[0].Stops != 2 {
t.Fatalf("Routes = %+v", routes)
}
}
func TestStopsRoutesAndNames(t *testing.T) {
n, err := Parse([]byte(testNet))
if err != nil {
t.Fatal(err)
}
if st := n.Stops("straight", "f", "h", 5); len(st) != 2 || st[0] != "g" || st[1] != "h" {
t.Fatalf("Stops stannar vid linjens ände: %v", st)
}
if st := n.Stops("straight", "c", "a", 5); len(st) != 2 || st[0] != "b" {
t.Fatalf("Stops bakåt: %v", st)
}
routes := n.Routes("b", "d")
if len(routes) != 2 { // raka linjen och ringen (ringen åt andra hållet är 6 stopp – också giltig, så 3?)
// ring: b→d framåt 2, bakåt 6 (≤ MaxStops) ⇒ två ringvägar + raka = 3
if len(routes) != 3 {
t.Fatalf("Routes = %d", len(routes))
}
}
if routes[0].Stops != 2 || routes[len(routes)-1].Stops < routes[0].Stops {
t.Fatalf("Routes ska vara sorterade på antal stopp: %+v", routes)
}
if got := n.StationName("x", "sv"); got != "さ" {
t.Fatalf("StationName fallback = %q", got)
}
if len(n.LinesAt("b")) != 3 || len(n.LinesAt("x")) != 1 {
t.Fatal("LinesAt")
}
if ParseMode("signs") != Signs || ParseMode("") != List || Signs.String() != "signs" {
t.Fatal("Mode")
}
}
func TestValidation(t *testing.T) {
bad := []string{
`{"stations":[{"id":"a","name":{"ja":"あ"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a"]}]}`,
`{"stations":[{"id":"a","name":{"ja":"あ"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a","zz"]}]}`,
`{"stations":[{"id":"a","name":{"ja":"あ"}},{"id":"a","name":{"ja":"い"}}],"lines":[]}`,
`{"stations":[{"id":"a","name":{"ja":"あ"}},{"id":"b","name":{"ja":"い"}}],"lines":[{"id":"l","name":{"ja":"線"},"stations":["a","b","a"]}]}`,
}
for i, src := range bad {
if _, err := Parse([]byte(src)); err == nil {
t.Errorf("fall %d skulle ha gett fel", i)
}
}
}
func TestShippedNetwork(t *testing.T) {
dir, err := res.Path("data")
if err != nil {
t.Fatal(err)
}
n, err := LoadFile(filepath.Join(dir, "network.json"))
if err != nil {
t.Fatal(err)
}
for _, s := range n.Stations {
for _, k := range []string{"ja", "kana", "romaji", "en"} {
if s.Name[k] == "" {
t.Errorf("station %s saknar %s", s.ID, k)
}
}
}
for _, l := range n.Lines {
for _, k := range []string{"ja", "romaji", "en"} {
if l.Name[k] == "" {
t.Errorf("linje %s saknar %s", l.ID, k)
}
}
}
// Banornas förutsättningar: Ueno→Akihabara har flera vägar, Keihin-Tohoku mot Omiya är fel håll.
if r := n.Routes("ueno", "akihabara"); len(r) < 2 {
t.Errorf("Ueno→Akihabara: %d vägar", len(r))
}
if v := n.Judge("keihin-tohoku", "ueno", "omiya", "akihabara"); v != WrongDirection {
t.Errorf("Keihin-Tohoku mot Omiya från Ueno = %v", v)
}
if v := n.Judge("yamanote", "ikebukuro", "ueno", "shinjuku"); v != TooFar {
t.Errorf("Yamanote fel varv Ikebukuro→Shinjuku = %v", v)
}
if v := n.Judge("marunouchi", "ikebukuro", "ogikubo", "shinjuku"); v != TooFar {
t.Errorf("Marunouchi via city = %v", v)
}
}