Files
ResolveSpec/pkg/security/lookup/database_test.go
T
Hein c9fa8c60f2 refactor(security): move all database access into pkg/security/lookup
pkg/security no longer contains SQL. Every provider calls a store interface
from lookup, implemented by a procedure backend (Postgres stored procedures,
the default there) and a direct backend (dialect-driven SQL for postgres,
sqlite, mysql and mssql with configurable table and column names).

- add sectypes, lookup, lookup/{dialect,procedure,direct,backends,ddl,conformance}
- split totp and providers sub packages out of the core package
- replace SQLNames/TableNames/QueryMode with lookup.Config (see breaking_changes.md)
- direct backend now covers column/row security and API-key login
- move txsettings SQL to lookup.ApplyTxSettings; remove password.go
- move schema scripts under lookup/, add reference DDL per dialect
- add a shared conformance suite; run it on sqlite, and on Postgres in a
  podman/docker container (RESOLVESPEC_TEST_CONTAINERS=1)
- fix procedure schema bugs found on real Postgres: duplicate p_data
  parameter, JSON null arrays, expires_at timezone casts, passkey list
  GROUP BY, missing resolvespec_passkey_login; accept zone-less timestamps
2026-10-01 13:19:44 +02:00

85 lines
2.3 KiB
Go

package lookup_test
import (
"database/sql"
"testing"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/sqlitedialect"
"github.com/uptrace/bun/driver/sqliteshim"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/bitechdev/ResolveSpec/pkg/common"
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
"github.com/bitechdev/ResolveSpec/pkg/security/lookup"
)
func TestFromDatabase(t *testing.T) {
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared")
if err != nil {
t.Fatal(err)
}
defer sqldb.Close()
gdb, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
cases := map[string]struct {
db common.Database
same *sql.DB // expected handle; nil = just must be non-nil
}{
"pgsql": {database.NewPgSQLAdapter(sqldb, "sqlite"), sqldb},
"bun": {database.NewBunAdapter(bun.NewDB(sqldb, sqlitedialect.New())), sqldb},
"gorm": {database.NewGormAdapter(gdb), nil},
}
for name, c := range cases {
got, dialectName, err := lookup.FromDatabase(c.db)
if err != nil {
t.Errorf("%s: %v", name, err)
continue
}
if got == nil || (c.same != nil && got != c.same) {
t.Errorf("%s: unexpected *sql.DB %v", name, got)
}
if dialectName != "sqlite" {
t.Errorf("%s: dialect = %q, want sqlite", name, dialectName)
}
if err := got.Ping(); err != nil {
t.Errorf("%s: handle not usable: %v", name, err)
}
}
}
func TestFromDatabaseRejects(t *testing.T) {
if _, _, err := lookup.FromDatabase(nil); err == nil {
t.Error("nil database should fail")
}
// A database that does not expose a *sql.DB (the embedded interface is nil; only the type matters).
if _, _, err := lookup.FromDatabase(struct{ common.Database }{}); err == nil {
t.Error("adapter without SQLDB should fail")
}
}
func TestResolveDialect(t *testing.T) {
sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:")
if err != nil {
t.Fatal(err)
}
defer sqldb.Close()
d, err := lookup.Config{}.ResolveDialect(sqldb)
if err != nil || d.Name() != "sqlite" {
t.Errorf("detected = %v, %v", d, err)
}
d, err = lookup.Config{Dialect: "mysql"}.ResolveDialect(sqldb)
if err != nil || d.Name() != "mysql" {
t.Errorf("explicit dialect should win: %v, %v", d, err)
}
if _, err := (lookup.Config{Dialect: "oracle"}).Resolve(); err == nil {
t.Error("unknown dialect should fail Resolve")
}
}