重构代码

This commit is contained in:
2026-03-07 19:27:44 +08:00
parent fb9808a139
commit 88569eb176
33 changed files with 5910 additions and 2003 deletions
+563
View File
@@ -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")
}
}
+23
View File
@@ -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 => ../
+53
View File
@@ -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=
+691
View File
@@ -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())
}
}
+272
View File
@@ -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)
}
}
+231
View File
@@ -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)
}
}
}
+103
View File
@@ -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"])
}
}
+233
View File
@@ -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)
}
}
+47
View File
@@ -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
}