@@ -486,3 +486,300 @@ func TestTheme_WithComplexData(t *testing.T) {
486486
487487 mockStore .AssertExpectations (t )
488488}
489+
490+ func TestTheme_Render_SimpleTemplate (t * testing.T ) {
491+ mockStore := & MockStore {}
492+ theme := NewTheme ("test" , mockStore )
493+
494+ ctx := context .Background ()
495+
496+ // Create a simple template
497+ templateContent := `<h1>{{.Title}}</h1>`
498+ testTemplate := createTestTemplate ("test" , "simple" , templateContent )
499+
500+ mockStore .On ("Find" , ctx , "test" , "simple" ).Return (testTemplate , nil ).Once ()
501+
502+ data := map [string ]string {"Title" : "Hello World" }
503+ result , err := theme .Render (ctx , "simple" , data )
504+
505+ assert .NoError (t , err )
506+ assert .Equal (t , "<h1>Hello World</h1>" , string (result ))
507+ assert .IsType (t , []byte {}, result , "Render should return []byte" )
508+
509+ mockStore .AssertExpectations (t )
510+ }
511+
512+ func TestTheme_Render_WithNilData (t * testing.T ) {
513+ mockStore := & MockStore {}
514+ theme := NewTheme ("test" , mockStore )
515+
516+ ctx := context .Background ()
517+
518+ // Create a template that works with nil data
519+ templateContent := `<div>Static content</div>`
520+ testTemplate := createTestTemplate ("test" , "static" , templateContent )
521+
522+ mockStore .On ("Find" , ctx , "test" , "static" ).Return (testTemplate , nil ).Once ()
523+
524+ result , err := theme .Render (ctx , "static" , nil )
525+
526+ assert .NoError (t , err )
527+ assert .Equal (t , "<div>Static content</div>" , string (result ))
528+
529+ mockStore .AssertExpectations (t )
530+ }
531+
532+ func TestTheme_Render_TemplateNotFoundError (t * testing.T ) {
533+ mockStore := & MockStore {}
534+ theme := NewTheme ("test" , mockStore )
535+
536+ ctx := context .Background ()
537+
538+ mockStore .On ("Find" , ctx , "test" , "missing" ).Return (nil , ErrTemplateNotFound ).Once ()
539+
540+ result , err := theme .Render (ctx , "missing" , nil )
541+
542+ assert .Error (t , err )
543+ assert .Nil (t , result , "Render should return nil on error" )
544+ assert .Contains (t , err .Error (), "failed to find template test/missing" )
545+
546+ mockStore .AssertExpectations (t )
547+ }
548+
549+ func TestTheme_Render_WithParseError (t * testing.T ) {
550+ mockStore := & MockStore {}
551+ theme := NewTheme ("test" , mockStore )
552+
553+ ctx := context .Background ()
554+
555+ // Create a template with invalid syntax
556+ invalidContent := `<h1>{{.Title</h1>`
557+ invalidTemplate := createTestTemplate ("test" , "invalid" , invalidContent )
558+
559+ mockStore .On ("Find" , ctx , "test" , "invalid" ).Return (invalidTemplate , nil ).Once ()
560+
561+ result , err := theme .Render (ctx , "invalid" , map [string ]string {"Title" : "Test" })
562+
563+ assert .Error (t , err )
564+ assert .Nil (t , result , "Render should return nil on parse error" )
565+ assert .Contains (t , err .Error (), "template:" )
566+
567+ mockStore .AssertExpectations (t )
568+ }
569+
570+ func TestTheme_Render_WithEmptyContent (t * testing.T ) {
571+ mockStore := & MockStore {}
572+ theme := NewTheme ("test" , mockStore )
573+
574+ ctx := context .Background ()
575+
576+ // Create a template with empty content
577+ emptyTemplate := createTestTemplate ("test" , "empty" , "" )
578+
579+ mockStore .On ("Find" , ctx , "test" , "empty" ).Return (emptyTemplate , nil ).Once ()
580+
581+ result , err := theme .Render (ctx , "empty" , nil )
582+
583+ assert .NoError (t , err )
584+ assert .Equal (t , []byte (nil ), result , "Render should return nil byte slice for empty content" )
585+
586+ mockStore .AssertExpectations (t )
587+ }
588+
589+ func TestTheme_Render_WithComplexData (t * testing.T ) {
590+ mockStore := & MockStore {}
591+ theme := NewTheme ("test" , mockStore )
592+
593+ ctx := context .Background ()
594+
595+ // Create a template with complex data
596+ complexContent := `{{range .Items}}{{$index := .Index}}{{$value := .Value}}Item {{$index}}: {{$value}} {{end}}`
597+ complexTemplate := createTestTemplate ("test" , "complex" , complexContent )
598+
599+ mockStore .On ("Find" , ctx , "test" , "complex" ).Return (complexTemplate , nil ).Once ()
600+
601+ type Item struct {
602+ Index int
603+ Value string
604+ }
605+
606+ data := map [string ][]Item {
607+ "Items" : {
608+ {Index : 1 , Value : "First" },
609+ {Index : 2 , Value : "Second" },
610+ {Index : 3 , Value : "Third" },
611+ },
612+ }
613+
614+ result , err := theme .Render (ctx , "complex" , data )
615+
616+ assert .NoError (t , err )
617+ assert .Contains (t , string (result ), "Item 1: First" )
618+ assert .Contains (t , string (result ), "Item 2: Second" )
619+ assert .Contains (t , string (result ), "Item 3: Third" )
620+
621+ mockStore .AssertExpectations (t )
622+ }
623+
624+ func TestTheme_Render_WithDebugMode (t * testing.T ) {
625+ mockStore := & MockStore {}
626+ theme := NewTheme ("test" , mockStore )
627+ theme .SetDebug (true )
628+
629+ ctx := context .Background ()
630+
631+ // Create a simple template
632+ templateContent := `<h1>{{.Title}}</h1>`
633+ testTemplate := createTestTemplate ("test" , "simple" , templateContent )
634+
635+ mockStore .On ("Find" , ctx , "test" , "simple" ).Return (testTemplate , nil ).Once ()
636+
637+ data := map [string ]string {"Title" : "Hello World" }
638+ result , err := theme .Render (ctx , "simple" , data )
639+
640+ assert .NoError (t , err )
641+ assert .Equal (t , "<h1>Hello World</h1>" , string (result ))
642+
643+ mockStore .AssertExpectations (t )
644+ }
645+
646+ func TestTheme_Render_WithCache (t * testing.T ) {
647+ mockStore := & MockStore {}
648+ theme := NewTheme ("test" , mockStore )
649+
650+ ctx := context .Background ()
651+
652+ // Create a simple template
653+ templateContent := `<h1>{{.Title}}</h1>`
654+ testTemplate := createTestTemplate ("test" , "simple" , templateContent )
655+
656+ // The template should only be fetched once (first call caches it)
657+ mockStore .On ("Find" , ctx , "test" , "simple" ).Return (testTemplate , nil ).Once ()
658+
659+ data := map [string ]string {"Title" : "Hello World" }
660+
661+ // First call - should fetch from store
662+ result1 , err := theme .Render (ctx , "simple" , data )
663+ assert .NoError (t , err )
664+ assert .Equal (t , "<h1>Hello World</h1>" , string (result1 ))
665+
666+ // Second call - should use cache (no additional store.Find call)
667+ result2 , err := theme .Render (ctx , "simple" , data )
668+ assert .NoError (t , err )
669+ assert .Equal (t , "<h1>Hello World</h1>" , string (result2 ))
670+
671+ // Both results should be the same
672+ assert .Equal (t , result1 , result2 )
673+
674+ mockStore .AssertExpectations (t )
675+ }
676+
677+ func TestTheme_Render_WithParentTheme (t * testing.T ) {
678+ parentStore := & MockStore {}
679+ childStore := & MockStore {}
680+
681+ parentTheme := NewTheme ("parent" , parentStore )
682+ childTheme := NewTheme ("child" , childStore )
683+ childTheme .SetParent (parentTheme )
684+
685+ ctx := context .Background ()
686+
687+ // Template exists in parent theme
688+ templateContent := `<h1>{{.Title}}</h1>`
689+ parentTemplate := createTestTemplate ("parent" , "inherited" , templateContent )
690+
691+ // Child theme doesn't have this template
692+ childStore .On ("Find" , ctx , "child" , "inherited" ).Return (nil , ErrTemplateNotFound ).Once ()
693+ parentStore .On ("Find" , ctx , "parent" , "inherited" ).Return (parentTemplate , nil ).Once ()
694+
695+ result , err := childTheme .Render (ctx , "inherited" , map [string ]string {"Title" : "Inherited Template" })
696+
697+ assert .NoError (t , err )
698+ assert .Equal (t , "<h1>Inherited Template</h1>" , string (result ))
699+
700+ childStore .AssertExpectations (t )
701+ parentStore .AssertExpectations (t )
702+ }
703+
704+ func TestTheme_Render_WithUnicodeContent (t * testing.T ) {
705+ mockStore := & MockStore {}
706+ theme := NewTheme ("test" , mockStore )
707+
708+ ctx := context .Background ()
709+
710+ // Create a template with unicode content
711+ templateContent := `<h1>{{.Title}}</h1><p>{{.Message}}</p>`
712+ testTemplate := createTestTemplate ("test" , "unicode" , templateContent )
713+
714+ mockStore .On ("Find" , ctx , "test" , "unicode" ).Return (testTemplate , nil ).Once ()
715+
716+ data := map [string ]string {
717+ "Title" : "🎉 Unicode Test" ,
718+ "Message" : "こんにちは世界 🌍" ,
719+ }
720+
721+ result , err := theme .Render (ctx , "unicode" , data )
722+
723+ assert .NoError (t , err )
724+ assert .Contains (t , string (result ), "🎉 Unicode Test" )
725+ assert .Contains (t , string (result ), "こんにちは世界 🌍" )
726+
727+ mockStore .AssertExpectations (t )
728+ }
729+
730+ func TestTheme_Render_WithSpecialCharacters (t * testing.T ) {
731+ mockStore := & MockStore {}
732+ theme := NewTheme ("test" , mockStore )
733+
734+ ctx := context .Background ()
735+
736+ // Create a template with special HTML characters
737+ templateContent := `<div>{{.Content}}</div>`
738+ testTemplate := createTestTemplate ("test" , "special" , templateContent )
739+
740+ mockStore .On ("Find" , ctx , "test" , "special" ).Return (testTemplate , nil ).Once ()
741+
742+ data := map [string ]string {"Content" : `<script>alert("XSS")</script>` }
743+
744+ result , err := theme .Render (ctx , "special" , data )
745+
746+ assert .NoError (t , err )
747+ // Go's html/template auto-escapes HTML by default
748+ assert .Contains (t , string (result ), "<script>" )
749+
750+ mockStore .AssertExpectations (t )
751+ }
752+
753+ func TestTheme_Render_ConcurrentAccess (t * testing.T ) {
754+ mockStore := & MockStore {}
755+ theme := NewTheme ("test" , mockStore )
756+
757+ ctx := context .Background ()
758+
759+ // Create a simple template
760+ templateContent := `<h1>{{.Title}}</h1>`
761+ testTemplate := createTestTemplate ("test" , "simple" , templateContent )
762+
763+ mockStore .On ("Find" , ctx , "test" , "simple" ).Return (testTemplate , nil ).Maybe ()
764+
765+ var wg sync.WaitGroup
766+ numGoroutines := 10
767+ numIterations := 5
768+
769+ // Test concurrent renders
770+ for i := 0 ; i < numGoroutines ; i ++ {
771+ wg .Add (1 )
772+ go func (id int ) {
773+ defer wg .Done ()
774+ for j := 0 ; j < numIterations ; j ++ {
775+ data := map [string ]string {"Title" : "Test" }
776+ result , err := theme .Render (ctx , "simple" , data )
777+ assert .NoError (t , err )
778+ assert .Equal (t , "<h1>Test</h1>" , string (result ))
779+ assert .IsType (t , []byte {}, result )
780+ }
781+ }(i )
782+ }
783+
784+ wg .Wait ()
785+ }
0 commit comments