package templates import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestInterpolateVars(t *testing.T) { tests := []struct { name string content string vars map[string]string expected string }{ { name: "single variable", content: "Hello {{PROJECT_NAME}}!", vars: map[string]string{ "PROJECT_NAME": "myapp", }, expected: "Hello myapp!", }, { name: "multiple variables", content: "Project {{PROJECT_NAME}} at {{DOMAIN}} from {{GIT_URL}}", vars: map[string]string{ "PROJECT_NAME": "myapp", "DOMAIN": "myapp.threesix.ai", "GIT_URL": "https://git.example.com/org/myapp.git", }, expected: "Project myapp at myapp.threesix.ai from https://git.example.com/org/myapp.git", }, { name: "no variables", content: "Static content with no placeholders", vars: map[string]string{}, expected: "Static content with no placeholders", }, { name: "variable not in map", content: "Hello {{UNKNOWN_VAR}}!", vars: map[string]string{ "PROJECT_NAME": "myapp", }, expected: "Hello {{UNKNOWN_VAR}}!", }, { name: "empty vars map", content: "Hello {{PROJECT_NAME}}!", vars: nil, expected: "Hello {{PROJECT_NAME}}!", }, { name: "repeated variable", content: "{{NAME}} and {{NAME}} again", vars: map[string]string{ "NAME": "test", }, expected: "test and test again", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := interpolateVars(tt.content, tt.vars) assert.Equal(t, tt.expected, result) }) } } func TestIsValidTemplateName(t *testing.T) { tests := []struct { name string input string expected bool }{ {"valid simple", "default", true}, {"valid with dash", "astro-landing", true}, {"valid with numbers", "go-api-v2", true}, {"empty", "", false}, {"starts with number", "123template", false}, {"starts with dash", "-invalid", false}, {"contains uppercase", "MyTemplate", false}, {"contains underscore", "my_template", false}, {"contains dot", "my.template", false}, {"contains slash", "my/template", false}, {"path traversal attempt", "../evil", false}, {"too long", "a" + string(make([]byte, 64)), false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := isValidTemplateName(tt.input) assert.Equal(t, tt.expected, result, "isValidTemplateName(%q)", tt.input) }) } } func TestListTemplateFiles(t *testing.T) { // Test that default template exists and has expected files files, err := listTemplateFiles("default") require.NoError(t, err) // Should have at least these core files assert.Contains(t, files, ".woodpecker.yml", "default template should have .woodpecker.yml") assert.Contains(t, files, "Dockerfile", "default template should have Dockerfile") assert.Contains(t, files, "README.md", "default template should have README.md") } func TestListTemplateFiles_InvalidTemplate(t *testing.T) { _, err := listTemplateFiles("nonexistent-template") assert.Error(t, err, "should error for nonexistent template") } func TestAvailableTemplates(t *testing.T) { // Verify all declared templates actually exist for _, tmpl := range availableTemplates { t.Run(tmpl.Name, func(t *testing.T) { files, err := listTemplateFiles(tmpl.Name) require.NoError(t, err, "template %s should exist", tmpl.Name) assert.NotEmpty(t, files, "template %s should have files", tmpl.Name) // Every template should have these core files assert.Contains(t, files, ".woodpecker.yml", "template %s should have .woodpecker.yml", tmpl.Name) }) } } func TestListTemplateFiles_TmplExtensionStripped(t *testing.T) { // go-api template has go.mod.tmpl which should be listed as go.mod files, err := listTemplateFiles("go-api") require.NoError(t, err) // Should contain go.mod (not go.mod.tmpl) assert.Contains(t, files, "go.mod", "go.mod.tmpl should be listed as go.mod") // Should NOT contain the .tmpl extension for _, f := range files { assert.NotContains(t, f, ".tmpl", "file %s should not have .tmpl extension", f) } } func TestSkeletonTemplate(t *testing.T) { // Test that skeleton template exists and has expected files files, err := listTemplateFiles("skeleton") require.NoError(t, err) // Should have the core monorepo files assert.Contains(t, files, "CLAUDE.md", "skeleton template should have CLAUDE.md") assert.Contains(t, files, "README.md", "skeleton template should have README.md") assert.Contains(t, files, ".woodpecker.yml", "skeleton template should have .woodpecker.yml") assert.Contains(t, files, "docker-compose.yml", "skeleton template should have docker-compose.yml") assert.Contains(t, files, "go.work", "skeleton template should have go.work") assert.Contains(t, files, "Procfile", "skeleton template should have Procfile") assert.Contains(t, files, ".gitignore", "skeleton template should have .gitignore") assert.Contains(t, files, ".golangci.yml", "skeleton template should have .golangci.yml") // Should have scripts assert.Contains(t, files, "scripts/dev.sh", "skeleton template should have scripts/dev.sh") assert.Contains(t, files, "scripts/install.sh", "skeleton template should have scripts/install.sh") assert.Contains(t, files, "scripts/quality.sh", "skeleton template should have scripts/quality.sh") assert.Contains(t, files, "scripts/discover.sh", "skeleton template should have scripts/discover.sh") // Should have .claude structure assert.Contains(t, files, ".claude/settings.local.json", "skeleton template should have .claude/settings.local.json") assert.Contains(t, files, ".claude/guides/local/setup.md", "skeleton template should have .claude/guides/local/setup.md") assert.Contains(t, files, ".claude/guides/ops/deploying.md", "skeleton template should have .claude/guides/ops/deploying.md") assert.Contains(t, files, ".claude/skills/code-review/SKILL.md", "skeleton template should have .claude/skills/code-review/SKILL.md") // Should have component directory placeholders assert.Contains(t, files, "services/.gitkeep", "skeleton template should have services/.gitkeep") assert.Contains(t, files, "workers/.gitkeep", "skeleton template should have workers/.gitkeep") assert.Contains(t, files, "apps/.gitkeep", "skeleton template should have apps/.gitkeep") assert.Contains(t, files, "cli/.gitkeep", "skeleton template should have cli/.gitkeep") // Should have pkg directory files assert.Contains(t, files, "pkg/go.mod", "skeleton template should have pkg/go.mod") assert.Contains(t, files, "pkg/README.md", "skeleton template should have pkg/README.md") } func TestSkeletonTemplateInfo(t *testing.T) { // Verify skeleton template metadata assert.Equal(t, "skeleton", skeletonTemplate.Name) assert.Equal(t, "monorepo", skeletonTemplate.Stack) assert.NotEmpty(t, skeletonTemplate.Description) }