package learn import ( "reflect" "testing" ) func TestBuildDatasetMapsWinAndLoss(t *testing.T) { rows := []LabeledRow{ {Result: ResultWin, Features: map[string]float64{"a": 1}}, {Result: ResultLoss, Features: map[string]float64{"a": 2}}, } x, y, names := BuildDataset(rows) if !reflect.DeepEqual(names, []string{"a"}) { t.Fatalf("feature names must be [a], got %v", names) } if !reflect.DeepEqual(y, []int{1, 0}) { t.Fatalf("win must map to 1 and loss to 0, got %v", y) } if len(x) != 2 || x[0][0] != 1 || x[1][0] != 2 { t.Fatalf("feature matrix wrong: %v", x) } } func TestBuildDatasetDropsUndecided(t *testing.T) { rows := []LabeledRow{ {Result: ResultWin, Features: map[string]float64{"a": 1}}, {Result: ResultPending, Features: map[string]float64{"a": 9}}, {Result: ResultNoBid, Features: map[string]float64{"a": 9}}, {Result: ResultLoss, Features: map[string]float64{"a": 2}}, {Result: "garbage", Features: map[string]float64{"a": 9}}, } x, y, _ := BuildDataset(rows) if len(x) != 2 || len(y) != 2 { t.Fatalf("only the two decided rows must survive, got %d rows", len(x)) } if y[0] != 1 || y[1] != 0 { t.Fatalf("the surviving rows must be the win then the loss, got %v", y) } } func TestBuildDatasetStableColumnOrdering(t *testing.T) { // feature keys arrive in different orders across rows; the column layout must be the sorted union. rows := []LabeledRow{ {Result: ResultWin, Features: map[string]float64{"zeta": 1, "alpha": 2, "mid": 3}}, {Result: ResultLoss, Features: map[string]float64{"mid": 4, "alpha": 5, "zeta": 6}}, } x, _, names := BuildDataset(rows) if !reflect.DeepEqual(names, []string{"alpha", "mid", "zeta"}) { t.Fatalf("columns must be the sorted union, got %v", names) } // row 0 values must line up with the sorted names: alpha=2, mid=3, zeta=1 if !reflect.DeepEqual(x[0], []float64{2, 3, 1}) { t.Fatalf("row 0 must align to sorted columns, got %v", x[0]) } if !reflect.DeepEqual(x[1], []float64{5, 4, 6}) { t.Fatalf("row 1 must align to sorted columns, got %v", x[1]) } } func TestBuildDatasetMissingFeatureIsZero(t *testing.T) { // row 1 lacks feature b; the union has a and b, so row 1's b column defaults to 0. rows := []LabeledRow{ {Result: ResultWin, Features: map[string]float64{"a": 1, "b": 7}}, {Result: ResultLoss, Features: map[string]float64{"a": 2}}, } x, _, names := BuildDataset(rows) if !reflect.DeepEqual(names, []string{"a", "b"}) { t.Fatalf("columns must be [a b], got %v", names) } if !reflect.DeepEqual(x[1], []float64{2, 0}) { t.Fatalf("a missing feature must default to 0, got %v", x[1]) } } func TestBuildDatasetEmptyInput(t *testing.T) { x, y, names := BuildDataset(nil) if len(x) != 0 || len(y) != 0 || len(names) != 0 { t.Fatalf("an empty input must yield empty outputs, got x=%v y=%v names=%v", x, y, names) } } func TestIsDecided(t *testing.T) { if !IsDecided(ResultWin) || !IsDecided(ResultLoss) { t.Fatal("win and loss must be decided") } if IsDecided(ResultPending) || IsDecided(ResultNoBid) || IsDecided("") { t.Fatal("pending, no_bid, and unknown must not be decided") } }