Skip to content

Commit 44fbbd4

Browse files
authored
test(golang): the generator's kind check and Go-name rule against the eql build
Review of #1095: lowerDeclaration's plaintext-kind check (Age int64 with encrypt_into=TextEq) ran only against the real engine with EQL types, which no test linked; and EQLGoName and eql-codegen's go_name held one rule with nothing comparing them. TestRunAsksTheEmbeddedEngine, the one test that links encrypt/eql, gains the kind-mismatch case ("TextEq seals a string, and int64 is int", nothing written). The Go-name rule is exported as stashgen.EQLGoName and checked against every row of the generated eql.Types table from cmd/stashgen — not from stashgen's own tests, which must not link encrypt/eql or the embedded engine they test changes build. Claude-Session: https://claude.ai/code/session_01V3WFXwax4J3uecpFEJ6yHc
1 parent 1caa5f6 commit 44fbbd4

2 files changed

Lines changed: 54 additions & 8 deletions

File tree

‎languages/golang/cmd/stashgen/main_test.go‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"testing"
1010

1111
"github.com/cipherstash/stack/languages/golang/encrypt"
12+
"github.com/cipherstash/stack/languages/golang/encrypt/eql"
1213
"github.com/cipherstash/stack/languages/golang/stashgen"
1314
"github.com/cipherstash/stack/languages/golang/stashgen/enginetest"
1415
)
@@ -155,6 +156,22 @@ func TestRunAsksTheEmbeddedEngine(t *testing.T) {
155156
if _, err := os.Stat(filepath.Join(dir, "user_stash.go")); !os.IsNotExist(err) {
156157
t.Fatal("a file was written after the engine refused")
157158
}
159+
// A Go type the EQL type does not seal: TextEq takes a string. The
160+
// generator refuses it from the engine's plaintext kind, before the
161+
// engine is asked. Only this test links encrypt/eql, so only it reaches
162+
// the kind check against the real target list.
163+
mismatched := strings.Replace(userSource, "Email string `stash:\"email,encrypt_into=TextEq\"`", "Age int64 `stash:\"age,encrypt_into=TextEq\"`", 1)
164+
if mismatched == userSource {
165+
t.Fatal("the fixture's Email line changed shape")
166+
}
167+
dir = writeModule(t, map[string]string{"model.go": mismatched})
168+
stderr.Reset()
169+
if code := run([]string{"-type", "User"}, dir, &stdout, &stderr, stashgen.GuestEngine); code != 1 {
170+
t.Fatalf("exit %d, want 1\n%s", code, stderr.String())
171+
}
172+
if !strings.Contains(stderr.String(), "User.Age") || !strings.Contains(stderr.String(), "TextEq seals a string, and int64 is int") {
173+
t.Fatalf("stderr = %q", stderr.String())
174+
}
158175
// A context of two segments leaves an EQL field no column: refused by
159176
// name, before the engine is asked, and nothing written.
160177
deep := strings.Replace(userSource, "context=users", "context=app/users", 1)
@@ -178,6 +195,33 @@ func TestRunAsksTheEmbeddedEngine(t *testing.T) {
178195
}
179196
}
180197

198+
// stashgen.EQLGoName and eql-codegen's go_name hold one rule (Json is JSON).
199+
// The generated encrypt/eql table records both names for every type, so a
200+
// type whose GoName the Go rule does not reproduce is a rule the two no
201+
// longer share. This test lives here because the command links encrypt/eql;
202+
// the stashgen package's own tests must not, or the embedded engine they
203+
// test changes build.
204+
func TestEQLGoNameAgreesWithTheGeneratedPackage(t *testing.T) {
205+
if len(eql.Types) == 0 {
206+
t.Fatal("the generated eql.Types table is empty")
207+
}
208+
renamed := 0
209+
for _, typ := range eql.Types {
210+
if got := stashgen.EQLGoName(typ.Name); got != typ.GoName {
211+
t.Errorf("%s: EQLGoName = %s, generated GoName = %s", typ.Name, got, typ.GoName)
212+
}
213+
if typ.Name != typ.GoName {
214+
renamed++
215+
}
216+
}
217+
if renamed == 0 {
218+
t.Fatal("the rule renamed nothing: the Json family is spelled JSON")
219+
}
220+
if stashgen.EQLGoName("Json") != "JSON" || stashgen.EQLGoName("TextEq") != "TextEq" || stashgen.EQLGoName("SteVecQuery") != "SteVecQuery" {
221+
t.Fatal("the one rule: a Json prefix is JSON, every other name is itself")
222+
}
223+
}
224+
181225
func TestModelFlagsRepeat(t *testing.T) {
182226
var m modelFlags
183227
for _, s := range []string{"Rows=ContactRow", "Legacy=userdb.Contact:contactRow"} {

‎languages/golang/stashgen/read.go‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,14 @@ type modelField struct {
124124
output *output
125125
}
126126

127-
// eqlGoName is the Go type name in encrypt/eql of an EQL type the engine
127+
// EQLGoName is the Go type name in encrypt/eql of an EQL type the engine
128128
// names: the same name in every language, save the JSON family, whose Go
129-
// names start with JSON (Json -> JSON, JsonSearch -> JSONSearch), as Go
130-
// spells initialisms. The generated encrypt/eql package follows the same
131-
// rule (eql-codegen's go_eql renderer).
132-
func eqlGoName(name string) string {
129+
// names start with JSON (Json is JSON), as Go spells initialisms. The
130+
// generated encrypt/eql package follows the same rule (eql-codegen's go_eql
131+
// renderer), and its Types table records both names, which is what holds
132+
// the two in agreement (a test in cmd/stashgen reads it; this package's own
133+
// tests cannot link encrypt/eql without changing the engine they test).
134+
func EQLGoName(name string) string {
133135
if strings.HasPrefix(name, "Json") {
134136
return "JSON" + strings.TrimPrefix(name, "Json")
135137
}
@@ -610,10 +612,10 @@ func (r *reader) buildFields(c *collected) error {
610612
return fieldErr(typeName, cf.goName, "the engine cannot produce the EQL type %s yet: %s", field.EQLType, reason)
611613
}
612614
f.imports.add(eqlPath, "eql")
613-
g.outputType = "eql." + eqlGoName(eqlType.Name)
614-
g.outputs = []output{{name: "EQL", typeExpr: g.outputType, pathType: eqlPath + "." + eqlGoName(eqlType.Name)}}
615+
g.outputType = "eql." + EQLGoName(eqlType.Name)
616+
g.outputs = []output{{name: "EQL", typeExpr: g.outputType, pathType: eqlPath + "." + EQLGoName(eqlType.Name)}}
615617
if eqlType.Query != "" {
616-
g.queryType = "eql." + eqlGoName(eqlType.Query)
618+
g.queryType = "eql." + EQLGoName(eqlType.Query)
617619
}
618620
default:
619621
g.outputType = f.encName + cf.goName

0 commit comments

Comments
 (0)