Files
mesh-catalog/modules/mesh-build-go/buildstep/seatgen/states_test.go
T

116 lines
4.0 KiB
Go

package seatgen
import (
"os"
"os/exec"
"path/filepath"
re "regexp"
"strings"
"testing"
)
// TestAStateHasItsNamesKeyFormsAndStrictValues is ADR 0634 §6's generator check for `states`: a seat's states give a
// constant per local name, per key form a builder, a parser and a strict reader of the value, and the fixtures of
// `state:` decode into those types. The generated seat package is written out and run with `go vet` and `go test`, so
// what the generator writes is compiled, not only read.
func TestAStateHasItsNamesKeyFormsAndStrictValues(t *testing.T) {
m, err := Load("testdata/contracts", "example.org/contracts/go", "@example/contracts")
if err != nil {
t.Fatal(err)
}
files, err := Generate(m)
if err != nil {
t.Fatal(err)
}
src := string(files["go/seats/aregistry/contract.go"])
for _, want := range []string{
"func AsksByAskerIDKey(asker, id string) string", "func ParseAsksByAskerIDKey(key string) (string, string, bool)",
"func DecodeAsksByAskerIDValue(raw []byte)", "type AsksByAskerIDValue struct",
"func AsksByAskerIDDoneKey(", "func AsksByDefaultsKey() string",
"func LocalNameBySeenIDOfAskKey(idOfAsk string) string",
} {
if !strings.Contains(src, want) {
t.Errorf("the seat's package lacks %q:\n%s", want, src)
}
}
for _, c := range []string{`StateAsks\s+= "asks"`, `StateLocalName\s+= "local-name"`} {
if !re.MustCompile(c).MatchString(src) {
t.Errorf("the seat's package lacks a constant %s", c)
}
}
if strings.Contains(src, "Record") {
t.Errorf("the seat's package still names a record:\n%s", src)
}
ts := string(files["ts/seats/aregistry/src/index.ts"])
for _, want := range []string{"export const states = {", "export function asksByAskerIDKey(asker: string, id: string): string",
"export function parseAsksByAskerIDKey(key: string)", "export interface AsksByAskerIDValue"} {
if !strings.Contains(ts, want) {
t.Errorf("the seat's npm package lacks %q:\n%s", want, ts)
}
}
dir := t.TempDir()
for p, b := range files {
if !strings.HasPrefix(p, "go/seats/aregistry/") {
continue
}
dst := filepath.Join(dir, strings.TrimPrefix(p, "go/seats/aregistry/"))
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(dst, b, 0o644); err != nil {
t.Fatal(err)
}
}
extra := `package aregistry
import "testing"
func TestKeysAndValues(t *testing.T) {
if k := AsksByAskerIDKey("a", "7"); k != "a.7" {
t.Fatal(k)
}
if a, id, ok := ParseAsksByAskerIDKey("a.7"); !ok || a != "a" || id != "7" {
t.Fatal(a, id, ok)
}
for _, bad := range []string{"", "a", ".7", "a.", "a.7.done"} {
if bad == "a.7.done" {
continue // the id takes the rest: another form is told apart by its own parser
}
if _, _, ok := ParseAsksByAskerIDKey(bad); ok {
t.Errorf("%q parsed", bad)
}
}
if a, id, ok := ParseAsksByAskerIDDoneKey("a.7.done"); !ok || a != "a" || id != "7" {
t.Fatal(a, id, ok)
}
if _, _, ok := ParseAsksByAskerIDDoneKey("a.7"); ok {
t.Error("a key without its literal parsed")
}
if !ParseAsksByDefaultsKey("defaults") || ParseAsksByDefaultsKey("defaultsx") {
t.Error("a literal key")
}
if _, err := DecodeAsksByAskerIDValue([]byte(` + "`" + `{"state":"open","extra":1}` + "`" + `)); err == nil {
t.Error("a field the type does not know was taken")
}
if _, err := DecodeAsksByAskerIDValue([]byte(` + "`" + `{"state":"open"} {}` + "`" + `)); err == nil {
t.Error("text after the value was taken")
}
if v, err := DecodeAsksByAskerIDValue([]byte(` + "`" + `{"state":"open","since":3}` + "`" + `)); err != nil || v.State != "open" {
t.Error(v, err)
}
}
`
if err := os.WriteFile(filepath.Join(dir, "extra_test.go"), []byte(extra), 0o644); err != nil {
t.Fatal(err)
}
for _, args := range [][]string{{"vet", "./..."}, {"test", "./..."}} {
cmd := exec.Command("go", args...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GOWORK=off", "GOFLAGS=-mod=mod")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("go %s on the generated package: %v\n%s", strings.Join(args, " "), err, out)
}
}
}