Extract common database functionality from api/ntpdb and monitor/ntpdb into shared common/database package: - Dynamic connector pattern with configuration loading - Configurable connection pool management (API: 25/10, Monitor: 10/5) - Optional Prometheus metrics integration - Generic transaction helpers with proper error handling - Unified interfaces compatible with SQLC-generated code Foundation for migration to eliminate ~200 lines of duplicate code.
118 lines
3.7 KiB
Go
118 lines
3.7 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
)
|
|
|
|
// Mock types for testing SQLC integration patterns
|
|
type mockQueries struct {
|
|
db DBTX
|
|
}
|
|
|
|
type mockQueriesTx struct {
|
|
*mockQueries
|
|
tx *sql.Tx
|
|
}
|
|
|
|
// Mock the Begin method pattern that SQLC generates
|
|
func (q *mockQueries) Begin(ctx context.Context) (*mockQueriesTx, error) {
|
|
// This would normally be: tx, err := q.db.(*sql.DB).BeginTx(ctx, nil)
|
|
// For our test, we return a mock
|
|
return &mockQueriesTx{mockQueries: q, tx: nil}, nil
|
|
}
|
|
|
|
func (qtx *mockQueriesTx) Commit(ctx context.Context) error {
|
|
return nil // Mock implementation
|
|
}
|
|
|
|
func (qtx *mockQueriesTx) Rollback(ctx context.Context) error {
|
|
return nil // Mock implementation
|
|
}
|
|
|
|
// This test verifies that our common database interfaces are compatible with SQLC-generated code
|
|
func TestSQLCIntegration(t *testing.T) {
|
|
// Test that SQLC's DBTX interface matches our DBTX interface
|
|
t.Run("DBTX Interface Compatibility", func(t *testing.T) {
|
|
// Test interface compatibility by assignment without execution
|
|
var ourDBTX DBTX
|
|
|
|
// Test with sql.DB (should implement DBTX)
|
|
var db *sql.DB
|
|
ourDBTX = db // This will compile only if interfaces are compatible
|
|
_ = ourDBTX // Use the variable to avoid "unused" warning
|
|
|
|
// Test with sql.Tx (should implement DBTX)
|
|
var tx *sql.Tx
|
|
ourDBTX = tx // This will compile only if interfaces are compatible
|
|
_ = ourDBTX // Use the variable to avoid "unused" warning
|
|
|
|
// If we reach here, interfaces are compatible
|
|
t.Log("DBTX interface is compatible with sql.DB and sql.Tx")
|
|
})
|
|
|
|
t.Run("Transaction Interface Compatibility", func(t *testing.T) {
|
|
// This test verifies our transaction interfaces work with SQLC patterns
|
|
// We can't define methods inside a function, so we test interface compatibility
|
|
|
|
// Verify our DB interface is compatible with what SQLC expects
|
|
var dbInterface DB[*mockQueriesTx]
|
|
var mockDB *mockQueries = &mockQueries{}
|
|
dbInterface = mockDB
|
|
|
|
// Test that our transaction helper can work with this pattern
|
|
err := WithTransaction(context.Background(), dbInterface, func(ctx context.Context, qtx *mockQueriesTx) error {
|
|
// This would be where you'd call SQLC-generated query methods
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Errorf("Transaction helper failed: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test that demonstrates how the common package would be used with real SQLC patterns
|
|
func TestRealWorldUsagePattern(t *testing.T) {
|
|
// This test shows how a package would typically use our common database code
|
|
|
|
t.Run("Database Opening Pattern", func(t *testing.T) {
|
|
// Test that our configuration options work as expected
|
|
opts := DefaultConfigOptions()
|
|
|
|
// Modify for test environment (no actual database connection)
|
|
opts.ConfigFiles = []string{} // No config files for unit test
|
|
opts.PrometheusRegisterer = nil // No metrics for unit test
|
|
|
|
// This would normally open a database: db, err := OpenDB(ctx, opts)
|
|
// For our unit test, we just verify the options are reasonable
|
|
if opts.MaxOpenConns <= 0 {
|
|
t.Error("MaxOpenConns should be positive")
|
|
}
|
|
if opts.MaxIdleConns <= 0 {
|
|
t.Error("MaxIdleConns should be positive")
|
|
}
|
|
if opts.ConnMaxLifetime <= 0 {
|
|
t.Error("ConnMaxLifetime should be positive")
|
|
}
|
|
})
|
|
|
|
t.Run("Monitor Package Configuration", func(t *testing.T) {
|
|
opts := MonitorConfigOptions()
|
|
|
|
// Verify monitor-specific settings
|
|
if opts.EnablePoolMonitoring {
|
|
t.Error("Monitor package should not enable pool monitoring")
|
|
}
|
|
if opts.PrometheusRegisterer != nil {
|
|
t.Error("Monitor package should not have Prometheus registerer")
|
|
}
|
|
if opts.MaxOpenConns != 10 {
|
|
t.Errorf("Expected MaxOpenConns=10 for monitor, got %d", opts.MaxOpenConns)
|
|
}
|
|
if opts.MaxIdleConns != 5 {
|
|
t.Errorf("Expected MaxIdleConns=5 for monitor, got %d", opts.MaxIdleConns)
|
|
}
|
|
})
|
|
}
|