Answer architecture round 1: rows.Options checked whole in internal/rows, Owner and ProveStepUp, neutral pin docs, one walk position, tests of internal/rows and Weighted, and a true README list of Fake's failures

This commit is contained in:
2026-10-05 21:48:33 +02:00
committed by Lillem4n
parent 707cd6b203
commit 29a5314f60
11 changed files with 279 additions and 126 deletions
+130
View File
@@ -0,0 +1,130 @@
package rows
import (
"strings"
"testing"
"github.com/larvit/fejkdata/internal/drawstate"
)
func parse(t *testing.T, segment, data string, o Options) *Table[string] {
t.Helper()
tb, err := Parse(segment, segment, "g."+segment, segment+".tsv", data, o)
if err != nil {
t.Fatalf("Parse(%s): %v", segment, err)
}
return tb
}
// family is a country table with two cities linked to it, each with a name only unique inside its country.
func family(t *testing.T) (country, city *Table[string]) {
t.Helper()
country = parse(t, "country", "code\tname\nSE\tSweden\nNO\tNorway\n", Options{Key: "code"})
city = parse(t, "city", "country\tname\nSE\tBorg\nNO\tBorg\nSE\tLund\n", Options{Name: "name", Parent: "country"})
if err := city.Link(country, func(string) *Table[string] { return nil }); err != nil {
t.Fatalf("Link: %v", err)
}
return country, city
}
func TestParseRefuses(t *testing.T) {
for _, c := range []struct {
name, data string
o Options
want string
}{
{"no header", "", Options{}, "no header line"},
{"one row", "a\n1\n", Options{}, "has one row"},
{"a short row", "a\tb\n1\t2\n3\n", Options{}, "line 3 has fewer cells"},
{"a repeated key", "k\nx\nx\n", Options{Key: "k"}, `key "x" repeats line 2`},
{"the key named as name", "k\nx\ny\n", Options{Key: "k", Name: "k"}, "name names the key column"},
{"a weight of zero", "k\tw\nx\t1\ny\t0\n", Options{Weight: "w"}, `weight "0" is not a positive number`},
{"a key a selector cannot spell", "k\nx\ny|z\n", Options{Key: "k"}, `contains "|"`},
{"an option naming no column", "k\nx\ny\n", Options{Key: "nope"}, `key names no column "nope"`},
} {
if _, err := Parse("o", "t", "g.t", "t.tsv", c.data, c.o); err == nil || !strings.Contains(err.Error(), c.want) {
t.Errorf("%s: Parse = %v, want %q", c.name, err, c.want)
}
}
}
func TestLinkRefusesAParentRowNoChildLinksTo(t *testing.T) {
country := parse(t, "country", "code\nSE\nNO\n", Options{Key: "code"})
city := parse(t, "city", "country\tname\nSE\tLund\nSE\tBorg\n", Options{Parent: "country"})
if err := city.Link(country, func(string) *Table[string] { return nil }); err == nil || !strings.Contains(err.Error(), `country "NO"`) {
t.Errorf("Link = %v, want the unlinked row NO named", err)
}
if city.Parent() != nil {
t.Error("a refused link set the parent")
}
}
func TestOwnerIsWhatParseWasGiven(t *testing.T) {
if _, city := family(t); city.Parent().Owner() != "country" {
t.Errorf("Owner = %q, want country", city.Parent().Owner())
}
}
func TestSelectResolvesANameInsideThePinnedParent(t *testing.T) {
country, city := family(t)
var p Pins[string]
if _, err := p.Select(city, "Borg"); err == nil || !strings.Contains(err.Error(), "names 2 rows") {
t.Fatalf("Select(Borg) with no country pinned = %v, want it ambiguous", err)
}
if _, err := p.Select(country, "NO"); err != nil {
t.Fatal(err)
}
r, err := p.Select(city, "Borg")
if err != nil || r != 1 {
t.Fatalf("Select(Borg) inside NO = %d, %v, want row 1", r, err)
}
if err := p.PinRow(city, 2); err == nil || !strings.Contains(err.Error(), "two rows of g.city") {
t.Errorf("PinRow(Lund) over Borg = %v, want a clash", err)
}
}
func TestPinRowRefusesARowOutsideThePinnedParent(t *testing.T) {
country, city := family(t)
var p Pins[string]
if err := p.PinRow(country, 1); err != nil {
t.Fatal(err)
}
if err := p.PinRow(city, 2); err == nil || !strings.Contains(err.Error(), "is not inside g.country[NO]") {
t.Errorf("PinRow(Lund) inside NO = %v, want it refused", err)
}
}
func TestDrawInDrawsInsideThePinnedParentAndPinsItsAncestors(t *testing.T) {
country, city := family(t)
s := drawstate.New(1)
for i := 0; i < 50; i++ {
var p Pins[string]
if err := p.PinRow(country, 0); err != nil {
t.Fatal(err)
}
if r := city.DrawIn(s, &p); city.Cell(r, 0) != "SE" {
t.Fatalf("DrawIn inside SE drew a city of %s", city.Cell(r, 0))
}
var q Pins[string]
r := city.DrawIn(s, &q)
if pr := q.MustRow(country); country.Cell(pr, 0) != city.Cell(r, 0) {
t.Fatalf("DrawIn pinned country %s above a city of %s", country.Cell(pr, 0), city.Cell(r, 0))
}
if above := q.Above(country); func() bool { _, ok := above.Pinned(city); return ok }() {
t.Fatal("Above(country) kept the city's pin")
}
}
}
func TestProveStepUpNamesTheParent(t *testing.T) {
country, city := family(t)
if err := city.ProveStepUp([]string{"country", "name"}); err != nil {
t.Errorf("ProveStepUp(country.name) = %v", err)
}
if err := city.ProveStepUp([]string{"name"}); err == nil || !strings.Contains(err.Error(), "..country") {
t.Errorf("ProveStepUp(name) = %v, want ..country named", err)
}
if err := country.ProveStepUp([]string{"x"}); err == nil {
t.Error("ProveStepUp from a table with no parent passed")
}
}