First commit, done on M6 from the PLAN.MD
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"countrygame/web"
|
||||
)
|
||||
|
||||
var (
|
||||
indIDs = []string{"order", "stability", "civilRights", "opinion", "economy", "hardPower", "softPower", "unrest", "armyLoyalty"}
|
||||
varIDs = []string{"growth", "unemployment", "inflation"}
|
||||
taxIDs = []string{"income", "corporate", "vat"}
|
||||
devKeys = []string{"growth", "unemployment", "inflation", "debt", "deficit", "taxPressure", "taxGrowthDrag", "crackdown"}
|
||||
)
|
||||
|
||||
type obj = map[string]any
|
||||
|
||||
func load(t *testing.T, path string) obj {
|
||||
t.Helper()
|
||||
raw, err := web.Files.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", path, err)
|
||||
}
|
||||
var out obj
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
t.Fatalf("parse %s: %v", path, err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func keys(m obj) []string {
|
||||
out := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
out = append(out, k)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func set(list []string) map[string]bool {
|
||||
m := make(map[string]bool, len(list))
|
||||
for _, s := range list {
|
||||
m[s] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func sameKeys(t *testing.T, what string, got obj, want []string) {
|
||||
t.Helper()
|
||||
w := append([]string(nil), want...)
|
||||
sort.Strings(w)
|
||||
g := keys(got)
|
||||
if len(g) != len(w) {
|
||||
t.Errorf("%s: keys %v, want %v", what, g, w)
|
||||
return
|
||||
}
|
||||
for i := range g {
|
||||
if g[i] != w[i] {
|
||||
t.Errorf("%s: keys %v, want %v", what, g, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkWeights verifies every key of def[field] is in the allowed set.
|
||||
func checkWeights(t *testing.T, who string, def obj, field string, allowed map[string]bool) {
|
||||
t.Helper()
|
||||
m, ok := def[field].(obj)
|
||||
if !ok {
|
||||
return // optional section
|
||||
}
|
||||
for k := range m {
|
||||
if !allowed[k] {
|
||||
t.Errorf("%s.%s references unknown %q", who, field, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBalanceAndScenario(t *testing.T) {
|
||||
bal := load(t, "data/balance.json")
|
||||
scn := load(t, "data/scenarios/default.json")
|
||||
|
||||
sectors := scn["sectors"].(obj)
|
||||
sectorSet := make(map[string]bool)
|
||||
for id := range sectors {
|
||||
sectorSet[id] = true
|
||||
}
|
||||
|
||||
// Scenario consistency.
|
||||
sameKeys(t, "scenario.indicators", scn["indicators"].(obj), indIDs)
|
||||
sameKeys(t, "scenario.economy.taxes", scn["economy"].(obj)["taxes"].(obj), taxIDs)
|
||||
limit := bal["limits"].(obj)["allocMaxRatio"].(float64)
|
||||
for id, raw := range sectors {
|
||||
s := raw.(obj)
|
||||
need, alloc := s["need"].(float64), s["alloc"].(float64)
|
||||
if need <= 0 || alloc < 0 || alloc > need*limit {
|
||||
t.Errorf("scenario sector %s: need=%v alloc=%v out of range", id, need, alloc)
|
||||
}
|
||||
}
|
||||
for id, v := range scn["indicators"].(obj) {
|
||||
if f := v.(float64); f < 0 || f > 100 {
|
||||
t.Errorf("scenario indicator %s=%v outside 0..100", id, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Balance coverage.
|
||||
sameKeys(t, "balance.sectorTau", bal["sectorTau"].(obj), keys(sectors))
|
||||
sameKeys(t, "balance.indicators", bal["indicators"].(obj), indIDs)
|
||||
sameKeys(t, "balance.vars", bal["vars"].(obj), varIDs)
|
||||
sameKeys(t, "balance.tax", bal["tax"].(obj), taxIDs)
|
||||
for id, tau := range bal["sectorTau"].(obj) {
|
||||
if tau.(float64) <= 0 {
|
||||
t.Errorf("sectorTau.%s must be positive", id)
|
||||
}
|
||||
}
|
||||
|
||||
// Every weight points at something that exists.
|
||||
dev, ind := set(devKeys), set(indIDs)
|
||||
groups := []obj{bal["indicators"].(obj), bal["vars"].(obj)}
|
||||
for _, g := range groups {
|
||||
for id, raw := range g {
|
||||
def := raw.(obj)
|
||||
if def["tau"].(float64) <= 0 {
|
||||
t.Errorf("%s.tau must be positive", id)
|
||||
}
|
||||
checkWeights(t, id, def, "sectors", sectorSet)
|
||||
checkWeights(t, id, def, "alloc", sectorSet)
|
||||
checkWeights(t, id, def, "econ", dev)
|
||||
checkWeights(t, id, def, "ind", ind)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user