mirror of
https://github.com/gofiber/storage.git
synced 2025-09-29 13:52:20 +08:00
364 lines
8.6 KiB
Go
364 lines
8.6 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var testStore = New(Config{
|
|
Database: os.Getenv("POSTGRES_DATABASE"),
|
|
Username: os.Getenv("POSTGRES_USERNAME"),
|
|
Password: os.Getenv("POSTGRES_PASSWORD"),
|
|
Reset: true,
|
|
})
|
|
|
|
func TestNoCreateUser(t *testing.T) {
|
|
// Create a new user
|
|
// give the use usage permissions to the database (but not create)
|
|
ctx := context.Background()
|
|
conn := testStore.Conn()
|
|
|
|
username := "testuser" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
password := "testpassword"
|
|
|
|
_, err := conn.Exec(ctx, "CREATE USER "+username+" WITH PASSWORD '"+password+"'")
|
|
require.NoError(t, err)
|
|
|
|
_, err = conn.Exec(ctx, "GRANT CONNECT ON DATABASE "+os.Getenv("POSTGRES_DATABASE")+" TO "+username)
|
|
require.NoError(t, err)
|
|
|
|
_, err = conn.Exec(ctx, "GRANT USAGE ON SCHEMA public TO "+username)
|
|
require.NoError(t, err)
|
|
|
|
_, err = conn.Exec(ctx, "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "+username)
|
|
require.NoError(t, err)
|
|
|
|
_, err = conn.Exec(ctx, "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO "+username)
|
|
require.NoError(t, err)
|
|
|
|
_, err = conn.Exec(ctx, "REVOKE CREATE ON SCHEMA public FROM "+username)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("should panic if limited user tries to create table", func(t *testing.T) {
|
|
tableThatDoesNotExist := "public.table_does_not_exists_" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
|
|
defer func() {
|
|
r := recover()
|
|
require.NotNil(t, r, "Expected a panic when creating a table without permissions")
|
|
}()
|
|
|
|
// This should panic since the user doesn't have CREATE permissions
|
|
New(Config{
|
|
Database: os.Getenv("POSTGRES_DATABASE"),
|
|
Username: username,
|
|
Password: password,
|
|
Reset: true,
|
|
Table: tableThatDoesNotExist,
|
|
})
|
|
})
|
|
|
|
// connect to an existing table using an unprivileged user
|
|
limitedStore := New(Config{
|
|
Database: os.Getenv("POSTGRES_DATABASE"),
|
|
Username: username,
|
|
Password: password,
|
|
Reset: false,
|
|
})
|
|
|
|
defer func() {
|
|
limitedStore.Close()
|
|
conn.Exec(ctx, "DROP USER "+username)
|
|
}()
|
|
|
|
t.Run("should set", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
)
|
|
|
|
err := limitedStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
})
|
|
t.Run("should set override", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
)
|
|
err := limitedStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
err = limitedStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
})
|
|
t.Run("should get", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
)
|
|
err := limitedStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
result, err := limitedStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, result)
|
|
})
|
|
t.Run("should set expiration", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
exp = 100 * time.Millisecond
|
|
)
|
|
err := limitedStore.Set(key, val, exp)
|
|
require.NoError(t, err)
|
|
})
|
|
t.Run("should get expired", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
exp = 100 * time.Millisecond
|
|
)
|
|
err := limitedStore.Set(key, val, exp)
|
|
require.NoError(t, err)
|
|
time.Sleep(200 * time.Millisecond)
|
|
result, err := limitedStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
})
|
|
t.Run("should get not exists", func(t *testing.T) {
|
|
result, err := limitedStore.Get("nonexistentkey")
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
})
|
|
t.Run("should delete", func(t *testing.T) {
|
|
var (
|
|
key = "john" + strconv.Itoa(int(time.Now().UnixNano()))
|
|
val = []byte("doe" + strconv.Itoa(int(time.Now().UnixNano())))
|
|
)
|
|
err := limitedStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
err = limitedStore.Delete(key)
|
|
require.NoError(t, err)
|
|
result, err := limitedStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
})
|
|
|
|
}
|
|
func Test_Should_Panic_On_Wrong_Schema(t *testing.T) {
|
|
// Create a test table with wrong schema
|
|
_, err := testStore.Conn().Exec(context.Background(), `
|
|
CREATE TABLE IF NOT EXISTS test_schema_table (
|
|
k VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT '',
|
|
v BYTEA NOT NULL,
|
|
e VARCHAR(64) NOT NULL DEFAULT '' -- Changed e from BIGINT to VARCHAR
|
|
);
|
|
`)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
_, err := testStore.Conn().Exec(context.Background(), "DROP TABLE IF EXISTS test_schema_table;")
|
|
require.NoError(t, err)
|
|
}()
|
|
|
|
// Call checkSchema with the wrong table
|
|
require.Panics(t, func() {
|
|
testStore.checkSchema("test_schema_table")
|
|
})
|
|
}
|
|
|
|
func Test_Postgres_Set(t *testing.T) {
|
|
var (
|
|
key = "john"
|
|
val = []byte("doe")
|
|
)
|
|
|
|
err := testStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func Test_Postgres_Set_Override(t *testing.T) {
|
|
var (
|
|
key = "john"
|
|
val = []byte("doe")
|
|
)
|
|
|
|
err := testStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
|
|
err = testStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func Test_Postgres_Get(t *testing.T) {
|
|
var (
|
|
key = "john"
|
|
val = []byte("doe")
|
|
)
|
|
|
|
err := testStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
|
|
result, err := testStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, result)
|
|
}
|
|
|
|
func Test_Postgres_Set_Expiration(t *testing.T) {
|
|
var (
|
|
key = "john"
|
|
val = []byte("doe")
|
|
exp = 1 * time.Second
|
|
)
|
|
|
|
err := testStore.Set(key, val, exp)
|
|
require.NoError(t, err)
|
|
|
|
time.Sleep(1100 * time.Millisecond)
|
|
}
|
|
|
|
func Test_Postgres_Get_Expired(t *testing.T) {
|
|
key := "john"
|
|
|
|
result, err := testStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
}
|
|
|
|
func Test_Postgres_Get_NotExist(t *testing.T) {
|
|
result, err := testStore.Get("notexist")
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
}
|
|
|
|
func Test_Postgres_Delete(t *testing.T) {
|
|
var (
|
|
key = "john"
|
|
val = []byte("doe")
|
|
)
|
|
|
|
err := testStore.Set(key, val, 0)
|
|
require.NoError(t, err)
|
|
|
|
err = testStore.Delete(key)
|
|
require.NoError(t, err)
|
|
|
|
result, err := testStore.Get(key)
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
}
|
|
|
|
func Test_Postgres_Reset(t *testing.T) {
|
|
val := []byte("doe")
|
|
|
|
err := testStore.Set("john1", val, 0)
|
|
require.NoError(t, err)
|
|
|
|
err = testStore.Set("john2", val, 0)
|
|
require.NoError(t, err)
|
|
|
|
err = testStore.Reset()
|
|
require.NoError(t, err)
|
|
|
|
result, err := testStore.Get("john1")
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
|
|
result, err = testStore.Get("john2")
|
|
require.NoError(t, err)
|
|
require.Zero(t, len(result))
|
|
}
|
|
|
|
func Test_Postgres_GC(t *testing.T) {
|
|
testVal := []byte("doe")
|
|
|
|
// This key should expire
|
|
err := testStore.Set("john", testVal, time.Nanosecond)
|
|
require.NoError(t, err)
|
|
|
|
testStore.gc(time.Now())
|
|
row := testStore.db.QueryRow(context.Background(), testStore.sqlSelect, "john")
|
|
err = row.Scan(nil, nil)
|
|
require.Equal(t, pgx.ErrNoRows, err)
|
|
|
|
// This key should not expire
|
|
err = testStore.Set("john", testVal, 0)
|
|
require.NoError(t, err)
|
|
|
|
testStore.gc(time.Now())
|
|
val, err := testStore.Get("john")
|
|
require.NoError(t, err)
|
|
require.Equal(t, testVal, val)
|
|
}
|
|
|
|
func Test_Postgres_Non_UTF8(t *testing.T) {
|
|
val := []byte("0xF5")
|
|
|
|
err := testStore.Set("0xF6", val, 0)
|
|
require.NoError(t, err)
|
|
|
|
result, err := testStore.Get("0xF6")
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, result)
|
|
}
|
|
|
|
func Test_SslRequiredMode(t *testing.T) {
|
|
defer func() {
|
|
if recover() == nil {
|
|
require.Equalf(t, true, nil, "Connection was established with a `require`")
|
|
}
|
|
}()
|
|
_ = New(Config{
|
|
Reset: true,
|
|
})
|
|
}
|
|
|
|
func Test_Postgres_Conn(t *testing.T) {
|
|
require.True(t, testStore.Conn() != nil)
|
|
}
|
|
|
|
func Test_Postgres_Close(t *testing.T) {
|
|
require.Nil(t, testStore.Close())
|
|
}
|
|
|
|
func Benchmark_Postgres_Set(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
var err error
|
|
for i := 0; i < b.N; i++ {
|
|
err = testStore.Set("john", []byte("doe"), 0)
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
func Benchmark_Postgres_Get(b *testing.B) {
|
|
err := testStore.Set("john", []byte("doe"), 0)
|
|
require.NoError(b, err)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err = testStore.Get("john")
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|
|
|
|
func Benchmark_Postgres_SetAndDelete(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
|
|
var err error
|
|
for i := 0; i < b.N; i++ {
|
|
_ = testStore.Set("john", []byte("doe"), 0)
|
|
err = testStore.Delete("john")
|
|
}
|
|
|
|
require.NoError(b, err)
|
|
}
|