Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,18 @@ jobs:
- name: Run compiler unit tests
run: make test

- name: Run Integration Tests
env:
PG_DATABASE_URL: ${{ secrets.PG_DATABASE_URL }}
SQLITE_DATABASE_URL: ${{ secrets.SQLITE_DATABASE_URL }}
- name: Run SQLite Integration Tests
run: make integration-test

- name: Run Postgres Integration Tests
run: |
PG_URL="${{ secrets.PG_DATABASE_URL }}"
if [ -z "$PG_URL" ]; then
PG_URL="postgres://testuser:testpassword@localhost:5432/valkyrie_test?sslmode=disable"
fi
sed -i 's/provider = "sqlite"/provider = "postgres"/' integration/schema.prisma
rm -f integration/valkyrie/migrations/*.sql
make integration-gen
cd integration
../bin/valkyrie migrate -u "$PG_URL" init_pg
PG_DATABASE_URL="$PG_URL" go test -v ./...
40 changes: 29 additions & 11 deletions integration/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func generateCUID() string {
buf := make([]byte, 1, 33)
buf[0] = 'c'
buf = strconv.AppendUint(buf, now, 16)

const hextable = "0123456789abcdef"
for _, v := range b {
buf = append(buf, hextable[v>>4], hextable[v&0x0f])
Expand All @@ -35,14 +35,17 @@ func TestCreationBenchmark(t *testing.T) {
// 1. Raw SQL Insert (Write Only)
t.Logf("Running %d iterations of Raw SQL Insert (Write-only)...", iterations)
startRawWrite := time.Now()
for i := 0; i < iterations; i++ {
for i := range iterations {
id := "raw-w-" + strconv.Itoa(i)
email := fmt.Sprintf("raw-w-%d@example.com", i)
phone := fmt.Sprintf("+12345%d", i)
role := "student"

_, err := db.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
id, email, phone, role,
)
if err != nil {
Expand All @@ -55,10 +58,10 @@ func TestCreationBenchmark(t *testing.T) {
// 2. ORM Create (which inserts and scans back the record)
t.Logf("Running %d iterations of ORM Create...", iterations)
startORM := time.Now()
for i := 0; i < iterations; i++ {
for i := range iterations {
_, err := db.User.Create(valkyrie.UserCreateInput{
Email: fmt.Sprintf("orm-%d@example.com", i),
PhoneNum: fmt.Sprintf("+12345%d", i),
PhoneNum: fmt.Sprintf("+54321%d", i),
}).Exec(ctx)
if err != nil {
t.Fatalf("ORM create failed: %v", err)
Expand All @@ -73,11 +76,14 @@ func TestCreationBenchmark(t *testing.T) {
for i := range iterations {
id := "raw-r-" + strconv.Itoa(i)
email := fmt.Sprintf("raw-r-%d@example.com", i)
phone := fmt.Sprintf("+12345%d", i)
phone := fmt.Sprintf("+99999%d", i)
role := "student"

_, err := db.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
id, email, phone, role,
)
if err != nil {
Expand All @@ -86,7 +92,10 @@ func TestCreationBenchmark(t *testing.T) {

var res valkyrie.User
err = db.Raw().QueryRowContext(ctx,
"SELECT id, email, phoneNum, role, referredById FROM User WHERE id = ?",
query(
`SELECT "id", "email", "phoneNum", "role", "referredById" FROM "User" WHERE "id" = ?`,
`SELECT "id", "email", "phoneNum", "role", "referredById" FROM "User" WHERE "id" = $1`,
),
id,
).Scan(&res.Id, &res.Email, &res.PhoneNum, &res.Role, &res.ReferredById)
if err != nil {
Expand Down Expand Up @@ -125,7 +134,10 @@ func BenchmarkRawSQLCreate(b *testing.B) {
role := "student"

_, err := db.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
id, email, phone, role,
)
if err != nil {
Expand All @@ -146,7 +158,10 @@ func BenchmarkRawSQLCreateWithScan(b *testing.B) {
role := "student"

_, err := db.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
id, email, phone, role,
)
if err != nil {
Expand All @@ -155,7 +170,10 @@ func BenchmarkRawSQLCreateWithScan(b *testing.B) {

var res valkyrie.User
err = db.Raw().QueryRowContext(ctx,
"SELECT id, email, phoneNum, role, referredById FROM User WHERE id = ?",
query(
`SELECT "id", "email", "phoneNum", "role", "referredById" FROM "User" WHERE "id" = ?`,
`SELECT "id", "email", "phoneNum", "role", "referredById" FROM "User" WHERE "id" = $1`,
),
id,
).Scan(&res.Id, &res.Email, &res.PhoneNum, &res.Role, &res.ReferredById)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion integration/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ func TestCreateBasic(t *testing.T) {

var dbEmail, dbPhone string
var dbRole string
err = db.Raw().QueryRowContext(ctx, "SELECT email, phoneNum, role FROM User WHERE id = ?", u.Id).Scan(&dbEmail, &dbPhone, &dbRole)
err = db.Raw().QueryRowContext(ctx, query(
`SELECT "email", "phoneNum", "role" FROM "User" WHERE "id" = ?`,
`SELECT "email", "phoneNum", "role" FROM "User" WHERE "id" = $1`,
), u.Id).Scan(&dbEmail, &dbPhone, &dbRole)
if err != nil {
t.Fatalf("failed to query database for created user: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions integration/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace valkyrie => ../

require (
github.com/google/uuid v1.6.0
github.com/lib/pq v1.12.3
github.com/pressly/goose/v3 v3.27.2
modernc.org/sqlite v1.53.0
)
Expand Down
2 changes: 2 additions & 0 deletions integration/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ 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/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
Expand Down
2 changes: 1 addition & 1 deletion integration/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ model Profile {

model Post {
id String @id @default(cuid())
title String @db.VarChar(22)
title String
content String?
published Boolean @default(false)
authorId String
Expand Down
88 changes: 85 additions & 3 deletions integration/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,102 @@ package main

import (
"context"
"database/sql"
"integration/valkyrie"
"os"
"strings"
"testing"

_ "github.com/lib/pq"
_ "modernc.org/sqlite"
)

func query(sqlite, postgres string) string {
if getActiveProvider() == "postgres" {
return postgres
}
return sqlite
}

func getActiveProvider() string {
content, err := os.ReadFile("schema.prisma")
if err != nil {
return "sqlite"
}
s := string(content)
if strings.Contains(s, `provider = "postgres"`) || strings.Contains(s, `provider = "postgresql"`) {
return "postgres"
}
return "sqlite"
}

func getPostgresDSN() string {
if url := os.Getenv("PG_DATABASE_URL"); url != "" {
return url
}

// Try local docker-compose default DSN first
localDSN := "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable"
db, err := sql.Open("postgres", localDSN)
if err == nil {
err = db.Ping()
db.Close()
if err == nil {
return localDSN
}
}

// Try CI default DSN
return "postgres://testuser:testpassword@localhost:5432/valkyrie_test?sslmode=disable"
}

func setupTestDB(t *testing.T) (*valkyrie.DB, func()) {
ctx := context.Background()
db, err := valkyrie.Open("sqlite", "file::memory:?cache=shared&_pragma=foreign_keys(1)")

provider := getActiveProvider()
var dsn string

if provider == "postgres" {
dsn = getPostgresDSN()

// Reset the postgres schema so we start fresh every time
resetDB, err := sql.Open("postgres", dsn)
if err != nil {
if t != nil {
t.Fatalf("failed to open database for reset: %v", err)
} else {
panic(err)
}
}
_, err = resetDB.Exec("DROP SCHEMA public CASCADE; CREATE SCHEMA public;")
resetDB.Close()
if err != nil {
if t != nil {
t.Fatalf("failed to reset postgres database: %v", err)
} else {
panic(err)
}
}
} else {
dsn = "file::memory:?cache=shared&_pragma=foreign_keys(1)"
}

db, err := valkyrie.Open(provider, dsn)
if err != nil {
t.Fatalf("failed to open database: %v", err)
if t != nil {
t.Fatalf("failed to open database (provider: %s, dsn: %s): %v", provider, dsn, err)
} else {
panic(err)
}
}

if err := db.RunMigrations(ctx); err != nil {
db.Close()
t.Fatalf("failed to run migrations: %v", err)
if t != nil {
t.Fatalf("failed to run migrations: %v", err)
} else {
panic(err)
}
}

cleanup := func() {
Expand Down
50 changes: 40 additions & 10 deletions integration/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ func TestTransactionCommit(t *testing.T) {

err := db.Transaction(ctx, func(tx *valkyrie.Tx) error {
_, err := tx.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
"u1", "user1@example.com", "123456789", "student",
)
return err
Expand All @@ -25,7 +28,10 @@ func TestTransactionCommit(t *testing.T) {
}

var email string
err = db.Raw().QueryRowContext(ctx, "SELECT email FROM User WHERE id = ?", "u1").Scan(&email)
err = db.Raw().QueryRowContext(ctx, query(
`SELECT "email" FROM "User" WHERE "id" = ?`,
`SELECT "email" FROM "User" WHERE "id" = $1`,
), "u1").Scan(&email)
if err != nil {
t.Fatalf("failed to query committed user: %v", err)
}
Expand All @@ -42,7 +48,10 @@ func TestTransactionRollbackOnError(t *testing.T) {
expectedErr := errors.New("something went wrong")
err := db.Transaction(ctx, func(tx *valkyrie.Tx) error {
_, err := tx.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
"u2", "user2@example.com", "987654321", "ADMIN",
)
if err != nil {
Expand All @@ -56,7 +65,10 @@ func TestTransactionRollbackOnError(t *testing.T) {
}

var count int
err = db.Raw().QueryRowContext(ctx, "SELECT COUNT(*) FROM User WHERE id = ?", "u2").Scan(&count)
err = db.Raw().QueryRowContext(ctx, query(
`SELECT COUNT(*) FROM "User" WHERE "id" = ?`,
`SELECT COUNT(*) FROM "User" WHERE "id" = $1`,
), "u2").Scan(&count)
if err != nil {
t.Fatalf("failed to query database: %v", err)
}
Expand All @@ -81,7 +93,10 @@ func TestTransactionRollbackOnPanic(t *testing.T) {
}

var count int
err := db.Raw().QueryRowContext(ctx, "SELECT COUNT(*) FROM User WHERE id = ?", "u3").Scan(&count)
err := db.Raw().QueryRowContext(ctx, query(
`SELECT COUNT(*) FROM "User" WHERE "id" = ?`,
`SELECT COUNT(*) FROM "User" WHERE "id" = $1`,
), "u3").Scan(&count)
if err != nil {
t.Fatalf("failed to query database after panic rollback: %v", err)
}
Expand All @@ -92,7 +107,10 @@ func TestTransactionRollbackOnPanic(t *testing.T) {

_ = db.Transaction(ctx, func(tx *valkyrie.Tx) error {
_, err := tx.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
"u3", "user3@example.com", "555555555", "TEACHER",
)
if err != nil {
Expand All @@ -112,7 +130,10 @@ func TestManualTransactionCommitAndRollback(t *testing.T) {
t.Fatalf("failed to begin tx1: %v", err)
}
_, err = tx1.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
"u4", "user4@example.com", "444444444", "student",
)
if err != nil {
Expand All @@ -124,7 +145,10 @@ func TestManualTransactionCommitAndRollback(t *testing.T) {
}

var count1 int
err = db.Raw().QueryRowContext(ctx, "SELECT COUNT(*) FROM User WHERE id = ?", "u4").Scan(&count1)
err = db.Raw().QueryRowContext(ctx, query(
`SELECT COUNT(*) FROM "User" WHERE "id" = ?`,
`SELECT COUNT(*) FROM "User" WHERE "id" = $1`,
), "u4").Scan(&count1)
if err != nil || count1 != 1 {
t.Errorf("expected user to be committed, count=%d, err=%v", count1, err)
}
Expand All @@ -134,7 +158,10 @@ func TestManualTransactionCommitAndRollback(t *testing.T) {
t.Fatalf("failed to begin tx2: %v", err)
}
_, err = tx2.Raw().ExecContext(ctx,
"INSERT INTO User (id, email, phoneNum, role) VALUES (?, ?, ?, ?)",
query(
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES (?, ?, ?, ?)`,
`INSERT INTO "User" ("id", "email", "phoneNum", "role") VALUES ($1, $2, $3, $4)`,
),
"u5", "user5@example.com", "555555555", "student",
)
if err != nil {
Expand All @@ -146,7 +173,10 @@ func TestManualTransactionCommitAndRollback(t *testing.T) {
}

var count2 int
err = db.Raw().QueryRowContext(ctx, "SELECT COUNT(*) FROM User WHERE id = ?", "u5").Scan(&count2)
err = db.Raw().QueryRowContext(ctx, query(
`SELECT COUNT(*) FROM "User" WHERE "id" = ?`,
`SELECT COUNT(*) FROM "User" WHERE "id" = $1`,
), "u5").Scan(&count2)
if err != nil || count2 != 0 {
t.Errorf("expected user to be rolled back, count=%d, err=%v", count2, err)
}
Expand Down
Loading