package bfcodegen import ( "git.blackforestbytes.com/BlackForestBytes/goext/langext" "git.blackforestbytes.com/BlackForestBytes/goext/tst" "os" "path/filepath" "strings" "testing" ) func writeTestFile(t *testing.T, dir string, name string, content string) string { t.Helper() p := filepath.Join(dir, name) err := os.WriteFile(p, []byte(content), 0o644) tst.AssertNoErr(t, err) return p } func TestProcessIDFileSimple(t *testing.T) { dir := t.TempDir() src := `package mymodels type UserID string // @id:type type OrderID string // @id:type ` fp := writeTestFile(t, dir, "models.go", src) ids, pkg, err := processIDFile(dir, fp, false) tst.AssertNoErr(t, err) tst.AssertEqual(t, pkg, "mymodels") tst.AssertEqual(t, len(ids), 2) tst.AssertEqual(t, ids[0].Name, "UserID") tst.AssertEqual(t, ids[1].Name, "OrderID") tst.AssertEqual(t, ids[0].FileRelative, "models.go") } func TestProcessIDFileNoMatches(t *testing.T) { dir := t.TempDir() src := `package x type Foo string type Bar int type Baz string // not the right marker ` fp := writeTestFile(t, dir, "x.go", src) ids, pkg, err := processIDFile(dir, fp, false) tst.AssertNoErr(t, err) tst.AssertEqual(t, pkg, "x") tst.AssertEqual(t, len(ids), 0) } func TestProcessIDFileGeneratedHeaderSkipped(t *testing.T) { dir := t.TempDir() src := `// Code generated by id-generate.go DO NOT EDIT. package x type SkipMeID string // @id:type ` fp := writeTestFile(t, dir, "skip.go", src) ids, pkg, err := processIDFile(dir, fp, false) tst.AssertNoErr(t, err) tst.AssertEqual(t, pkg, "") tst.AssertEqual(t, len(ids), 0) } func TestProcessIDFileMissingFile(t *testing.T) { _, _, err := processIDFile(t.TempDir(), filepath.Join(t.TempDir(), "does_not_exist.go"), false) tst.AssertTrue(t, err != nil) } func TestGenerateIDSpecsEndToEnd(t *testing.T) { dir := t.TempDir() src1 := `package models type UserID string // @id:type type AnyID string // @id:type ` writeTestFile(t, dir, "a_models.go", src1) src2 := `package models type OrderID string // @id:type ` writeTestFile(t, dir, "b_models.go", src2) dest := filepath.Join(dir, "id_gen.go") err := GenerateIDSpecs(dir, dest, IDGenOptions{DebugOutput: langext.PFalse}) 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, "ChecksumIDGenerator")) tst.AssertTrue(t, strings.Contains(outStr, "func NewUserID()")) tst.AssertTrue(t, strings.Contains(outStr, "func NewOrderID()")) tst.AssertTrue(t, strings.Contains(outStr, "func NewAnyID()")) tst.AssertTrue(t, strings.Contains(outStr, "AsAny()")) } func TestGenerateIDSpecsIdempotentWhenUnchanged(t *testing.T) { dir := t.TempDir() src := `package models type SomeID string // @id:type ` writeTestFile(t, dir, "models.go", src) dest := filepath.Join(dir, "id_gen.go") err := GenerateIDSpecs(dir, dest, IDGenOptions{}) tst.AssertNoErr(t, err) stat1, err := os.Stat(dest) tst.AssertNoErr(t, err) content1, err := os.ReadFile(dest) tst.AssertNoErr(t, err) err = GenerateIDSpecs(dir, dest, IDGenOptions{}) tst.AssertNoErr(t, err) stat2, err := os.Stat(dest) tst.AssertNoErr(t, err) content2, err := os.ReadFile(dest) tst.AssertNoErr(t, err) tst.AssertEqual(t, stat1.ModTime().Equal(stat2.ModTime()), true) tst.AssertEqual(t, string(content1), string(content2)) } func TestGenerateIDSpecsRegeneratesAfterChange(t *testing.T) { dir := t.TempDir() src := `package models type FirstID string // @id:type ` fp := writeTestFile(t, dir, "models.go", src) dest := filepath.Join(dir, "id_gen.go") err := GenerateIDSpecs(dir, dest, IDGenOptions{}) tst.AssertNoErr(t, err) content1, err := os.ReadFile(dest) tst.AssertNoErr(t, err) tst.AssertTrue(t, strings.Contains(string(content1), "FirstID")) tst.AssertFalse(t, strings.Contains(string(content1), "SecondID")) src2 := `package models type FirstID string // @id:type type SecondID string // @id:type ` err = os.WriteFile(fp, []byte(src2), 0o644) tst.AssertNoErr(t, err) err = GenerateIDSpecs(dir, dest, IDGenOptions{}) tst.AssertNoErr(t, err) content2, err := os.ReadFile(dest) tst.AssertNoErr(t, err) tst.AssertTrue(t, strings.Contains(string(content2), "SecondID")) } func TestGenerateIDSpecsErrorsWithoutPackage(t *testing.T) { dir := t.TempDir() src := `// Code generated by id-generate.go DO NOT EDIT. package x type SkippedID string // @id:type ` writeTestFile(t, dir, "z.go", src) dest := filepath.Join(dir, "id_gen.go") err := GenerateIDSpecs(dir, dest, IDGenOptions{}) tst.AssertTrue(t, err != nil) } func TestGenerateIDSpecsMissingDir(t *testing.T) { dir := filepath.Join(t.TempDir(), "definitely-missing") err := GenerateIDSpecs(dir, filepath.Join(dir, "id_gen.go"), IDGenOptions{}) tst.AssertTrue(t, err != nil) } func TestFmtIDOutputContainsAllNames(t *testing.T) { ids := []IDDef{ {File: "a.go", FileRelative: "a.go", Name: "AlphaID"}, {File: "b.go", FileRelative: "b.go", Name: "BetaID"}, } out := fmtIDOutput("CHK_ABC", ids, "models") tst.AssertTrue(t, strings.Contains(out, "package models")) tst.AssertTrue(t, strings.Contains(out, "CHK_ABC")) tst.AssertTrue(t, strings.Contains(out, "AlphaID")) tst.AssertTrue(t, strings.Contains(out, "BetaID")) }