Dragon-Knight/internal/template/template_test.go

349 lines
7.6 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 TestIncludeDisabled(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)
}
mainTemplate := `<div>{include "partial.html"}</div>`
err = os.WriteFile(filepath.Join(templatesDir, "main.html"), []byte(mainTemplate), 0644)
if err != nil {
t.Fatal(err)
}
cache := NewCache(tmpDir)
tmpl, err := cache.Load("main.html")
if err != nil {
t.Fatal(err)
}
opts := RenderOptions{ResolveIncludes: false}
result := tmpl.RenderNamedWithOptions(opts, map[string]any{})
expected := `<div>{include "partial.html"}</div>`
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)
}
}