Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ jobs:

- name: Run tests
run: |
go test ./cmd/... ./pkg/cli/... ./pkg/helpers/proxy/... ./pkg/helpers/routes/... \
-json -coverprofile=coverage.out -covermode=atomic \
PKGS=$(find . -name '*.go' -not -path './pkg/data/*' -not -path './.git/*' -not -path '*/testdata/*' \
| xargs -n1 dirname | sort -u \
| sed 's|^\./|github.com/felipegenef/gothicframework/v2/|; s|^\.$|github.com/felipegenef/gothicframework/v2|')
GOWORK=off go test $PKGS -json -coverprofile=coverage.out -covermode=atomic -timeout 20s \
2>&1 | tee /tmp/gotest.log | gotestfmt

- name: Upload coverage to Codecov
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ main
main.exe
gothicframework
gothicframework.exe
.claude/
.claude/
coverage.out
76 changes: 76 additions & 0 deletions cmd/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cmd

import (
"os"
"testing"

gothic_cli "github.com/felipegenef/gothicframework/v2/pkg/cli"
"github.com/spf13/cobra"
)

// scaffoldSrc creates the minimal src/ tree the file-based router walks so that
// FileBasedRouter.Render succeeds without any real page files.
func scaffoldSrc(t *testing.T) {
t.Helper()
for _, d := range []string{"src/pages", "src/components", "src/api", "src/routes"} {
if err := os.MkdirAll(d, 0o755); err != nil {
t.Fatalf("mkdir %s: %v", d, err)
}
}
}

func TestNewBuildCommandCli(t *testing.T) {
cli := gothic_cli.NewCli()
cmd := newBuildCommandCli(&cli)
if cmd.cli == nil {
t.Fatal("expected cli to be set on BuildCommand")
}
}

func TestBuildSucceedsWithScaffold(t *testing.T) {
chdirTemp(t)
scaffoldSrc(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo"}`)

cli := gothic_cli.NewCli()
cmd := newBuildCommandCli(&cli)
if err := cmd.Build(); err != nil {
t.Fatalf("Build() unexpected error: %v", err)
}
if _, err := os.Stat("src/routes/routes_gen.go"); err != nil {
t.Errorf("expected routes_gen.go to be generated: %v", err)
}
}

func TestBuildFailsWithoutConfig(t *testing.T) {
chdirTemp(t)
// No gothic-config.json present: Templ.Render succeeds (no templ files),
// then GetConfig must fail.
cli := gothic_cli.NewCli()
cmd := newBuildCommandCli(&cli)
if err := cmd.Build(); err == nil {
t.Fatal("expected Build() to fail without gothic-config.json")
}
}

func TestBuildFailsWithoutSrcPages(t *testing.T) {
chdirTemp(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo"}`)
// No src/ tree: FileBasedRouter.Render must fail walking ./src/pages.
cli := gothic_cli.NewCli()
cmd := newBuildCommandCli(&cli)
if err := cmd.Build(); err == nil {
t.Fatal("expected Build() to fail without src/pages directory")
}
}

func TestNewBuildCommandRunE(t *testing.T) {
chdirTemp(t)
scaffoldSrc(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo"}`)

runE := newBuildCommand(gothic_cli.NewCli())
if err := runE(&cobra.Command{}, nil); err != nil {
t.Fatalf("build RunE unexpected error: %v", err)
}
}
66 changes: 66 additions & 0 deletions cmd/cmd_testhelpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package cmd

import (
"os"
"path/filepath"
"runtime"
"testing"
)

// chdirTemp creates a fresh temp directory, chdir's into it for the duration of
// the test, and restores the original working dir on cleanup. Returns the temp
// dir path.
func chdirTemp(t *testing.T) string {
t.Helper()
dir := t.TempDir()
orig, err := os.Getwd()
if err != nil {
t.Fatalf("getwd: %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("chdir: %v", err)
}
t.Cleanup(func() { _ = os.Chdir(orig) })
return dir
}

// writeConfig writes a gothic-config.json into the current working dir.
func writeConfig(t *testing.T, contents string) {
t.Helper()
if err := os.WriteFile("gothic-config.json", []byte(contents), 0o644); err != nil {
t.Fatalf("write gothic-config.json: %v", err)
}
}

// writeGoMod writes a minimal go.mod so astx.NewLoader / packages.Load can run
// against the temp project. With no .go files, ScanPages returns zero pages
// without error, letting wasm-driving code paths proceed past the scan stage.
func writeGoMod(t *testing.T, module string) {
t.Helper()
contents := "module " + module + "\n\ngo 1.23\n"
if err := os.WriteFile("go.mod", []byte(contents), 0o644); err != nil {
t.Fatalf("write go.mod: %v", err)
}
}

// writeFakeTailwind writes an executable no-op script that any TailwindHelper
// pointed at it (via the tailwindBinary config override) will treat as the
// Tailwind CLI. It simply exits 0, so Build()/EnsureBinary() succeed without a
// real download or compile. Skips the test on Windows where shell scripts are
// not executable as-is. Returns the absolute binary path.
func writeFakeTailwind(t *testing.T, ok bool) string {
t.Helper()
if runtime.GOOS == "windows" {
t.Skip("fake shell binary not supported on windows")
}
exit := "0"
if !ok {
exit = "1"
}
path := filepath.Join(t.TempDir(), "faketailwind")
script := "#!/bin/sh\nexit " + exit + "\n"
if err := os.WriteFile(path, []byte(script), 0o755); err != nil {
t.Fatalf("write fake tailwind: %v", err)
}
return path
}
48 changes: 48 additions & 0 deletions cmd/css_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"testing"

gothic_cli "github.com/felipegenef/gothicframework/v2/pkg/cli"
"github.com/spf13/cobra"
)

func TestNewCssCommandCli(t *testing.T) {
cli := gothic_cli.NewCli()
cmd := newCssCommandCli(&cli)
if cmd.cli == nil {
t.Fatal("expected cli to be set on CssCommand")
}
}

func TestCssCommandSucceedsWithFakeBinary(t *testing.T) {
bin := writeFakeTailwind(t, true)
chdirTemp(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo","tailwindBinary":"`+bin+`"}`)

runE := newCssCommand(gothic_cli.NewCli())
if err := runE(&cobra.Command{}, nil); err != nil {
t.Fatalf("css RunE unexpected error: %v", err)
}
}

func TestCssCommandPropagatesBinaryError(t *testing.T) {
bin := writeFakeTailwind(t, false) // exits 1
chdirTemp(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo","tailwindBinary":"`+bin+`"}`)

runE := newCssCommand(gothic_cli.NewCli())
if err := runE(&cobra.Command{}, nil); err == nil {
t.Fatal("expected css RunE to fail when tailwind binary exits non-zero")
}
}

func TestCssCommandFailsWithMissingBinaryOverride(t *testing.T) {
chdirTemp(t)
writeConfig(t, `{"projectName":"demo","goModuleName":"demo","tailwindBinary":"/nonexistent/tailwind-bin"}`)

runE := newCssCommand(gothic_cli.NewCli())
if err := runE(&cobra.Command{}, nil); err == nil {
t.Fatal("expected css RunE to fail when tailwind binary override does not exist")
}
}
Loading
Loading