package engine import ( "testing" "github.com/seaweedfs/seaweedfs/weed/pb/schema_pb" ) func TestStringFunctions(t *testing.T) { engine := NewTestSQLEngine() t.Run("LENGTH function tests", func(t *testing.T) { tests := []struct { name string value *schema_pb.Value expected int64 expectErr bool }{ { name: "Length of string", value: &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}}, expected: 11, expectErr: false, }, { name: "Length of empty string", value: &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: ""}}, expected: 0, expectErr: false, }, { name: "Length of number", value: &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 12345}}, expected: 5, expectErr: false, }, { name: "Length of null value", value: nil, expected: 0, expectErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := engine.Length(tt.value) if tt.expectErr { if err == nil { t.Errorf("Expected error but got none") } return } if err != nil { t.Errorf("Unexpected error: %v", err) return } intVal, ok := result.Kind.(*schema_pb.Value_Int64Value) if !ok { t.Errorf("LENGTH should return int64 value, got %T", result.Kind) return } if intVal.Int64Value != tt.expected { t.Errorf("Expected %d, got %d", tt.expected, intVal.Int64Value) } }) } }) t.Run("UPPER/LOWER function tests", func(t *testing.T) { // Test UPPER result, err := engine.Upper(&schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}}) if err != nil { t.Errorf("UPPER failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "HELLO WORLD" { t.Errorf("Expected 'HELLO WORLD', got '%s'", stringVal.StringValue) } // Test LOWER result, err = engine.Lower(&schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}}) if err != nil { t.Errorf("LOWER failed: %v", err) } stringVal, _ = result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "hello world" { t.Errorf("Expected 'hello world', got '%s'", stringVal.StringValue) } }) t.Run("TRIM function tests", func(t *testing.T) { tests := []struct { name string function func(*schema_pb.Value) (*schema_pb.Value, error) input string expected string }{ {"TRIM whitespace", engine.Trim, " Hello World ", "Hello World"}, {"LTRIM whitespace", engine.LTrim, " Hello World ", "Hello World "}, {"RTRIM whitespace", engine.RTrim, " Hello World ", " Hello World"}, {"TRIM with tabs and newlines", engine.Trim, "\t\nHello\t\n", "Hello"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := tt.function(&schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: tt.input}}) if err != nil { t.Errorf("Function failed: %v", err) return } stringVal, ok := result.Kind.(*schema_pb.Value_StringValue) if !ok { t.Errorf("Function should return string value, got %T", result.Kind) return } if stringVal.StringValue != tt.expected { t.Errorf("Expected '%s', got '%s'", tt.expected, stringVal.StringValue) } }) } }) t.Run("SUBSTRING function tests", func(t *testing.T) { testStr := &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}} // Test substring with start and length result, err := engine.Substring(testStr, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 7}}, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 5}}) if err != nil { t.Errorf("SUBSTRING failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "World" { t.Errorf("Expected 'World', got '%s'", stringVal.StringValue) } // Test substring with just start position result, err = engine.Substring(testStr, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 7}}) if err != nil { t.Errorf("SUBSTRING failed: %v", err) } stringVal, _ = result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "World" { t.Errorf("Expected 'World', got '%s'", stringVal.StringValue) } }) t.Run("CONCAT function tests", func(t *testing.T) { result, err := engine.Concat( &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello"}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: " "}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "World"}}, ) if err != nil { t.Errorf("CONCAT failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "Hello World" { t.Errorf("Expected 'Hello World', got '%s'", stringVal.StringValue) } // Test with mixed types result, err = engine.Concat( &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Number: "}}, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 42}}, ) if err != nil { t.Errorf("CONCAT failed: %v", err) } stringVal, _ = result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "Number: 42" { t.Errorf("Expected 'Number: 42', got '%s'", stringVal.StringValue) } }) t.Run("REPLACE function tests", func(t *testing.T) { result, err := engine.Replace( &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World World"}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "World"}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Universe"}}, ) if err != nil { t.Errorf("REPLACE failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "Hello Universe Universe" { t.Errorf("Expected 'Hello Universe Universe', got '%s'", stringVal.StringValue) } }) t.Run("POSITION function tests", func(t *testing.T) { result, err := engine.Position( &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "World"}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}}, ) if err != nil { t.Errorf("POSITION failed: %v", err) } intVal, _ := result.Kind.(*schema_pb.Value_Int64Value) if intVal.Int64Value != 7 { t.Errorf("Expected 7, got %d", intVal.Int64Value) } // Test not found result, err = engine.Position( &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "NotFound"}}, &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}}, ) if err != nil { t.Errorf("POSITION failed: %v", err) } intVal, _ = result.Kind.(*schema_pb.Value_Int64Value) if intVal.Int64Value != 0 { t.Errorf("Expected 0 for not found, got %d", intVal.Int64Value) } }) t.Run("LEFT/RIGHT function tests", func(t *testing.T) { testStr := &schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello World"}} // Test LEFT result, err := engine.Left(testStr, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 5}}) if err != nil { t.Errorf("LEFT failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "Hello" { t.Errorf("Expected 'Hello', got '%s'", stringVal.StringValue) } // Test RIGHT result, err = engine.Right(testStr, &schema_pb.Value{Kind: &schema_pb.Value_Int64Value{Int64Value: 5}}) if err != nil { t.Errorf("RIGHT failed: %v", err) } stringVal, _ = result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "World" { t.Errorf("Expected 'World', got '%s'", stringVal.StringValue) } }) t.Run("REVERSE function tests", func(t *testing.T) { result, err := engine.Reverse(&schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "Hello"}}) if err != nil { t.Errorf("REVERSE failed: %v", err) } stringVal, _ := result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "olleH" { t.Errorf("Expected 'olleH', got '%s'", stringVal.StringValue) } // Test with Unicode result, err = engine.Reverse(&schema_pb.Value{Kind: &schema_pb.Value_StringValue{StringValue: "🙂👍"}}) if err != nil { t.Errorf("REVERSE failed: %v", err) } stringVal, _ = result.Kind.(*schema_pb.Value_StringValue) if stringVal.StringValue != "👍🙂" { t.Errorf("Expected '👍🙂', got '%s'", stringVal.StringValue) } }) }