mirror of
https://github.com/semihalev/twig.git
synced 2026-03-14 13:55:46 +01:00
- Updated matches operator to properly handle regex character classes - Fixed \d to [0-9] conversion for digit matching in regex - Added support for other common character classes: \w, \s - Fixed test cases to use compatible regex patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
199 lines
5.7 KiB
Go
199 lines
5.7 KiB
Go
package twig
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// TestAdvancedComparisonOperators tests advanced comparison operators
|
|
func TestAdvancedComparisonOperators(t *testing.T) {
|
|
engine := New()
|
|
|
|
tests := []struct {
|
|
name string
|
|
source string
|
|
context map[string]interface{}
|
|
expected string
|
|
}{
|
|
// Starts with operator
|
|
{
|
|
name: "Starts with operator - true case",
|
|
source: "{% if 'Hello world' starts with 'Hello' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Starts with operator - false case",
|
|
source: "{% if 'Hello world' starts with 'world' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "false",
|
|
},
|
|
{
|
|
name: "Starts with operator with variables",
|
|
source: "{% if text starts with prefix %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"text": "Hello world", "prefix": "Hello"},
|
|
expected: "true",
|
|
},
|
|
|
|
// Ends with operator
|
|
{
|
|
name: "Ends with operator - true case",
|
|
source: "{% if 'Hello world' ends with 'world' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Ends with operator - false case",
|
|
source: "{% if 'Hello world' ends with 'Hello' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "false",
|
|
},
|
|
{
|
|
name: "Ends with operator with variables",
|
|
source: "{% if text ends with suffix %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"text": "Hello world", "suffix": "world"},
|
|
expected: "true",
|
|
},
|
|
|
|
// Matches operator (regex)
|
|
{
|
|
name: "Matches operator - true case",
|
|
source: "{% if 'abc123' matches '/^[a-z]+[0-9]+$/' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Matches operator - false case",
|
|
source: "{% if '123abc' matches '/^[a-z]+[0-9]+$/' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "false",
|
|
},
|
|
{
|
|
name: "Matches operator with case insensitive flag",
|
|
source: "{% if 'HELLO' matches '/hello/i' %}true{% else %}false{% endif %}",
|
|
context: nil,
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Matches operator with variables",
|
|
source: "{% if text matches pattern %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"text": "abc123", "pattern": "/^[a-z]+[0-9]+$/"},
|
|
expected: "true",
|
|
},
|
|
|
|
// Combined operators
|
|
{
|
|
name: "Combined comparison operators (and)",
|
|
source: "{% if num > 5 and num < 15 %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"num": 10},
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Combined comparison operators (or)",
|
|
source: "{% if num < 5 or num > 15 %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"num": 20},
|
|
expected: "true",
|
|
},
|
|
{
|
|
name: "Mixed string and comparison operators",
|
|
source: "{% if text starts with 'H' and num > 5 %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"text": "Hello", "num": 10},
|
|
expected: "true",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := engine.RegisterString(tt.name, tt.source)
|
|
if err != nil {
|
|
t.Fatalf("Error registering template: %v", err)
|
|
}
|
|
|
|
result, err := engine.Render(tt.name, tt.context)
|
|
if err != nil {
|
|
t.Fatalf("Error rendering template: %v", err)
|
|
}
|
|
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %q, got %q", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestComplexConditionalExpressions tests more complex conditional expressions
|
|
func TestComplexConditionalExpressions(t *testing.T) {
|
|
engine := New()
|
|
|
|
tests := []struct {
|
|
name string
|
|
source string
|
|
context map[string]interface{}
|
|
expected string
|
|
}{
|
|
// Nested ternary operators
|
|
{
|
|
name: "Deeply nested ternary operators",
|
|
source: "{{ a ? (b ? 'a and b' : 'a not b') : (c ? 'not a but c' : 'neither a nor c') }}",
|
|
context: map[string]interface{}{"a": true, "b": false, "c": true},
|
|
expected: "a not b",
|
|
},
|
|
|
|
// Complex test expressions
|
|
{
|
|
name: "Complex test expressions with 'is' operator",
|
|
source: "{% if value is defined and value is not empty and value > 10 %}valid{% else %}invalid{% endif %}",
|
|
context: map[string]interface{}{"value": 15},
|
|
expected: "valid",
|
|
},
|
|
|
|
// Complex expressions with grouping
|
|
{
|
|
name: "Complex expressions with parentheses for grouping",
|
|
source: "{% if (a or b) and (c or d) %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"a": false, "b": true, "c": true, "d": false},
|
|
expected: "true",
|
|
},
|
|
|
|
// Complex negation
|
|
{
|
|
name: "Complex negation with 'not' operator",
|
|
source: "{% if not (a or b) %}true{% else %}false{% endif %}",
|
|
context: map[string]interface{}{"a": false, "b": false},
|
|
expected: "true",
|
|
},
|
|
|
|
// Attribute access in conditions
|
|
{
|
|
name: "Attribute access in conditions",
|
|
source: "{% if user.active and user.age >= 18 %}allowed{% else %}denied{% endif %}",
|
|
context: map[string]interface{}{"user": map[string]interface{}{"active": true, "age": 25}},
|
|
expected: "allowed",
|
|
},
|
|
|
|
// Nested attribute access in ternary
|
|
{
|
|
name: "Nested attribute access in ternary",
|
|
source: "{{ user.active ? user.name : 'Guest' }}",
|
|
context: map[string]interface{}{"user": map[string]interface{}{"active": true, "name": "John"}},
|
|
expected: "John",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := engine.RegisterString(tt.name, tt.source)
|
|
if err != nil {
|
|
t.Fatalf("Error registering template: %v", err)
|
|
}
|
|
|
|
result, err := engine.Render(tt.name, tt.context)
|
|
if err != nil {
|
|
t.Fatalf("Error rendering template: %v", err)
|
|
}
|
|
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %q, got %q", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|