Files
s3/weed/query/engine/comprehensive_sql_test.go
zooqueen dd74d3e6d6 Replace AGPL MinIO fork with Apache-2.0 SeaweedFS fork; rebrand CLI weed->s3
hanzoai/s3 was an AGPL-3.0 MinIO fork whose network-copyleft blocks embedding
and reselling as a managed service. Replace it wholesale with a fork of
SeaweedFS (github.com/seaweedfs/seaweedfs, Apache-2.0, Go) which ships a
built-in S3 gateway + Filer.

- License: AGPL-3.0 -> Apache-2.0 (upstream LICENSE kept verbatim, incl.
  "Copyright 2025 Chris Lu"); NOTICE added crediting SeaweedFS. No AGPL/BUSL/
  SSPL/GPL or enterprise ("ee") files remain.
- Module: go.mod path set to github.com/hanzoai/s3 with a self-replace
  (replace github.com/seaweedfs/seaweedfs => ./) so the ~1800 internal Go files
  keep their upstream import paths with zero churn. Internal identifiers and
  import paths are intentionally NOT renamed.
- CLI rebrand: the binary is built as `s3` (was `weed`); root usage, per-command
  help/usage/flag strings, shell autocomplete program name, and the product name
  in CLI output now read "Hanzo S3" / `s3`. Users never see `weed`.
  (e.g. `s3 server`, `s3 filer`, `s3 master`, `s3 volume`, `s3 s3`, `s3 mount`.)
- Build: `make` (or `go build -o s3 ./weed`) builds the `s3` binary green;
  `cmd/weed-db` and `cmd/weed-sql` also build; `go vet ./weed/command/` clean.
- README/LLM.md rewritten for Hanzo S3; AGENTS.md/CLAUDE.md symlinks preserved.
- Dropped a 33MB prebuilt Mach-O `stress` binary upstream had committed; `.gitignore`
  updated to ignore the `s3` build output.
2026-06-18 23:41:25 -07:00

350 lines
9.0 KiB
Go

