315 lines
6.8 KiB
Go
315 lines
6.8 KiB
Go
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 := `<html><head>{include "header.html"}</head><body>Hello {name}!</body></html>`
|
|
err = os.WriteFile(filepath.Join(templatesDir, "main.html"), []byte(mainTemplate), 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create included template
|
|
headerTemplate := `<title>{title}</title><meta charset="utf-8">`
|
|
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 := "<html><head><title>Welcome</title><meta charset=\"utf-8\"></head><body>Hello Alice!</body></html>"
|
|
|
|
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}<main>{yield content}</main>`,
|
|
}
|
|
|
|
result := tmpl.RenderNamed(map[string]any{})
|
|
expected := "<main>Default content</main>"
|
|
|
|
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 := `<!DOCTYPE html><html><head><title>{title}</title></head><body>{yield content}</body></html>`
|
|
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"}<h1>Welcome {name}!</h1><p>This is the content block.</p>{/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 := `<!DOCTYPE html><html><head><title>Test Page</title></head><body><h1>Welcome Bob!</h1><p>This is the content block.</p></body></html>`
|
|
|
|
if result != expected {
|
|
t.Errorf("Expected %q, got %q", expected, result)
|
|
}
|
|
}
|