This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
package bfcodegen
|
||||
|
||||
import (
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProcessEnumFileBasicStringEnum(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package mymodels
|
||||
|
||||
type Color string // @enum:type
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
ColorBlue Color = "blue"
|
||||
ColorGreen Color = "green"
|
||||
)
|
||||
`
|
||||
fp := writeTestFile(t, dir, "color.go", src)
|
||||
|
||||
enums, pkg, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, pkg, "mymodels")
|
||||
tst.AssertEqual(t, len(enums), 1)
|
||||
tst.AssertEqual(t, enums[0].EnumTypeName, "Color")
|
||||
tst.AssertEqual(t, enums[0].Type, "string")
|
||||
tst.AssertEqual(t, len(enums[0].Values), 3)
|
||||
tst.AssertEqual(t, enums[0].Values[0].VarName, "ColorRed")
|
||||
tst.AssertEqual(t, enums[0].Values[0].Value, `"red"`)
|
||||
tst.AssertEqual(t, enums[0].Values[1].VarName, "ColorBlue")
|
||||
tst.AssertEqual(t, enums[0].Values[2].VarName, "ColorGreen")
|
||||
}
|
||||
|
||||
func TestProcessEnumFileIntEnum(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package m
|
||||
|
||||
type Priority int // @enum:type
|
||||
|
||||
const (
|
||||
PriorityLow Priority = 0
|
||||
PriorityMedium Priority = 1
|
||||
PriorityHigh Priority = 2
|
||||
)
|
||||
`
|
||||
fp := writeTestFile(t, dir, "prio.go", src)
|
||||
|
||||
enums, pkg, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, pkg, "m")
|
||||
tst.AssertEqual(t, len(enums), 1)
|
||||
tst.AssertEqual(t, enums[0].EnumTypeName, "Priority")
|
||||
tst.AssertEqual(t, enums[0].Type, "int")
|
||||
tst.AssertEqual(t, len(enums[0].Values), 3)
|
||||
tst.AssertEqual(t, enums[0].Values[0].Value, "0")
|
||||
tst.AssertEqual(t, enums[0].Values[2].Value, "2")
|
||||
}
|
||||
|
||||
func TestProcessEnumFileWithDescriptions(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package m
|
||||
|
||||
type Status string // @enum:type
|
||||
|
||||
const (
|
||||
StatusActive Status = "active" // The active status
|
||||
StatusInactive Status = "inactive" // The inactive status
|
||||
)
|
||||
`
|
||||
fp := writeTestFile(t, dir, "s.go", src)
|
||||
|
||||
enums, _, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, len(enums), 1)
|
||||
tst.AssertEqual(t, len(enums[0].Values), 2)
|
||||
|
||||
v0 := enums[0].Values[0]
|
||||
tst.AssertTrue(t, v0.Description != nil)
|
||||
tst.AssertEqual(t, *v0.Description, "The active status")
|
||||
tst.AssertTrue(t, v0.Data == nil)
|
||||
}
|
||||
|
||||
func TestProcessEnumFileWithDataComment(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package m
|
||||
|
||||
type Severity string // @enum:type
|
||||
|
||||
const (
|
||||
SeverityLow Severity = "low" // {"description": "Low severity", "weight": 1}
|
||||
SeverityHigh Severity = "high" // {"description": "High severity", "weight": 9}
|
||||
)
|
||||
`
|
||||
fp := writeTestFile(t, dir, "sev.go", src)
|
||||
|
||||
enums, _, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, len(enums), 1)
|
||||
tst.AssertEqual(t, len(enums[0].Values), 2)
|
||||
|
||||
v0 := enums[0].Values[0]
|
||||
tst.AssertTrue(t, v0.Data != nil)
|
||||
tst.AssertTrue(t, v0.Description != nil)
|
||||
tst.AssertEqual(t, *v0.Description, "Low severity")
|
||||
}
|
||||
|
||||
func TestProcessEnumFileNonMatchingValuesNotAttached(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package m
|
||||
|
||||
type Color string // @enum:type
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
)
|
||||
|
||||
const (
|
||||
OtherX OtherType = "x"
|
||||
)
|
||||
`
|
||||
fp := writeTestFile(t, dir, "c.go", src)
|
||||
|
||||
enums, _, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, len(enums), 1)
|
||||
tst.AssertEqual(t, len(enums[0].Values), 1)
|
||||
tst.AssertEqual(t, enums[0].Values[0].VarName, "ColorRed")
|
||||
}
|
||||
|
||||
func TestProcessEnumFileGeneratedHeaderSkipped(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `// Code generated by enum-generate.go DO NOT EDIT.
|
||||
|
||||
package x
|
||||
|
||||
type Foo string // @enum:type
|
||||
`
|
||||
fp := writeTestFile(t, dir, "skip.go", src)
|
||||
|
||||
enums, pkg, err := processEnumFile(dir, fp, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertEqual(t, pkg, "")
|
||||
tst.AssertEqual(t, len(enums), 0)
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentValid(t *testing.T) {
|
||||
m, ok := tryParseDataComment(`{"description": "hello", "weight": 5}`)
|
||||
tst.AssertTrue(t, ok)
|
||||
descr, _ := m["description"].(string)
|
||||
tst.AssertEqual(t, descr, "hello")
|
||||
weight, _ := m["weight"].(float64)
|
||||
tst.AssertEqual(t, weight, float64(5))
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentBool(t *testing.T) {
|
||||
m, ok := tryParseDataComment(`{"a": true, "b": false}`)
|
||||
tst.AssertTrue(t, ok)
|
||||
a, _ := m["a"].(bool)
|
||||
tst.AssertTrue(t, a)
|
||||
b, _ := m["b"].(bool)
|
||||
tst.AssertFalse(t, b)
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentRejectsNull(t *testing.T) {
|
||||
// null becomes a nil interface — its reflect.Kind is Invalid, not Pointer,
|
||||
// so it does not match any of the allowed kinds and is rejected.
|
||||
_, ok := tryParseDataComment(`{"x": null}`)
|
||||
tst.AssertFalse(t, ok)
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentInvalidJSON(t *testing.T) {
|
||||
_, ok := tryParseDataComment(`{not valid json}`)
|
||||
tst.AssertFalse(t, ok)
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentRejectsArrays(t *testing.T) {
|
||||
// arrays as values are not in the supported kinds list
|
||||
_, ok := tryParseDataComment(`{"x": [1, 2, 3]}`)
|
||||
tst.AssertFalse(t, ok)
|
||||
}
|
||||
|
||||
func TestTryParseDataCommentRejectsObjects(t *testing.T) {
|
||||
_, ok := tryParseDataComment(`{"x": {"nested": 1}}`)
|
||||
tst.AssertFalse(t, ok)
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsEndToEnd(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package models
|
||||
|
||||
type Color string // @enum:type
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
ColorGreen Color = "green"
|
||||
ColorBlue Color = "blue"
|
||||
)
|
||||
`
|
||||
writeTestFile(t, dir, "color.go", src)
|
||||
dest := filepath.Join(dir, "enum_gen.go")
|
||||
|
||||
err := GenerateEnumSpecs(dir, dest, EnumGenOptions{
|
||||
DebugOutput: langext.PFalse,
|
||||
GoFormat: langext.PTrue,
|
||||
})
|
||||
tst.AssertNoErr(t, err)
|
||||
|
||||
out, err := os.ReadFile(dest)
|
||||
tst.AssertNoErr(t, err)
|
||||
outStr := string(out)
|
||||
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "package models"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ChecksumEnumGenerator"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ParseColor"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ColorValues"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ColorRed"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ColorBlue"))
|
||||
tst.AssertTrue(t, strings.Contains(outStr, "ColorGreen"))
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsDeterministic(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package models
|
||||
|
||||
type Status string // @enum:type
|
||||
|
||||
const (
|
||||
StatusActive Status = "active" // The active one
|
||||
StatusOff Status = "off" // The off one
|
||||
)
|
||||
`
|
||||
writeTestFile(t, dir, "s.go", src)
|
||||
|
||||
s1, cs1, changed1, err := _generateEnumSpecs(dir, "", "N/A", true, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertTrue(t, changed1)
|
||||
|
||||
s2, cs2, changed2, err := _generateEnumSpecs(dir, "", "N/A", true, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertTrue(t, changed2)
|
||||
|
||||
tst.AssertEqual(t, cs1, cs2)
|
||||
tst.AssertEqual(t, s1, s2)
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsNoChangeWhenChecksumMatches(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package models
|
||||
|
||||
type Status string // @enum:type
|
||||
|
||||
const (
|
||||
StatusActive Status = "active"
|
||||
)
|
||||
`
|
||||
writeTestFile(t, dir, "s.go", src)
|
||||
|
||||
_, cs, changed, err := _generateEnumSpecs(dir, "", "N/A", true, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertTrue(t, changed)
|
||||
|
||||
s2, cs2, changed2, err := _generateEnumSpecs(dir, "", cs, true, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertFalse(t, changed2)
|
||||
tst.AssertEqual(t, cs2, cs)
|
||||
tst.AssertEqual(t, s2, "")
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsWithoutGoFormat(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package models
|
||||
|
||||
type Color string // @enum:type
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
)
|
||||
`
|
||||
writeTestFile(t, dir, "c.go", src)
|
||||
|
||||
out, _, _, err := _generateEnumSpecs(dir, "", "N/A", false, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertTrue(t, strings.Contains(out, "ColorRed"))
|
||||
tst.AssertTrue(t, strings.Contains(out, "package models"))
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsErrorsWithoutPackage(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `// Code generated by enum-generate.go DO NOT EDIT.
|
||||
|
||||
package x
|
||||
|
||||
type Foo string // @enum:type
|
||||
`
|
||||
writeTestFile(t, dir, "z.go", src)
|
||||
|
||||
_, _, _, err := _generateEnumSpecs(dir, "", "N/A", false, false)
|
||||
tst.AssertTrue(t, err != nil)
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsMissingDir(t *testing.T) {
|
||||
dir := filepath.Join(t.TempDir(), "definitely-missing")
|
||||
_, _, _, err := _generateEnumSpecs(dir, "", "N/A", false, false)
|
||||
tst.AssertTrue(t, err != nil)
|
||||
}
|
||||
|
||||
func TestFmtEnumOutputContainsTypes(t *testing.T) {
|
||||
descr := "the red one"
|
||||
enums := []EnumDef{
|
||||
{
|
||||
File: "color.go",
|
||||
FileRelative: "color.go",
|
||||
EnumTypeName: "Color",
|
||||
Type: "string",
|
||||
Values: []EnumDefVal{
|
||||
{VarName: "ColorRed", Value: `"red"`, Description: &descr},
|
||||
{VarName: "ColorBlue", Value: `"blue"`, Description: &descr},
|
||||
},
|
||||
},
|
||||
}
|
||||
out := fmtEnumOutput("CHK1", enums, "models")
|
||||
tst.AssertTrue(t, strings.Contains(out, "package models"))
|
||||
tst.AssertTrue(t, strings.Contains(out, "CHK1"))
|
||||
tst.AssertTrue(t, strings.Contains(out, "ColorRed"))
|
||||
tst.AssertTrue(t, strings.Contains(out, "ColorBlue"))
|
||||
tst.AssertTrue(t, strings.Contains(out, "ParseColor"))
|
||||
}
|
||||
|
||||
func TestGenerateEnumSpecsSkipsGenFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
src := `package models
|
||||
|
||||
type Color string // @enum:type
|
||||
|
||||
const (
|
||||
ColorRed Color = "red"
|
||||
)
|
||||
`
|
||||
writeTestFile(t, dir, "c.go", src)
|
||||
|
||||
// generated file in same dir - should be filtered out
|
||||
gensrc := `package models
|
||||
|
||||
type ShouldBeIgnored string // @enum:type
|
||||
`
|
||||
writeTestFile(t, dir, "ignored_gen.go", gensrc)
|
||||
|
||||
out, _, _, err := _generateEnumSpecs(dir, filepath.Join(dir, "enum_gen.go"), "N/A", false, false)
|
||||
tst.AssertNoErr(t, err)
|
||||
tst.AssertTrue(t, strings.Contains(out, "ColorRed"))
|
||||
tst.AssertFalse(t, strings.Contains(out, "ShouldBeIgnored"))
|
||||
}
|
||||
Reference in New Issue
Block a user