package template import ( "os" "path/filepath" "testing" "time" ) func TestNewCache(t *testing.T) { cache := NewCache("") if cache == nil { t.Fatal("NewCache returned nil") } if cache.templates == nil { t.Fatal("templates map not initialized") } } func TestPositionalReplacement(t *testing.T) { tmpl := &Template{ name: "test", content: "Hello {0}, you are {1} years old!", } result := tmpl.RenderPositional("Alice", 25) expected := "Hello Alice, you are 25 years old!" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestNamedReplacement(t *testing.T) { tmpl := &Template{ name: "test", content: "Hello {name}, you are {age} years old!", } data := map[string]any{ "name": "Bob", "age": 30, } result := tmpl.RenderNamed(data) expected := "Hello Bob, you are 30 years old!" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestDotNotationReplacement(t *testing.T) { tmpl := &Template{ name: "test", content: "User: {user.name}, Email: {user.contact.email}", } data := map[string]any{ "user": map[string]any{ "name": "Charlie", "contact": map[string]any{ "email": "charlie@example.com", }, }, } result := tmpl.RenderNamed(data) expected := "User: Charlie, Email: charlie@example.com" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestTemplateLoadingAndCaching(t *testing.T) { tmpDir, err := os.MkdirTemp("", "template_test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) templatesDir := filepath.Join(tmpDir, "templates") err = os.MkdirAll(templatesDir, 0755) if err != nil { t.Fatal(err) } templateFile := filepath.Join(templatesDir, "test.html") content := "Hello {name}!" err = os.WriteFile(templateFile, []byte(content), 0644) if err != nil { t.Fatal(err) } cache := NewCache(tmpDir) tmpl, err := cache.Load("test.html") if err != nil { t.Fatal(err) } if tmpl.content != content { t.Errorf("Expected content %q, got %q", content, tmpl.content) } tmpl2, err := cache.Load("test.html") if err != nil { t.Fatal(err) } if tmpl != tmpl2 { t.Error("Template should be cached and return same instance") } } func TestTemplateReloading(t *testing.T) { tmpDir, err := os.MkdirTemp("", "template_test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) templatesDir := filepath.Join(tmpDir, "templates") err = os.MkdirAll(templatesDir, 0755) if err != nil { t.Fatal(err) } templateFile := filepath.Join(templatesDir, "test.html") content1 := "Hello {name}!" err = os.WriteFile(templateFile, []byte(content1), 0644) if err != nil { t.Fatal(err) } cache := NewCache(tmpDir) tmpl, err := cache.Load("test.html") if err != nil { t.Fatal(err) } if tmpl.content != content1 { t.Errorf("Expected content %q, got %q", content1, tmpl.content) } time.Sleep(10 * time.Millisecond) content2 := "Hi {name}, welcome!" err = os.WriteFile(templateFile, []byte(content2), 0644) if err != nil { t.Fatal(err) } tmpl2, err := cache.Load("test.html") if err != nil { t.Fatal(err) } if tmpl2.content != content2 { t.Errorf("Expected reloaded content %q, got %q", content2, tmpl2.content) } } func TestGetNestedValue(t *testing.T) { tmpl := &Template{} data := map[string]any{ "level1": map[string]any{ "level2": map[string]any{ "value": "found", }, }, } result := tmpl.getNestedValue(data, "level1.level2.value") if result != "found" { t.Errorf("Expected 'found', got %v", result) } result = tmpl.getNestedValue(data, "level1.nonexistent") if result != nil { t.Errorf("Expected nil for nonexistent path, got %v", result) } } func TestMixedReplacementTypes(t *testing.T) { tmpl := &Template{ name: "test", content: "Hello {name}, you have {count} {items.type}s!", } data := map[string]any{ "name": "Dave", "count": 5, "items": map[string]any{ "type": "apple", }, } result := tmpl.RenderNamed(data) expected := "Hello Dave, you have 5 apples!" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestIncludeSupport(t *testing.T) { tmpDir, err := os.MkdirTemp("", "template_test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) templatesDir := filepath.Join(tmpDir, "templates") err = os.MkdirAll(templatesDir, 0755) if err != nil { t.Fatal(err) } // Create main template with include mainTemplate := `{include "header.html"}Hello {name}!` err = os.WriteFile(filepath.Join(templatesDir, "main.html"), []byte(mainTemplate), 0644) if err != nil { t.Fatal(err) } // Create included template headerTemplate := `{title}` err = os.WriteFile(filepath.Join(templatesDir, "header.html"), []byte(headerTemplate), 0644) if err != nil { t.Fatal(err) } cache := NewCache(tmpDir) tmpl, err := cache.Load("main.html") if err != nil { t.Fatal(err) } data := map[string]any{ "name": "Alice", "title": "Welcome", } result := tmpl.RenderNamed(data) expected := "WelcomeHello Alice!" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestBlocksAndYield(t *testing.T) { tmpl := &Template{ name: "test", content: `{block "content"}Default content{/block}
{yield content}
`, } result := tmpl.RenderNamed(map[string]any{}) expected := "
Default content
" if result != expected { t.Errorf("Expected %q, got %q", expected, result) } } func TestTemplateInheritance(t *testing.T) { tmpDir, err := os.MkdirTemp("", "template_test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) templatesDir := filepath.Join(tmpDir, "templates") err = os.MkdirAll(templatesDir, 0755) if err != nil { t.Fatal(err) } // Layout template layoutTemplate := `{title}{yield content}` err = os.WriteFile(filepath.Join(templatesDir, "layout.html"), []byte(layoutTemplate), 0644) if err != nil { t.Fatal(err) } // Page template that extends layout pageTemplate := `{include "layout.html"}{block "content"}

Welcome {name}!

This is the content block.

{/block}` err = os.WriteFile(filepath.Join(templatesDir, "page.html"), []byte(pageTemplate), 0644) if err != nil { t.Fatal(err) } cache := NewCache(tmpDir) tmpl, err := cache.Load("page.html") if err != nil { t.Fatal(err) } data := map[string]any{ "title": "Test Page", "name": "Bob", } result := tmpl.RenderNamed(data) expected := `Test Page

Welcome Bob!

This is the content block.

` if result != expected { t.Errorf("Expected %q, got %q", expected, result) } }