+2
go.mod
+2
go.mod
+2
go.sum
+2
go.sum
+558
query_test.go
+558
query_test.go
···
1
package norm
2
3
import (
4
+
"database/sql"
5
"testing"
6
+
"time"
7
+
8
+
_ "github.com/mattn/go-sqlite3"
9
)
10
11
func TestIdentExpr(t *testing.T) {
···
383
}
384
}
385
}
386
+
387
+
// setupTestDB creates an in-memory SQLite database with test data
388
+
func setupTestDB(t *testing.T) *sql.DB {
389
+
db, err := sql.Open("sqlite3", ":memory:")
390
+
if err != nil {
391
+
t.Fatalf("Failed to open database: %v", err)
392
+
}
393
+
394
+
// Create users table
395
+
_, err = db.Exec(`
396
+
CREATE TABLE users (
397
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
398
+
name TEXT NOT NULL,
399
+
age INTEGER NOT NULL,
400
+
email TEXT UNIQUE NOT NULL,
401
+
department TEXT,
402
+
active BOOLEAN DEFAULT TRUE,
403
+
salary REAL,
404
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
405
+
)
406
+
`)
407
+
if err != nil {
408
+
t.Fatalf("Failed to create users table: %v", err)
409
+
}
410
+
411
+
// Create departments table
412
+
_, err = db.Exec(`
413
+
CREATE TABLE departments (
414
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
415
+
name TEXT NOT NULL,
416
+
budget REAL
417
+
)
418
+
`)
419
+
if err != nil {
420
+
t.Fatalf("Failed to create departments table: %v", err)
421
+
}
422
+
423
+
// Insert test data
424
+
users := []struct {
425
+
name, email, department string
426
+
age int
427
+
active bool
428
+
salary float64
429
+
}{
430
+
{"John Doe", "john@example.com", "Engineering", 30, true, 75000.0},
431
+
{"Jane Smith", "jane@example.com", "Marketing", 25, true, 65000.0},
432
+
{"Bob Johnson", "bob@example.com", "Engineering", 35, false, 80000.0},
433
+
{"Alice Brown", "alice@example.com", "Sales", 28, true, 70000.0},
434
+
{"Charlie Wilson", "charlie@example.com", "Engineering", 32, true, 85000.0},
435
+
{"Diana Prince", "diana@example.com", "Marketing", 29, false, 68000.0},
436
+
}
437
+
438
+
for _, user := range users {
439
+
_, err = db.Exec(`
440
+
INSERT INTO users (name, email, department, age, active, salary)
441
+
VALUES (?, ?, ?, ?, ?, ?)
442
+
`, user.name, user.email, user.department, user.age, user.active, user.salary)
443
+
if err != nil {
444
+
t.Fatalf("Failed to insert user %s: %v", user.name, err)
445
+
}
446
+
}
447
+
448
+
// Insert departments
449
+
departments := []struct {
450
+
name string
451
+
budget float64
452
+
}{
453
+
{"Engineering", 500000.0},
454
+
{"Marketing", 300000.0},
455
+
{"Sales", 400000.0},
456
+
}
457
+
458
+
for _, dept := range departments {
459
+
_, err = db.Exec(`
460
+
INSERT INTO departments (name, budget)
461
+
VALUES (?, ?)
462
+
`, dept.name, dept.budget)
463
+
if err != nil {
464
+
t.Fatalf("Failed to insert department %s: %v", dept.name, err)
465
+
}
466
+
}
467
+
468
+
return db
469
+
}
470
+
471
+
func TestSelectIntegration_BasicQueries(t *testing.T) {
472
+
db := setupTestDB(t)
473
+
defer db.Close()
474
+
475
+
tests := []struct {
476
+
name string
477
+
builder func() select_
478
+
expectedRows int
479
+
}{
480
+
{
481
+
name: "Select all users",
482
+
builder: func() select_ {
483
+
return Select("*").From("users")
484
+
},
485
+
expectedRows: 6,
486
+
},
487
+
{
488
+
name: "Select active users only",
489
+
builder: func() select_ {
490
+
return Select("name", "email").
491
+
From("users").
492
+
Where(Eq("active", true))
493
+
},
494
+
expectedRows: 4,
495
+
},
496
+
{
497
+
name: "Select users in Engineering",
498
+
builder: func() select_ {
499
+
return Select("name", "age").
500
+
From("users").
501
+
Where(Eq("department", "Engineering"))
502
+
},
503
+
expectedRows: 3,
504
+
},
505
+
{
506
+
name: "Select users with age > 30",
507
+
builder: func() select_ {
508
+
return Select("name", "age").
509
+
From("users").
510
+
Where(Gt("age", 30))
511
+
},
512
+
expectedRows: 2,
513
+
},
514
+
{
515
+
name: "Select users with salary between 70000 and 80000",
516
+
builder: func() select_ {
517
+
return Select("name", "salary").
518
+
From("users").
519
+
Where(Gte("salary", 70000.0).And(Lte("salary", 80000.0)))
520
+
},
521
+
expectedRows: 3,
522
+
},
523
+
}
524
+
525
+
for _, test := range tests {
526
+
t.Run(test.name, func(t *testing.T) {
527
+
s := test.builder()
528
+
sql, args, err := s.Build()
529
+
if err != nil {
530
+
t.Fatalf("Failed to build query: %v", err)
531
+
}
532
+
533
+
rows, err := db.Query(sql, args...)
534
+
if err != nil {
535
+
t.Fatalf("Failed to execute query: %v", err)
536
+
}
537
+
defer rows.Close()
538
+
539
+
count := 0
540
+
for rows.Next() {
541
+
count++
542
+
}
543
+
544
+
if count != test.expectedRows {
545
+
t.Errorf("Expected %d rows, got %d", test.expectedRows, count)
546
+
}
547
+
})
548
+
}
549
+
}
550
+
551
+
func TestSelectIntegration_ComplexQueries(t *testing.T) {
552
+
db := setupTestDB(t)
553
+
defer db.Close()
554
+
555
+
t.Run("Complex WHERE with AND/OR", func(t *testing.T) {
556
+
s := Select("name", "department", "age").
557
+
From("users").
558
+
Where(
559
+
Eq("department", "Engineering").
560
+
And(Gt("age", 30)).
561
+
Or(Eq("department", "Marketing").And(Lt("age", 30))),
562
+
)
563
+
564
+
sql, args, err := s.Build()
565
+
if err != nil {
566
+
t.Fatalf("Failed to build query: %v", err)
567
+
}
568
+
569
+
rows, err := db.Query(sql, args...)
570
+
if err != nil {
571
+
t.Fatalf("Failed to execute query: %v", err)
572
+
}
573
+
defer rows.Close()
574
+
575
+
var results []struct {
576
+
name, department string
577
+
age int
578
+
}
579
+
580
+
for rows.Next() {
581
+
var name, department string
582
+
var age int
583
+
if err := rows.Scan(&name, &department, &age); err != nil {
584
+
t.Fatalf("Failed to scan row: %v", err)
585
+
}
586
+
results = append(results, struct {
587
+
name, department string
588
+
age int
589
+
}{name, department, age})
590
+
}
591
+
592
+
// Should get:
593
+
// - Bob Johnson (Engineering, 35)
594
+
// - Charlie Wilson (Engineering, 32)
595
+
// - Jane Smith (Marketing, 25)
596
+
// - Diana Prince (Marketing, 29)
597
+
if len(results) != 4 {
598
+
t.Errorf("Expected 4 results, got %d", len(results))
599
+
}
600
+
})
601
+
602
+
t.Run("ORDER BY multiple columns", func(t *testing.T) {
603
+
s := Select("name", "department", "age").
604
+
From("users").
605
+
OrderBy("department", Ascending).
606
+
OrderBy("age", Descending)
607
+
608
+
sql, args, err := s.Build()
609
+
if err != nil {
610
+
t.Fatalf("Failed to build query: %v", err)
611
+
}
612
+
613
+
rows, err := db.Query(sql, args...)
614
+
if err != nil {
615
+
t.Fatalf("Failed to execute query: %v", err)
616
+
}
617
+
defer rows.Close()
618
+
619
+
var results []struct {
620
+
name, department string
621
+
age int
622
+
}
623
+
624
+
for rows.Next() {
625
+
var name, department string
626
+
var age int
627
+
if err := rows.Scan(&name, &department, &age); err != nil {
628
+
t.Fatalf("Failed to scan row: %v", err)
629
+
}
630
+
results = append(results, struct {
631
+
name, department string
632
+
age int
633
+
}{name, department, age})
634
+
}
635
+
636
+
if len(results) != 6 {
637
+
t.Fatalf("Expected 6 results, got %d", len(results))
638
+
}
639
+
640
+
// First result should be from Engineering (alphabetically first) with highest age
641
+
if results[0].department != "Engineering" {
642
+
t.Errorf("Expected first result to be from Engineering, got %s", results[0].department)
643
+
}
644
+
if results[0].age != 35 {
645
+
t.Errorf("Expected first result age to be 35, got %d", results[0].age)
646
+
}
647
+
})
648
+
649
+
t.Run("GROUP BY with COUNT", func(t *testing.T) {
650
+
s := Select("department", "COUNT(*) as user_count").
651
+
From("users").
652
+
GroupBy("department").
653
+
OrderBy("user_count", Descending)
654
+
655
+
sql, args, err := s.Build()
656
+
if err != nil {
657
+
t.Fatalf("Failed to build query: %v", err)
658
+
}
659
+
660
+
rows, err := db.Query(sql, args...)
661
+
if err != nil {
662
+
t.Fatalf("Failed to execute query: %v", err)
663
+
}
664
+
defer rows.Close()
665
+
666
+
var results []struct {
667
+
department string
668
+
count int
669
+
}
670
+
671
+
for rows.Next() {
672
+
var department string
673
+
var count int
674
+
if err := rows.Scan(&department, &count); err != nil {
675
+
t.Fatalf("Failed to scan row: %v", err)
676
+
}
677
+
results = append(results, struct {
678
+
department string
679
+
count int
680
+
}{department, count})
681
+
}
682
+
683
+
if len(results) != 3 {
684
+
t.Errorf("Expected 3 departments, got %d", len(results))
685
+
}
686
+
687
+
// Engineering should have the most users (3)
688
+
if results[0].department != "Engineering" || results[0].count != 3 {
689
+
t.Errorf("Expected Engineering with 3 users first, got %s with %d users", results[0].department, results[0].count)
690
+
}
691
+
})
692
+
693
+
t.Run("LIMIT with ORDER BY", func(t *testing.T) {
694
+
s := Select("name", "salary").
695
+
From("users").
696
+
Where(Eq("active", true)).
697
+
OrderBy("salary", Descending).
698
+
Limit(2)
699
+
700
+
sql, args, err := s.Build()
701
+
if err != nil {
702
+
t.Fatalf("Failed to build query: %v", err)
703
+
}
704
+
705
+
rows, err := db.Query(sql, args...)
706
+
if err != nil {
707
+
t.Fatalf("Failed to execute query: %v", err)
708
+
}
709
+
defer rows.Close()
710
+
711
+
var results []struct {
712
+
name string
713
+
salary float64
714
+
}
715
+
716
+
for rows.Next() {
717
+
var name string
718
+
var salary float64
719
+
if err := rows.Scan(&name, &salary); err != nil {
720
+
t.Fatalf("Failed to scan row: %v", err)
721
+
}
722
+
results = append(results, struct {
723
+
name string
724
+
salary float64
725
+
}{name, salary})
726
+
}
727
+
728
+
if len(results) != 2 {
729
+
t.Errorf("Expected 2 results, got %d", len(results))
730
+
}
731
+
732
+
// Should get Charlie Wilson (85000) and John Doe (75000)
733
+
if results[0].salary != 85000.0 {
734
+
t.Errorf("Expected highest salary to be 85000, got %f", results[0].salary)
735
+
}
736
+
if results[1].salary != 75000.0 {
737
+
t.Errorf("Expected second highest salary to be 75000, got %f", results[1].salary)
738
+
}
739
+
})
740
+
}
741
+
742
+
func TestSelectIntegration_DataTypes(t *testing.T) {
743
+
db := setupTestDB(t)
744
+
defer db.Close()
745
+
746
+
t.Run("String comparisons", func(t *testing.T) {
747
+
s := Select("name", "email").
748
+
From("users").
749
+
Where(Eq("name", "John Doe"))
750
+
751
+
sql, args, err := s.Build()
752
+
if err != nil {
753
+
t.Fatalf("Failed to build query: %v", err)
754
+
}
755
+
756
+
rows, err := db.Query(sql, args...)
757
+
if err != nil {
758
+
t.Fatalf("Failed to execute query: %v", err)
759
+
}
760
+
defer rows.Close()
761
+
762
+
count := 0
763
+
for rows.Next() {
764
+
count++
765
+
}
766
+
767
+
if count != 1 {
768
+
t.Errorf("Expected 1 result, got %d", count)
769
+
}
770
+
})
771
+
772
+
t.Run("Boolean comparisons", func(t *testing.T) {
773
+
s := Select("name").
774
+
From("users").
775
+
Where(Eq("active", false))
776
+
777
+
sql, args, err := s.Build()
778
+
if err != nil {
779
+
t.Fatalf("Failed to build query: %v", err)
780
+
}
781
+
782
+
rows, err := db.Query(sql, args...)
783
+
if err != nil {
784
+
t.Fatalf("Failed to execute query: %v", err)
785
+
}
786
+
defer rows.Close()
787
+
788
+
count := 0
789
+
for rows.Next() {
790
+
count++
791
+
}
792
+
793
+
if count != 2 {
794
+
t.Errorf("Expected 2 inactive users, got %d", count)
795
+
}
796
+
})
797
+
798
+
t.Run("Float comparisons", func(t *testing.T) {
799
+
s := Select("name", "salary").
800
+
From("users").
801
+
Where(Gte("salary", 80000.0))
802
+
803
+
sql, args, err := s.Build()
804
+
if err != nil {
805
+
t.Fatalf("Failed to build query: %v", err)
806
+
}
807
+
808
+
rows, err := db.Query(sql, args...)
809
+
if err != nil {
810
+
t.Fatalf("Failed to execute query: %v", err)
811
+
}
812
+
defer rows.Close()
813
+
814
+
count := 0
815
+
for rows.Next() {
816
+
count++
817
+
}
818
+
819
+
if count != 2 {
820
+
t.Errorf("Expected 2 users with salary >= 80000, got %d", count)
821
+
}
822
+
})
823
+
}
824
+
825
+
func TestSelectIntegration_ErrorHandling(t *testing.T) {
826
+
db := setupTestDB(t)
827
+
defer db.Close()
828
+
829
+
t.Run("Invalid table name", func(t *testing.T) {
830
+
s := Select("*").From("nonexistent_table")
831
+
832
+
sql, args, err := s.Build()
833
+
if err != nil {
834
+
t.Fatalf("Failed to build query: %v", err)
835
+
}
836
+
837
+
_, err = db.Query(sql, args...)
838
+
if err == nil {
839
+
t.Error("Expected error for nonexistent table, got nil")
840
+
}
841
+
})
842
+
843
+
t.Run("Invalid column name", func(t *testing.T) {
844
+
s := Select("nonexistent_column").From("users")
845
+
846
+
sql, args, err := s.Build()
847
+
if err != nil {
848
+
t.Fatalf("Failed to build query: %v", err)
849
+
}
850
+
851
+
_, err = db.Query(sql, args...)
852
+
if err == nil {
853
+
t.Error("Expected error for nonexistent column, got nil")
854
+
}
855
+
})
856
+
}
857
+
858
+
func TestSelectIntegration_PerformanceBaseline(t *testing.T) {
859
+
db := setupTestDB(t)
860
+
defer db.Close()
861
+
862
+
// Add more test data for performance testing
863
+
start := time.Now()
864
+
for i := 0; i < 1000; i++ {
865
+
_, err := db.Exec(`
866
+
INSERT INTO users (name, email, department, age, active, salary)
867
+
VALUES (?, ?, ?, ?, ?, ?)
868
+
`, "User"+string(rune(i)), "user"+string(rune(i))+"@example.com", "Engineering", 25+i%15, i%2 == 0, 50000.0+float64(i*100))
869
+
if err != nil {
870
+
t.Fatalf("Failed to insert test user: %v", err)
871
+
}
872
+
}
873
+
insertTime := time.Since(start)
874
+
t.Logf("Inserted 1000 users in %v", insertTime)
875
+
876
+
t.Run("Simple select performance", func(t *testing.T) {
877
+
s := Select("name", "email").
878
+
From("users").
879
+
Where(Eq("active", true))
880
+
881
+
sql, args, err := s.Build()
882
+
if err != nil {
883
+
t.Fatalf("Failed to build query: %v", err)
884
+
}
885
+
886
+
start := time.Now()
887
+
rows, err := db.Query(sql, args...)
888
+
if err != nil {
889
+
t.Fatalf("Failed to execute query: %v", err)
890
+
}
891
+
defer rows.Close()
892
+
893
+
count := 0
894
+
for rows.Next() {
895
+
count++
896
+
}
897
+
queryTime := time.Since(start)
898
+
899
+
t.Logf("Selected %d rows in %v", count, queryTime)
900
+
if queryTime > time.Millisecond*100 {
901
+
t.Logf("Query took longer than expected: %v", queryTime)
902
+
}
903
+
})
904
+
905
+
t.Run("Complex query performance", func(t *testing.T) {
906
+
s := Select("name", "department", "salary").
907
+
From("users").
908
+
Where(
909
+
Eq("active", true).
910
+
And(Gt("salary", 60000.0)).
911
+
Or(Eq("department", "Engineering")),
912
+
).
913
+
OrderBy("salary", Descending).
914
+
Limit(50)
915
+
916
+
sql, args, err := s.Build()
917
+
if err != nil {
918
+
t.Fatalf("Failed to build query: %v", err)
919
+
}
920
+
921
+
start := time.Now()
922
+
rows, err := db.Query(sql, args...)
923
+
if err != nil {
924
+
t.Fatalf("Failed to execute query: %v", err)
925
+
}
926
+
defer rows.Close()
927
+
928
+
count := 0
929
+
for rows.Next() {
930
+
count++
931
+
}
932
+
queryTime := time.Since(start)
933
+
934
+
t.Logf("Complex query returned %d rows in %v", count, queryTime)
935
+
if queryTime > time.Millisecond*200 {
936
+
t.Logf("Complex query took longer than expected: %v", queryTime)
937
+
}
938
+
})
939
+
}