package engine
import (
"context"
"strings"
"testing"
)
// TestComprehensiveSQLSuite tests all kinds of SQL patterns to ensure robustness
func TestComprehensiveSQLSuite(t *testing.T) {
engine := NewTestSQLEngine()
testCases := []struct {
name string
sql string
shouldPanic bool
shouldError bool
desc string
}{
// =========== BASIC QUERIES ===========
{
name: "Basic_Select_All",
sql: "SELECT * FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Basic select all columns",
},
{
name: "Basic_Select_Column",
sql: "SELECT id FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Basic select single column",
},
{
name: "Basic_Select_Multiple_Columns",
sql: "SELECT id, status FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Basic select multiple columns",
},
// =========== ARITHMETIC EXPRESSIONS (FIXED) ===========
{
name: "Arithmetic_Multiply_FIXED",
sql: "SELECT id*2 FROM user_events",
shouldPanic: false, // Fixed: no longer panics
shouldError: false,
desc: "FIXED: Arithmetic multiplication works",
},
{
name: "Arithmetic_Add",
sql: "SELECT id+10 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Arithmetic addition works",
},
{
name: "Arithmetic_Subtract",
sql: "SELECT id-5 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Arithmetic subtraction works",
},
{
name: "Arithmetic_Divide",
sql: "SELECT id/3 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Arithmetic division works",
},
{
name: "Arithmetic_Complex",
sql: "SELECT id*2+10 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Complex arithmetic expression works",
},
// =========== STRING OPERATIONS ===========
{
name: "String_Concatenation",
sql: "SELECT 'hello' || 'world' FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "String concatenation",
},
{
name: "String_Column_Concat",
sql: "SELECT status || '_suffix' FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Column string concatenation",
},
// =========== FUNCTIONS ===========
{
name: "Function_LENGTH",
sql: "SELECT LENGTH('hello') FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "LENGTH function with literal",
},
{
name: "Function_LENGTH_Column",
sql: "SELECT LENGTH(status) FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "LENGTH function with column",
},
{
name: "Function_UPPER",
sql: "SELECT UPPER('hello') FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "UPPER function",
},
{
name: "Function_Nested",
sql: "SELECT LENGTH(UPPER('hello')) FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Nested functions",
},
// =========== FUNCTIONS WITH ARITHMETIC ===========
{
name: "Function_Arithmetic",
sql: "SELECT LENGTH('hello') + 10 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Function with arithmetic",
},
{
name: "Function_Arithmetic_Complex",
sql: "SELECT LENGTH(status) * 2 + 5 FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Function with complex arithmetic",
},
// =========== TABLE REFERENCES ===========
{
name: "Table_Simple",
sql: "SELECT * FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Simple table reference",
},
{
name: "Table_With_Database",
sql: "SELECT * FROM ecommerce.user_events",
shouldPanic: false,
shouldError: false,
desc: "Table with database qualifier",
},
{
name: "Table_Quoted",
sql: `SELECT * FROM "user_events"`,
shouldPanic: false,
shouldError: false,
desc: "Quoted table name",
},
// =========== WHERE CLAUSES ===========
{
name: "Where_Simple",
sql: "SELECT * FROM user_events WHERE id = 1",
shouldPanic: false,
shouldError: false,
desc: "Simple WHERE clause",
},
{
name: "Where_String",
sql: "SELECT * FROM user_events WHERE status = 'active'",
shouldPanic: false,
shouldError: false,
desc: "WHERE clause with string",
},
// =========== LIMIT/OFFSET ===========
{
name: "Limit_Only",
sql: "SELECT * FROM user_events LIMIT 10",
shouldPanic: false,
shouldError: false,
desc: "LIMIT clause only",
},
{
name: "Limit_Offset",
sql: "SELECT * FROM user_events LIMIT 10 OFFSET 5",
shouldPanic: false,
shouldError: false,
desc: "LIMIT with OFFSET",
},
// =========== DATETIME FUNCTIONS ===========
{
name: "DateTime_CURRENT_DATE",
sql: "SELECT CURRENT_DATE FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "CURRENT_DATE function",
},
{
name: "DateTime_NOW",
sql: "SELECT NOW() FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "NOW() function",
},
{
name: "DateTime_EXTRACT",
sql: "SELECT EXTRACT(YEAR FROM CURRENT_DATE) FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "EXTRACT function",
},
// =========== EDGE CASES ===========
{
name: "Empty_String",
sql: "SELECT '' FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Empty string literal",
},
{
name: "Multiple_Spaces",
sql: "SELECT id FROM user_events",
shouldPanic: false,
shouldError: false,
desc: "Query with multiple spaces",
},
{
name: "Mixed_Case",
sql: "Select ID from User_Events",
shouldPanic: false,
shouldError: false,
desc: "Mixed case SQL",
},
// =========== SHOW STATEMENTS ===========
{
name: "Show_Databases",
sql: "SHOW DATABASES",
shouldPanic: false,
shouldError: false,
desc: "SHOW DATABASES statement",
},
{
name: "Show_Tables",
sql: "SHOW TABLES",
shouldPanic: false,
shouldError: false,
desc: "SHOW TABLES statement",
},
}
var panicTests []string
var errorTests []string
var successTests []string
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Capture panics
var panicValue interface{}
func() {
defer func() {
if r := recover(); r != nil {
panicValue = r
}
}()
result, err := engine.ExecuteSQL(context.Background(), tc.sql)
if tc.shouldPanic {
if panicValue == nil {
t.Errorf("FAIL: Expected panic for %s, but query completed normally", tc.desc)
panicTests = append(panicTests, "FAIL: "+tc.desc)
return
} else {
t.Logf("PASS: EXPECTED PANIC: %s - %v", tc.desc, panicValue)
panicTests = append(panicTests, "PASS: "+tc.desc+" (reproduced)")
return
}
}
if panicValue != nil {
t.Errorf("FAIL: Unexpected panic for %s: %v", tc.desc, panicValue)
panicTests = append(panicTests, "FAIL: "+tc.desc+" (unexpected panic)")
return
}
if tc.shouldError {
if err == nil && (result == nil || result.Error == nil) {
t.Errorf("FAIL: Expected error for %s, but query succeeded", tc.desc)
errorTests = append(errorTests, "FAIL: "+tc.desc)
return
} else {
t.Logf("PASS: Expected error: %s", tc.desc)
errorTests = append(errorTests, "PASS: "+tc.desc)
return
}
}
if err != nil {
t.Errorf("FAIL: Unexpected error for %s: %v", tc.desc, err)
errorTests = append(errorTests, "FAIL: "+tc.desc+" (unexpected error)")
return
}
if result != nil && result.Error != nil {
t.Errorf("FAIL: Unexpected result error for %s: %v", tc.desc, result.Error)
errorTests = append(errorTests, "FAIL: "+tc.desc+" (unexpected result error)")
return
}
t.Logf("PASS: Success: %s", tc.desc)
successTests = append(successTests, "PASS: "+tc.desc)
}()
})
}
// Summary report
separator := strings.Repeat("=", 80)
t.Log("\n" + separator)
t.Log("COMPREHENSIVE SQL TEST SUITE SUMMARY")
t.Log(separator)
t.Logf("Total Tests: %d", len(testCases))
t.Logf("Successful: %d", len(successTests))
t.Logf("Panics: %d", len(panicTests))
t.Logf("Errors: %d", len(errorTests))
t.Log(separator)
if len(panicTests) > 0 {
t.Log("\nPANICS TO FIX:")
for _, test := range panicTests {
t.Log(" " + test)
}
}
if len(errorTests) > 0 {
t.Log("\nERRORS TO INVESTIGATE:")
for _, test := range errorTests {
t.Log(" " + test)
}
}
}