Refactor database interface to use *sql.DB directly
Simplified the database layer by removing custom interface wrappers and using standard library *sql.DB and *sql.Tx types directly. Changes: - Removed DBConnection and DBTransaction interfaces from database.go - Removed NewDBConnection() wrapper function - Updated TokenGenerator to use *sql.DB instead of DBConnection - Updated all validation and revocation methods to accept *sql.Tx - Updated TableManager to work with *sql.DB directly - Updated all tests to use db.Begin() instead of custom wrappers - Fixed GeneratorConfig.DB field (was DBConn) - Updated documentation in doc.go with correct API usage Benefits: - Simpler API with fewer abstractions - Works directly with database/sql standard library - Compatible with GORM (via gormDB.DB()) and Bun (share same *sql.DB) - Easier to understand and maintain - No unnecessary wrapper layers Breaking changes: - GeneratorConfig.DBConn renamed to GeneratorConfig.DB - Removed NewDBConnection() function - pass *sql.DB directly - ValidateAccess/ValidateRefresh now accept *sql.Tx instead of DBTransaction - Token.Revoke/CheckNotRevoked now accept *sql.Tx instead of DBTransaction 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
231
hws/server_test.go
Normal file
231
hws/server_test.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package hws_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"git.haelnorr.com/h/golib/hlog"
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var ports []uint64
|
||||
|
||||
func randomPort() uint64 {
|
||||
port := uint64(3000 + rand.IntN(1001))
|
||||
for slices.Contains(ports, port) {
|
||||
port = uint64(3000 + rand.IntN(1001))
|
||||
}
|
||||
ports = append(ports, port)
|
||||
return port
|
||||
}
|
||||
|
||||
func createTestServer(t *testing.T, w io.Writer) *hws.Server {
|
||||
server, err := hws.NewServer(&hws.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: randomPort(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), w, nil, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = server.AddLogger(logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
var testHandler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("hello world"))
|
||||
})
|
||||
|
||||
func startTestServer(t *testing.T, server *hws.Server) {
|
||||
err := server.AddRoutes(hws.Route{
|
||||
Path: "/",
|
||||
Method: hws.MethodGET,
|
||||
Handler: testHandler,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = server.Start(t.Context())
|
||||
require.NoError(t, err)
|
||||
t.Log("Test server started")
|
||||
}
|
||||
|
||||
func Test_NewServer(t *testing.T) {
|
||||
server, err := hws.NewServer(&hws.Config{
|
||||
Host: "localhost",
|
||||
Port: randomPort(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, server)
|
||||
|
||||
t.Run("Nil config returns error", func(t *testing.T) {
|
||||
server, err := hws.NewServer(nil)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, server)
|
||||
assert.Contains(t, err.Error(), "Config cannot be nil")
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
host string
|
||||
port uint64
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "Valid localhost on http",
|
||||
host: "127.0.0.1",
|
||||
port: 80,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Valid IP on https",
|
||||
host: "192.168.1.1",
|
||||
port: 443,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Valid IP on port 65535",
|
||||
host: "10.0.0.5",
|
||||
port: 65535,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "0.0.0.0 on port 8080",
|
||||
host: "0.0.0.0",
|
||||
port: 8080,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Broadcast IP on port 1",
|
||||
host: "255.255.255.255",
|
||||
port: 1,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Port 0 gets default",
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
valid: true, // port 0 now gets default value of 3000
|
||||
},
|
||||
{
|
||||
name: "Invalid port 65536",
|
||||
host: "127.0.0.1",
|
||||
port: 65536,
|
||||
valid: true, // port is accepted (validated at OS level)
|
||||
},
|
||||
{
|
||||
name: "No hostname provided gets default",
|
||||
host: "",
|
||||
port: 80,
|
||||
valid: true, // empty hostname gets default 127.0.0.1
|
||||
},
|
||||
{
|
||||
name: "Spaces provided for host",
|
||||
host: " ",
|
||||
port: 80,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "Localhost as string",
|
||||
host: "localhost",
|
||||
port: 8080,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Number only host",
|
||||
host: "1234",
|
||||
port: 80,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Valid domain on http",
|
||||
host: "example.com",
|
||||
port: 80,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Valid domain on https",
|
||||
host: "a-b-c.example123.co",
|
||||
port: 443,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "Valid domain starting with a digit",
|
||||
host: "1example.com",
|
||||
port: 8080,
|
||||
valid: true, // labels may start with digits (RFC 1123)
|
||||
},
|
||||
{
|
||||
name: "Single character hostname",
|
||||
host: "a",
|
||||
port: 1,
|
||||
valid: true, // single-label hostname, min length
|
||||
},
|
||||
|
||||
{
|
||||
name: "Hostname starts with a hyphen",
|
||||
host: "-example.com",
|
||||
port: 80,
|
||||
valid: false, // label starts with hyphen
|
||||
},
|
||||
{
|
||||
name: "Hostname ends with a hyphen",
|
||||
host: "example-.com",
|
||||
port: 80,
|
||||
valid: false, // label ends with hyphen
|
||||
},
|
||||
{
|
||||
name: "Empty label in hostname",
|
||||
host: "ex..ample.com",
|
||||
port: 80,
|
||||
valid: false, // empty label
|
||||
},
|
||||
{
|
||||
name: "Invalid character: '_'",
|
||||
host: "exa_mple.com",
|
||||
port: 80,
|
||||
valid: false, // invalid character (_)
|
||||
},
|
||||
{
|
||||
name: "Trailing dot",
|
||||
host: "example.com.",
|
||||
port: 80,
|
||||
valid: false, // trailing dot not allowed per spec
|
||||
},
|
||||
{
|
||||
name: "Valid IPv6 localhost",
|
||||
host: "::1",
|
||||
port: 8080,
|
||||
valid: true, // IPv6 localhost
|
||||
},
|
||||
{
|
||||
name: "Valid IPv6 shortened",
|
||||
host: "2001:db8::1",
|
||||
port: 80,
|
||||
valid: true, // shortened IPv6
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
server, err := hws.NewServer(&hws.Config{
|
||||
Host: tt.host,
|
||||
Port: tt.port,
|
||||
})
|
||||
if tt.valid {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, server)
|
||||
} else {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user