重构代码
This commit is contained in:
@@ -0,0 +1,563 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"b612.me/stardb"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
type TestUser struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Email string `db:"email"`
|
||||
Age int `db:"age"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
func setupBatchTestDB(t *testing.T) *stardb.StarDB {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
age INTEGER,
|
||||
created_at DATETIME
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create table: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsert_Basic(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{
|
||||
{"Alice", "alice@example.com", 25},
|
||||
{"Bob", "bob@example.com", 30},
|
||||
{"Charlie", "charlie@example.com", 35},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert("users", columns, values)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsert failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 3 {
|
||||
t.Errorf("Expected 3 rows affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify insertion
|
||||
rows, err := db.Query("SELECT COUNT(*) as count FROM users")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
count := rows.Row(0).MustInt("count")
|
||||
if count != 3 {
|
||||
t.Errorf("Expected 3 rows in database, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsert_Single(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{
|
||||
{"Alice", "alice@example.com", 25},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert("users", columns, values)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsert failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsert_Empty(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{}
|
||||
|
||||
_, err := db.BatchInsert("users", columns, values)
|
||||
if err == nil {
|
||||
t.Error("Expected error with empty values, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsert_Large(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
var values [][]interface{}
|
||||
|
||||
// Insert 100 rows
|
||||
for i := 0; i < 100; i++ {
|
||||
values = append(values, []interface{}{
|
||||
"User" + string(rune(i)),
|
||||
"user" + string(rune(i)) + "@example.com",
|
||||
20 + i%50,
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert("users", columns, values)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsert failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 100 {
|
||||
t.Errorf("Expected 100 rows affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify
|
||||
rows, err := db.Query("SELECT COUNT(*) as count FROM users")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
count := rows.Row(0).MustInt("count")
|
||||
if count != 100 {
|
||||
t.Errorf("Expected 100 rows in database, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertContext(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{
|
||||
{"Alice", "alice@example.com", 25},
|
||||
{"Bob", "bob@example.com", 30},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertContext(ctx, "users", columns, values)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 2 {
|
||||
t.Errorf("Expected 2 rows affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertContext_Timeout(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
|
||||
defer cancel()
|
||||
|
||||
time.Sleep(10 * time.Millisecond) // Ensure timeout
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{
|
||||
{"Alice", "alice@example.com", 25},
|
||||
}
|
||||
|
||||
_, err := db.BatchInsertContext(ctx, "users", columns, values)
|
||||
if err == nil {
|
||||
t.Error("Expected timeout error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_Basic(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []TestUser{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 25, CreatedAt: time.Now()},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 30, CreatedAt: time.Now()},
|
||||
{Name: "Charlie", Email: "charlie@example.com", Age: 35, CreatedAt: time.Now()},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertStructs("users", users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructs failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 3 {
|
||||
t.Errorf("Expected 3 rows affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify insertion
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 3 {
|
||||
t.Errorf("Expected 3 rows, got %d", rows.Length())
|
||||
}
|
||||
|
||||
// Verify first user
|
||||
name := rows.Row(0).MustString("name")
|
||||
if name != "Alice" {
|
||||
t.Errorf("Expected first user 'Alice', got '%s'", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_Single(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []TestUser{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 25, CreatedAt: time.Now()},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertStructs("users", users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructs failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_Empty(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []TestUser{}
|
||||
|
||||
_, err := db.BatchInsertStructs("users", users, "id")
|
||||
if err == nil {
|
||||
t.Error("Expected error with empty slice, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_NotSlice(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
user := TestUser{Name: "Alice", Email: "alice@example.com", Age: 25}
|
||||
|
||||
_, err := db.BatchInsertStructs("users", user, "id")
|
||||
if err == nil {
|
||||
t.Error("Expected error with non-slice, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_Pointer(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []TestUser{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 25, CreatedAt: time.Now()},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 30, CreatedAt: time.Now()},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertStructs("users", &users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructs with pointer failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 2 {
|
||||
t.Errorf("Expected 2 rows affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructsContext(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
users := []TestUser{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 25, CreatedAt: time.Now()},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 30, CreatedAt: time.Now()},
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertStructsContext(ctx, "users", users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructsContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 2 {
|
||||
t.Errorf("Expected 2 rows affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_Large(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
var users []TestUser
|
||||
for i := 0; i < 50; i++ {
|
||||
users = append(users, TestUser{
|
||||
Name: "User" + string(rune(i)),
|
||||
Email: "user" + string(rune(i)) + "@example.com",
|
||||
Age: 20 + i%30,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.BatchInsertStructs("users", users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructs failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 50 {
|
||||
t.Errorf("Expected 50 rows affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify
|
||||
rows, err := db.Query("SELECT COUNT(*) as count FROM users")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
count := rows.Row(0).MustInt("count")
|
||||
if count != 50 {
|
||||
t.Errorf("Expected 50 rows in database, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsert_VerifyData(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
values := [][]interface{}{
|
||||
{"Alice", "alice@example.com", 25},
|
||||
{"Bob", "bob@example.com", 30},
|
||||
}
|
||||
|
||||
_, err := db.BatchInsert("users", columns, values)
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsert failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify data integrity
|
||||
rows, err := db.Query("SELECT name, email, age FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Check Alice
|
||||
row0 := rows.Row(0)
|
||||
if row0.MustString("name") != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", row0.MustString("name"))
|
||||
}
|
||||
if row0.MustString("email") != "alice@example.com" {
|
||||
t.Errorf("Expected email 'alice@example.com', got '%s'", row0.MustString("email"))
|
||||
}
|
||||
if row0.MustInt("age") != 25 {
|
||||
t.Errorf("Expected age 25, got %d", row0.MustInt("age"))
|
||||
}
|
||||
|
||||
// Check Bob
|
||||
row1 := rows.Row(1)
|
||||
if row1.MustString("name") != "Bob" {
|
||||
t.Errorf("Expected name 'Bob', got '%s'", row1.MustString("name"))
|
||||
}
|
||||
if row1.MustString("email") != "bob@example.com" {
|
||||
t.Errorf("Expected email 'bob@example.com', got '%s'", row1.MustString("email"))
|
||||
}
|
||||
if row1.MustInt("age") != 30 {
|
||||
t.Errorf("Expected age 30, got %d", row1.MustInt("age"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_BatchInsertStructs_VerifyData(t *testing.T) {
|
||||
db := setupBatchTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
now := time.Now()
|
||||
users := []TestUser{
|
||||
{Name: "Alice", Email: "alice@example.com", Age: 25, CreatedAt: now},
|
||||
{Name: "Bob", Email: "bob@example.com", Age: 30, CreatedAt: now},
|
||||
}
|
||||
|
||||
_, err := db.BatchInsertStructs("users", users, "id")
|
||||
if err != nil {
|
||||
t.Fatalf("BatchInsertStructs failed: %v", err)
|
||||
}
|
||||
|
||||
// Query and verify with ORM
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var resultUsers []TestUser
|
||||
err = rows.Orm(&resultUsers)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if len(resultUsers) != 2 {
|
||||
t.Fatalf("Expected 2 users, got %d", len(resultUsers))
|
||||
}
|
||||
|
||||
// Verify Alice
|
||||
if resultUsers[0].Name != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", resultUsers[0].Name)
|
||||
}
|
||||
if resultUsers[0].Email != "alice@example.com" {
|
||||
t.Errorf("Expected email 'alice@example.com', got '%s'", resultUsers[0].Email)
|
||||
}
|
||||
if resultUsers[0].Age != 25 {
|
||||
t.Errorf("Expected age 25, got %d", resultUsers[0].Age)
|
||||
}
|
||||
|
||||
// Verify Bob
|
||||
if resultUsers[1].Name != "Bob" {
|
||||
t.Errorf("Expected name 'Bob', got '%s'", resultUsers[1].Name)
|
||||
}
|
||||
if resultUsers[1].Email != "bob@example.com" {
|
||||
t.Errorf("Expected email 'bob@example.com', got '%s'", resultUsers[1].Email)
|
||||
}
|
||||
if resultUsers[1].Age != 30 {
|
||||
t.Errorf("Expected age 30, got %d", resultUsers[1].Age)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark tests
|
||||
func BenchmarkBatchInsert_10(b *testing.B) {
|
||||
db := setupBatchTestDB(&testing.T{})
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
var values [][]interface{}
|
||||
for i := 0; i < 10; i++ {
|
||||
values = append(values, []interface{}{"User", "user@example.com", 25})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
db.BatchInsert("users", columns, values)
|
||||
db.Exec("DELETE FROM users")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBatchInsert_100(b *testing.B) {
|
||||
db := setupBatchTestDB(&testing.T{})
|
||||
defer db.Close()
|
||||
|
||||
columns := []string{"name", "email", "age"}
|
||||
var values [][]interface{}
|
||||
for i := 0; i < 100; i++ {
|
||||
values = append(values, []interface{}{"User", "user@example.com", 25})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
db.BatchInsert("users", columns, values)
|
||||
db.Exec("DELETE FROM users")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBatchInsertStructs_10(b *testing.B) {
|
||||
db := setupBatchTestDB(&testing.T{})
|
||||
defer db.Close()
|
||||
|
||||
var users []TestUser
|
||||
for i := 0; i < 10; i++ {
|
||||
users = append(users, TestUser{
|
||||
Name: "User",
|
||||
Email: "user@example.com",
|
||||
Age: 25,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
db.BatchInsertStructs("users", users, "id")
|
||||
db.Exec("DELETE FROM users")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBatchInsertStructs_100(b *testing.B) {
|
||||
db := setupBatchTestDB(&testing.T{})
|
||||
defer db.Close()
|
||||
|
||||
var users []TestUser
|
||||
for i := 0; i < 100; i++ {
|
||||
users = append(users, TestUser{
|
||||
Name: "User",
|
||||
Email: "user@example.com",
|
||||
Age: 25,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
db.BatchInsertStructs("users", users, "id")
|
||||
db.Exec("DELETE FROM users")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
module b612.me/stardb/testing
|
||||
|
||||
go 1.25.6
|
||||
|
||||
require (
|
||||
b612.me/stardb v0.0.0
|
||||
modernc.org/sqlite v1.46.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
modernc.org/libc v1.67.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
replace b612.me/stardb => ../
|
||||
@@ -0,0 +1,53 @@
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
|
||||
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
@@ -0,0 +1,691 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Email string `db:"email"`
|
||||
Age int `db:"age"`
|
||||
Balance float64 `db:"balance"`
|
||||
Active bool `db:"active"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
UserID int `db:"user_id"`
|
||||
Bio string `db:"bio"`
|
||||
Avatar string `db:"avatar"`
|
||||
}
|
||||
|
||||
type NestedUser struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Profile `db:"---"`
|
||||
}
|
||||
|
||||
func TestStarRows_Orm_Single(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var user User
|
||||
err = rows.Orm(&user)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if user.Name != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", user.Name)
|
||||
}
|
||||
|
||||
if user.Email != "alice@example.com" {
|
||||
t.Errorf("Expected email 'alice@example.com', got '%s'", user.Email)
|
||||
}
|
||||
|
||||
if user.Age != 25 {
|
||||
t.Errorf("Expected age 25, got %d", user.Age)
|
||||
}
|
||||
|
||||
if user.Balance != 100.50 {
|
||||
t.Errorf("Expected balance 100.50, got %f", user.Balance)
|
||||
}
|
||||
|
||||
if !user.Active {
|
||||
t.Errorf("Expected user to be active")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_Orm_Multiple(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []User
|
||||
err = rows.Orm(&users)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if len(users) != 3 {
|
||||
t.Fatalf("Expected 3 users, got %d", len(users))
|
||||
}
|
||||
|
||||
expectedNames := []string{"Alice", "Bob", "Charlie"}
|
||||
for i, user := range users {
|
||||
if user.Name != expectedNames[i] {
|
||||
t.Errorf("Expected name '%s', got '%s'", expectedNames[i], user.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_Orm_Empty(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "NonExistent")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []User
|
||||
err = rows.Orm(&users)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if len(users) != 0 {
|
||||
t.Errorf("Expected 0 users, got %d", len(users))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_Orm_NotPointer(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var user User
|
||||
err = rows.Orm(user) // Not a pointer
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when passing non-pointer, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Insert(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
user := User{
|
||||
Name: "David",
|
||||
Email: "david@example.com",
|
||||
Age: 40,
|
||||
Balance: 400.00,
|
||||
Active: true,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
result, err := db.Insert(&user, "users", "id")
|
||||
if err != nil {
|
||||
t.Fatalf("Insert failed: %v", err)
|
||||
}
|
||||
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatalf("LastInsertId failed: %v", err)
|
||||
}
|
||||
|
||||
if lastID <= 0 {
|
||||
t.Errorf("Expected positive last insert ID, got %d", lastID)
|
||||
}
|
||||
|
||||
// Verify insertion
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "David")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var insertedUser User
|
||||
err = rows.Orm(&insertedUser)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if insertedUser.Name != "David" {
|
||||
t.Errorf("Expected name 'David', got '%s'", insertedUser.Name)
|
||||
}
|
||||
|
||||
if insertedUser.Email != "david@example.com" {
|
||||
t.Errorf("Expected email 'david@example.com', got '%s'", insertedUser.Email)
|
||||
}
|
||||
|
||||
if insertedUser.Age != 40 {
|
||||
t.Errorf("Expected age 40, got %d", insertedUser.Age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_InsertContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
user := User{
|
||||
Name: "Eve",
|
||||
Email: "eve@example.com",
|
||||
Age: 28,
|
||||
Balance: 250.00,
|
||||
Active: true,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
result, err := db.InsertContext(ctx, &user, "users", "id")
|
||||
if err != nil {
|
||||
t.Fatalf("InsertContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Update(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
// First, get the user
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var user User
|
||||
err = rows.Orm(&user)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
// Update the user
|
||||
user.Age = 26
|
||||
user.Balance = 150.75
|
||||
|
||||
result, err := db.Update(&user, "users", "id")
|
||||
if err != nil {
|
||||
t.Fatalf("Update failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify the update
|
||||
rows2, err := db.Query("SELECT * FROM users WHERE id = ?", user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows2.Close()
|
||||
|
||||
var updatedUser User
|
||||
err = rows2.Orm(&updatedUser)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if updatedUser.Age != 26 {
|
||||
t.Errorf("Expected age 26, got %d", updatedUser.Age)
|
||||
}
|
||||
|
||||
if updatedUser.Balance != 150.75 {
|
||||
t.Errorf("Expected balance 150.75, got %f", updatedUser.Balance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_UpdateContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Get user
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Bob")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var user User
|
||||
err = rows.Orm(&user)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
// Update
|
||||
user.Active = false
|
||||
result, err := db.UpdateContext(ctx, &user, "users", "id")
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_QueryX(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
user := User{
|
||||
Name: "Alice",
|
||||
}
|
||||
|
||||
rows, err := db.QueryX(&user, "SELECT * FROM users WHERE name = ?", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("QueryX failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
|
||||
var result User
|
||||
err = rows.Orm(&result)
|
||||
if err != nil {
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
if result.Name != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", result.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_QueryXContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
user := User{
|
||||
Age: 30,
|
||||
}
|
||||
|
||||
rows, err := db.QueryXContext(ctx, &user, "SELECT * FROM users WHERE age = ?", ":age")
|
||||
if err != nil {
|
||||
t.Fatalf("QueryXContext failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_QueryXS(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []User{
|
||||
{Name: "Alice"},
|
||||
{Name: "Bob"},
|
||||
}
|
||||
|
||||
results, err := db.QueryXS(&users, "SELECT * FROM users WHERE name = ?", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("QueryXS failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 2 {
|
||||
t.Errorf("Expected 2 results, got %d", len(results))
|
||||
}
|
||||
|
||||
for i, rows := range results {
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row in result %d, got %d", i, rows.Length())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_ExecX(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
user := User{
|
||||
Name: "Alice",
|
||||
Age: 99,
|
||||
}
|
||||
|
||||
result, err := db.ExecX(&user, "UPDATE users SET age = ? WHERE name = ?", ":age", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecX failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
|
||||
// Verify
|
||||
rows, err := db.Query("SELECT age FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
age := rows.Row(0).MustInt("age")
|
||||
if age != 99 {
|
||||
t.Errorf("Expected age 99, got %d", age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_ExecXContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
user := User{
|
||||
Name: "Bob",
|
||||
Active: false,
|
||||
}
|
||||
|
||||
result, err := db.ExecXContext(ctx, &user, "UPDATE users SET active = ? WHERE name = ?", ":active", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecXContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_ExecXS(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
users := []User{
|
||||
{Name: "Alice", Age: 26},
|
||||
{Name: "Bob", Age: 31},
|
||||
}
|
||||
|
||||
results, err := db.ExecXS(&users, "UPDATE users SET age = ? WHERE name = ?", ":age", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecXS failed: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 2 {
|
||||
t.Errorf("Expected 2 results, got %d", len(results))
|
||||
}
|
||||
|
||||
for i, result := range results {
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed for result %d: %v", i, err)
|
||||
}
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected in result %d, got %d", i, affected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarTx_Insert(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
user := User{
|
||||
Name: "Frank",
|
||||
Email: "frank@example.com",
|
||||
Age: 45,
|
||||
Balance: 500.00,
|
||||
Active: true,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
result, err := tx.Insert(&user, "users", "id")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Tx.Insert failed: %v", err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("Commit failed: %v", err)
|
||||
}
|
||||
|
||||
lastID, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
t.Fatalf("LastInsertId failed: %v", err)
|
||||
}
|
||||
|
||||
if lastID <= 0 {
|
||||
t.Errorf("Expected positive last insert ID, got %d", lastID)
|
||||
}
|
||||
|
||||
// Verify
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Frank")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarTx_Update(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
// Get user
|
||||
rows, err := tx.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
|
||||
var user User
|
||||
err = rows.Orm(&user)
|
||||
rows.Close()
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Orm failed: %v", err)
|
||||
}
|
||||
|
||||
// Update
|
||||
user.Age = 27
|
||||
result, err := tx.Update(&user, "users", "id")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Tx.Update failed: %v", err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("Commit failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarTx_QueryX(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
user := User{
|
||||
Name: "Charlie",
|
||||
}
|
||||
|
||||
rows, err := tx.QueryX(&user, "SELECT * FROM users WHERE name = ?", ":name")
|
||||
if err != nil {
|
||||
t.Fatalf("Tx.QueryX failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarTx_ExecX(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
user := User{
|
||||
Name: "Charlie",
|
||||
Age: 36,
|
||||
}
|
||||
|
||||
result, err := tx.ExecX(&user, "UPDATE users SET age = ? WHERE name = ?", ":age", ":name")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Tx.ExecX failed: %v", err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("Commit failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarTx_Rollback(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
user := User{
|
||||
Name: "Rollback User",
|
||||
Email: "rollback@example.com",
|
||||
Age: 50,
|
||||
Balance: 600.00,
|
||||
Active: true,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
_, err = tx.Insert(&user, "users", "id")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Tx.Insert failed: %v", err)
|
||||
}
|
||||
|
||||
// Rollback instead of commit
|
||||
err = tx.Rollback()
|
||||
if err != nil {
|
||||
t.Fatalf("Rollback failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the insert was rolled back
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Rollback User")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 0 {
|
||||
t.Errorf("Expected 0 rows after rollback, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamedParameterEscape(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
user := User{
|
||||
Name: "Alice",
|
||||
}
|
||||
|
||||
// Test escaped colon
|
||||
rows, err := db.QueryX(&user, "SELECT * FROM users WHERE name = ?", `\:name`)
|
||||
if err != nil {
|
||||
t.Fatalf("QueryX with escaped parameter failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Should use literal ":name" string, not the field value
|
||||
if rows.Length() != 0 {
|
||||
t.Errorf("Expected 0 rows with literal ':name', got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"b612.me/stardb"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestDefaultPoolConfig(t *testing.T) {
|
||||
config := stardb.DefaultPoolConfig()
|
||||
|
||||
if config.MaxOpenConns != 25 {
|
||||
t.Errorf("Expected MaxOpenConns 25, got %d", config.MaxOpenConns)
|
||||
}
|
||||
|
||||
if config.MaxIdleConns != 5 {
|
||||
t.Errorf("Expected MaxIdleConns 5, got %d", config.MaxIdleConns)
|
||||
}
|
||||
|
||||
if config.ConnMaxLifetime != time.Hour {
|
||||
t.Errorf("Expected ConnMaxLifetime 1 hour, got %v", config.ConnMaxLifetime)
|
||||
}
|
||||
|
||||
if config.ConnMaxIdleTime != 10*time.Minute {
|
||||
t.Errorf("Expected ConnMaxIdleTime 10 minutes, got %v", config.ConnMaxIdleTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_SetPoolConfig(t *testing.T) {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 50,
|
||||
MaxIdleConns: 10,
|
||||
ConnMaxLifetime: 30 * time.Minute,
|
||||
ConnMaxIdleTime: 5 * time.Minute,
|
||||
}
|
||||
|
||||
db.SetPoolConfig(config)
|
||||
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 50 {
|
||||
t.Errorf("Expected MaxOpenConnections 50, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_SetPoolConfig_Partial(t *testing.T) {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Only set MaxOpenConns
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 100,
|
||||
}
|
||||
|
||||
db.SetPoolConfig(config)
|
||||
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 100 {
|
||||
t.Errorf("Expected MaxOpenConnections 100, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_SetPoolConfig_Zero(t *testing.T) {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Zero values should be ignored
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 0,
|
||||
MaxIdleConns: 0,
|
||||
}
|
||||
|
||||
db.SetPoolConfig(config)
|
||||
|
||||
// Should not panic or error
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed after SetPoolConfig with zero values: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenWithPool_Default(t *testing.T) {
|
||||
db, err := stardb.OpenWithPool("sqlite", ":memory:", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenWithPool failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed: %v", err)
|
||||
}
|
||||
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 25 {
|
||||
t.Errorf("Expected default MaxOpenConnections 25, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenWithPool_Custom(t *testing.T) {
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 15,
|
||||
MaxIdleConns: 3,
|
||||
ConnMaxLifetime: 20 * time.Minute,
|
||||
ConnMaxIdleTime: 3 * time.Minute,
|
||||
}
|
||||
|
||||
db, err := stardb.OpenWithPool("sqlite", ":memory:", config)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenWithPool failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed: %v", err)
|
||||
}
|
||||
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 15 {
|
||||
t.Errorf("Expected MaxOpenConnections 15, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenWithPool_InvalidDriver(t *testing.T) {
|
||||
config := stardb.DefaultPoolConfig()
|
||||
|
||||
db, err := stardb.OpenWithPool("invalid_driver", "invalid_conn", config)
|
||||
if err == nil {
|
||||
db.Close()
|
||||
t.Error("Expected error with invalid driver, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenWithPool_Query(t *testing.T) {
|
||||
db, err := stardb.OpenWithPool("sqlite", ":memory:", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenWithPool failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create table
|
||||
_, err = db.Exec(`CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create table: %v", err)
|
||||
}
|
||||
|
||||
// Insert data
|
||||
_, err = db.Exec(`INSERT INTO test (name) VALUES (?)`, "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to insert data: %v", err)
|
||||
}
|
||||
|
||||
// Query data
|
||||
rows, err := db.Query("SELECT * FROM test")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolConfig_AllFields(t *testing.T) {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 30,
|
||||
MaxIdleConns: 8,
|
||||
ConnMaxLifetime: 45 * time.Minute,
|
||||
ConnMaxIdleTime: 7 * time.Minute,
|
||||
}
|
||||
|
||||
db.SetPoolConfig(config)
|
||||
|
||||
// Verify by checking stats
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 30 {
|
||||
t.Errorf("Expected MaxOpenConnections 30, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
|
||||
// Test that connections work
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed after SetPoolConfig: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolConfig_NegativeValues(t *testing.T) {
|
||||
db := stardb.NewStarDB()
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Negative values should be ignored
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: -1,
|
||||
MaxIdleConns: -1,
|
||||
}
|
||||
|
||||
db.SetPoolConfig(config)
|
||||
|
||||
// Should not panic or error
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed after SetPoolConfig with negative values: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenWithPool_MultipleConnections(t *testing.T) {
|
||||
config := &stardb.PoolConfig{
|
||||
MaxOpenConns: 5,
|
||||
MaxIdleConns: 2,
|
||||
}
|
||||
|
||||
db, err := stardb.OpenWithPool("sqlite", ":memory:", config)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenWithPool failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Create table
|
||||
_, err = db.Exec(`CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create table: %v", err)
|
||||
}
|
||||
|
||||
// Perform multiple operations
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err = db.Exec(`INSERT INTO test (value) VALUES (?)`, "test")
|
||||
if err != nil {
|
||||
t.Errorf("Insert %d failed: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify
|
||||
rows, err := db.Query("SELECT COUNT(*) as count FROM test")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
count := rows.Row(0).MustInt("count")
|
||||
if count != 10 {
|
||||
t.Errorf("Expected 10 rows, got %d", count)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStarResult_MustString(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
row := rows.Row(0)
|
||||
|
||||
name := row.MustString("name")
|
||||
if name != "Alice" {
|
||||
t.Errorf("Expected 'Alice', got '%s'", name)
|
||||
}
|
||||
|
||||
email := row.MustString("email")
|
||||
if email != "alice@example.com" {
|
||||
t.Errorf("Expected 'alice@example.com', got '%s'", email)
|
||||
}
|
||||
|
||||
// Test non-existent column
|
||||
nonexistent := row.MustString("nonexistent")
|
||||
if nonexistent != "" {
|
||||
t.Errorf("Expected empty string for non-existent column, got '%s'", nonexistent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResult_MustInt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Bob")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
row := rows.Row(0)
|
||||
|
||||
age := row.MustInt("age")
|
||||
if age != 30 {
|
||||
t.Errorf("Expected age 30, got %d", age)
|
||||
}
|
||||
|
||||
id := row.MustInt("id")
|
||||
if id <= 0 {
|
||||
t.Errorf("Expected positive id, got %d", id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResult_MustInt64(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Charlie")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
row := rows.Row(0)
|
||||
|
||||
age := row.MustInt64("age")
|
||||
if age != 35 {
|
||||
t.Errorf("Expected age 35, got %d", age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResult_MustFloat64(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
row := rows.Row(0)
|
||||
|
||||
balance := row.MustFloat64("balance")
|
||||
if balance != 100.50 {
|
||||
t.Errorf("Expected balance 100.50, got %f", balance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResult_MustBool(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Alice - active
|
||||
row := rows.Row(0)
|
||||
active := row.MustBool("active")
|
||||
if !active {
|
||||
t.Errorf("Expected Alice to be active")
|
||||
}
|
||||
|
||||
// Charlie - inactive
|
||||
row = rows.Row(2)
|
||||
active = row.MustBool("active")
|
||||
if active {
|
||||
t.Errorf("Expected Charlie to be inactive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResult_IsNil(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
// Insert a row with NULL value
|
||||
_, err := db.Exec("INSERT INTO users (name, email, age, balance, active, created_at) VALUES (?, ?, NULL, ?, ?, ?)",
|
||||
"David", "david@example.com", 150.0, true, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("Insert failed: %v", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE name = ?", "David")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
row := rows.Row(0)
|
||||
|
||||
if !row.IsNil("age") {
|
||||
t.Errorf("Expected age to be NULL")
|
||||
}
|
||||
|
||||
if row.IsNil("name") {
|
||||
t.Errorf("Expected name to not be NULL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResultCol_MustString(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
col := rows.Col("name")
|
||||
names := col.MustString()
|
||||
|
||||
expected := []string{"Alice", "Bob", "Charlie"}
|
||||
for i, name := range names {
|
||||
if name != expected[i] {
|
||||
t.Errorf("Expected name '%s', got '%s'", expected[i], name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResultCol_MustInt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
col := rows.Col("age")
|
||||
ages := col.MustInt()
|
||||
|
||||
expected := []int{25, 30, 35}
|
||||
for i, age := range ages {
|
||||
if age != expected[i] {
|
||||
t.Errorf("Expected age %d, got %d", expected[i], age)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResultCol_MustFloat64(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
col := rows.Col("balance")
|
||||
balances := col.MustFloat64()
|
||||
|
||||
expected := []float64{100.50, 200.75, 300.25}
|
||||
for i, balance := range balances {
|
||||
if balance != expected[i] {
|
||||
t.Errorf("Expected balance %f, got %f", expected[i], balance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarResultCol_MustBool(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
col := rows.Col("active")
|
||||
actives := col.MustBool()
|
||||
|
||||
expected := []bool{true, true, false}
|
||||
for i, active := range actives {
|
||||
if active != expected[i] {
|
||||
t.Errorf("Expected active %v, got %v", expected[i], active)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStarRows_Row(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Test first row
|
||||
row := rows.Row(0)
|
||||
name := row.MustString("name")
|
||||
if name != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", name)
|
||||
}
|
||||
|
||||
// Test out of bounds
|
||||
row = rows.Row(999)
|
||||
if len(row.Result()) != 0 {
|
||||
t.Errorf("Expected empty result for out of bounds index")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_Col(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users ORDER BY name")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Test column extraction
|
||||
col := rows.Col("name")
|
||||
names := col.MustString()
|
||||
|
||||
if len(names) != 3 {
|
||||
t.Errorf("Expected 3 names, got %d", len(names))
|
||||
}
|
||||
|
||||
if names[0] != "Alice" {
|
||||
t.Errorf("Expected first name 'Alice', got '%s'", names[0])
|
||||
}
|
||||
|
||||
// Test non-existent column
|
||||
col = rows.Col("nonexistent")
|
||||
if len(col.Result()) != 0 {
|
||||
t.Errorf("Expected empty result for non-existent column")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_Rescan(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
db.ManualScan = true
|
||||
rows, err := db.Query("SELECT * FROM users")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
err = rows.Rescan()
|
||||
if err != nil {
|
||||
t.Fatalf("Rescan failed: %v", err)
|
||||
}
|
||||
|
||||
if rows.Length() != 3 {
|
||||
t.Errorf("Expected 3 rows, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarRows_StringResult(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT name, age FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if len(rows.StringResult()) != 1 {
|
||||
t.Fatalf("Expected 1 string result, got %d", len(rows.StringResult()))
|
||||
}
|
||||
|
||||
record := rows.StringResult()[0]
|
||||
if record["name"] != "Alice" {
|
||||
t.Errorf("Expected name 'Alice', got '%s'", record["name"])
|
||||
}
|
||||
|
||||
if record["age"] != "25" {
|
||||
t.Errorf("Expected age '25', got '%s'", record["age"])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"b612.me/stardb"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func TestStarDB_Open(t *testing.T) {
|
||||
db := &stardb.StarDB{}
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Errorf("Open failed: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
t.Errorf("Ping failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Query(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT * FROM users WHERE age > ?", 25)
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 2 {
|
||||
t.Errorf("Expected 2 rows, got %d", rows.Length())
|
||||
}
|
||||
|
||||
if len(rows.Columns()) != 7 {
|
||||
t.Errorf("Expected 7 columns, got %d", len(rows.Columns()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_QueryContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
rows, err := db.QueryContext(ctx, "SELECT * FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("QueryContext failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Exec(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
result, err := db.Exec("UPDATE users SET age = ? WHERE name = ?", 26, "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Exec failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_ExecContext(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
result, err := db.ExecContext(ctx, "DELETE FROM users WHERE name = ?", "Charlie")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecContext failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_QueryStmt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.QueryStmt("SELECT * FROM users WHERE age > ?", 25)
|
||||
if err != nil {
|
||||
t.Fatalf("QueryStmt failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 2 {
|
||||
t.Errorf("Expected 2 rows, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_ExecStmt(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
result, err := db.ExecStmt("UPDATE users SET age = ? WHERE name = ?", 27, "Bob")
|
||||
if err != nil {
|
||||
t.Fatalf("ExecStmt failed: %v", err)
|
||||
}
|
||||
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
t.Fatalf("RowsAffected failed: %v", err)
|
||||
}
|
||||
|
||||
if affected != 1 {
|
||||
t.Errorf("Expected 1 row affected, got %d", affected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Prepare(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
stmt, err := db.Prepare("SELECT * FROM users WHERE name = ?")
|
||||
if err != nil {
|
||||
t.Fatalf("Prepare failed: %v", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query("Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Stmt.Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if rows.Length() != 1 {
|
||||
t.Errorf("Expected 1 row, got %d", rows.Length())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_Transaction(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec("UPDATE users SET age = ? WHERE name = ?", 28, "Alice")
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
t.Fatalf("Tx.Exec failed: %v", err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("Commit failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the change
|
||||
rows, err := db.Query("SELECT age FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
age := rows.Row(0).MustInt("age")
|
||||
if age != 28 {
|
||||
t.Errorf("Expected age 28, got %d", age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_TransactionRollback(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatalf("Begin failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec("UPDATE users SET age = ? WHERE name = ?", 99, "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Tx.Exec failed: %v", err)
|
||||
}
|
||||
|
||||
err = tx.Rollback()
|
||||
if err != nil {
|
||||
t.Fatalf("Rollback failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify the change was rolled back
|
||||
rows, err := db.Query("SELECT age FROM users WHERE name = ?", "Alice")
|
||||
if err != nil {
|
||||
t.Fatalf("Query failed: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
age := rows.Row(0).MustInt("age")
|
||||
if age == 99 {
|
||||
t.Errorf("Expected age to be rolled back, but got %d", age)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStarDB_SetMaxConnections(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetMaxIdleConns(5)
|
||||
|
||||
stats := db.Stats()
|
||||
if stats.MaxOpenConnections != 10 {
|
||||
t.Errorf("Expected MaxOpenConnections 10, got %d", stats.MaxOpenConnections)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"b612.me/stardb"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// setupTestDB creates a test database with sample data
|
||||
// This function is only available when building with -tags=testing
|
||||
func setupTestDB(t *testing.T) *stardb.StarDB {
|
||||
db := &stardb.StarDB{}
|
||||
err := db.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
|
||||
// Create test table
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
age INTEGER,
|
||||
balance REAL,
|
||||
active BOOLEAN,
|
||||
created_at DATETIME
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create table: %v", err)
|
||||
}
|
||||
|
||||
// Insert test data
|
||||
_, err = db.Exec(`
|
||||
INSERT INTO users (name, email, age, balance, active, created_at) VALUES
|
||||
('Alice', 'alice@example.com', 25, 100.50, 1, '2024-01-01 10:00:00'),
|
||||
('Bob', 'bob@example.com', 30, 200.75, 1, '2024-01-02 11:00:00'),
|
||||
('Charlie', 'charlie@example.com', 35, 300.25, 0, '2024-01-03 12:00:00')
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to insert test data: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
Reference in New Issue
Block a user