mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Use memdb for tests. (#131)
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
"github.com/ncruces/go-sqlite3/internal/util"
|
"github.com/ncruces/go-sqlite3/internal/util"
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Open_dir(t *testing.T) {
|
func Test_Open_dir(t *testing.T) {
|
||||||
@@ -38,8 +37,11 @@ func Test_Open_dir(t *testing.T) {
|
|||||||
|
|
||||||
func Test_Open_pragma(t *testing.T) {
|
func Test_Open_pragma(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_pragma": {"busy_timeout(1000)"},
|
||||||
|
})
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout(1000)")
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -57,8 +59,11 @@ func Test_Open_pragma(t *testing.T) {
|
|||||||
|
|
||||||
func Test_Open_pragma_invalid(t *testing.T) {
|
func Test_Open_pragma_invalid(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_pragma": {"busy_timeout 1000"},
|
||||||
|
})
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout+1000")
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -81,14 +86,13 @@ func Test_Open_pragma_invalid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Open_txLock(t *testing.T) {
|
func Test_Open_txLock(t *testing.T) {
|
||||||
if !vfs.SupportsFileLocking {
|
|
||||||
t.Skip("skipping without locks")
|
|
||||||
}
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_txlock": {"exclusive"},
|
||||||
|
"_pragma": {"busy_timeout(1000)"},
|
||||||
|
})
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file:"+
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
|
|
||||||
"?_txlock=exclusive&_pragma=busy_timeout(0)")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -119,8 +123,11 @@ func Test_Open_txLock(t *testing.T) {
|
|||||||
|
|
||||||
func Test_Open_txLock_invalid(t *testing.T) {
|
func Test_Open_txLock_invalid(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_txlock": {"xclusive"},
|
||||||
|
})
|
||||||
|
|
||||||
_, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive")
|
_, err := sql.Open("sqlite3", tmp+"_txlock=xclusive")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("want error")
|
t.Fatal("want error")
|
||||||
}
|
}
|
||||||
@@ -130,17 +137,16 @@ func Test_Open_txLock_invalid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_BeginTx(t *testing.T) {
|
func Test_BeginTx(t *testing.T) {
|
||||||
if !vfs.SupportsFileLocking {
|
|
||||||
t.Skip("skipping without locks")
|
|
||||||
}
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_txlock": {"exclusive"},
|
||||||
|
"_pragma": {"busy_timeout(0)"},
|
||||||
|
})
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file:"+
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
|
|
||||||
"?_txlock=exclusive&_pragma=busy_timeout(0)")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -182,8 +188,9 @@ func Test_BeginTx(t *testing.T) {
|
|||||||
|
|
||||||
func Test_Prepare(t *testing.T) {
|
func Test_Prepare(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", ":memory:")
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -222,11 +229,12 @@ func Test_Prepare(t *testing.T) {
|
|||||||
|
|
||||||
func Test_QueryRow_named(t *testing.T) {
|
func Test_QueryRow_named(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", ":memory:")
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -274,8 +282,9 @@ func Test_QueryRow_named(t *testing.T) {
|
|||||||
|
|
||||||
func Test_QueryRow_blob_null(t *testing.T) {
|
func Test_QueryRow_blob_null(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", ":memory:")
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -310,7 +319,11 @@ func Test_time(t *testing.T) {
|
|||||||
|
|
||||||
for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} {
|
for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} {
|
||||||
t.Run(fmt, func(t *testing.T) {
|
t.Run(fmt, func(t *testing.T) {
|
||||||
db, err := sql.Open("sqlite3", "file::memory:?_timefmt="+url.QueryEscape(fmt))
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_timefmt": {fmt},
|
||||||
|
})
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
"github.com/ncruces/go-sqlite3/ext/array"
|
"github.com/ncruces/go-sqlite3/ext/array"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Example_driver() {
|
func Example_driver() {
|
||||||
db, err := driver.Open(":memory:", array.Register)
|
db, err := driver.Open("file:/test.db?vfs=memdb", array.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -87,8 +88,9 @@ func Example() {
|
|||||||
|
|
||||||
func Test_cursor_Column(t *testing.T) {
|
func Test_cursor_Column(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", array.Register)
|
db, err := driver.Open(tmp, array.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
"github.com/ncruces/go-sqlite3/ext/fileio"
|
"github.com/ncruces/go-sqlite3/ext/fileio"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_lsmode(t *testing.T) {
|
func Test_lsmode(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", fileio.Register)
|
db, err := driver.Open(tmp, fileio.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -51,7 +53,9 @@ func Test_readfile(t *testing.T) {
|
|||||||
|
|
||||||
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
|
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
|
db, err := driver.Open(tmp, func(c *sqlite3.Conn) error {
|
||||||
fileio.RegisterFS(c, fsys)
|
fileio.RegisterFS(c, fsys)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
"github.com/ncruces/go-sqlite3/ext/fileio"
|
"github.com/ncruces/go-sqlite3/ext/fileio"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_fsdir(t *testing.T) {
|
func Test_fsdir(t *testing.T) {
|
||||||
@@ -20,7 +21,9 @@ func Test_fsdir(t *testing.T) {
|
|||||||
|
|
||||||
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
|
for _, fsys := range []fs.FS{nil, os.DirFS(".")} {
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
|
db, err := driver.Open(tmp, func(c *sqlite3.Conn) error {
|
||||||
fileio.RegisterFS(c, fsys)
|
fileio.RegisterFS(c, fsys)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_writefile(t *testing.T) {
|
func Test_writefile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
_ "golang.org/x/crypto/blake2b"
|
_ "golang.org/x/crypto/blake2b"
|
||||||
_ "golang.org/x/crypto/blake2s"
|
_ "golang.org/x/crypto/blake2s"
|
||||||
_ "golang.org/x/crypto/md4"
|
_ "golang.org/x/crypto/md4"
|
||||||
@@ -19,6 +20,7 @@ import (
|
|||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
func TestRegister(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -52,7 +54,7 @@ func TestRegister(t *testing.T) {
|
|||||||
{"blake2b('', 256)", "0E5751C026E543B2E8AB2EB06099DAA1D1E5DF47778F7787FAAB45CDF12FE3A8"},
|
{"blake2b('', 256)", "0E5751C026E543B2E8AB2EB06099DAA1D1E5DF47778F7787FAAB45CDF12FE3A8"},
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
"github.com/ncruces/go-sqlite3/ext/lines"
|
"github.com/ncruces/go-sqlite3/ext/lines"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Example() {
|
func Example() {
|
||||||
db, err := driver.Open(":memory:", lines.Register)
|
db, err := driver.Open("file:/test.db?vfs=memdb", lines.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -66,8 +67,9 @@ func Example() {
|
|||||||
|
|
||||||
func Test_lines(t *testing.T) {
|
func Test_lines(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", lines.Register)
|
db, err := driver.Open(tmp, lines.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -96,8 +98,9 @@ func Test_lines(t *testing.T) {
|
|||||||
|
|
||||||
func Test_lines_error(t *testing.T) {
|
func Test_lines_error(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", lines.Register)
|
db, err := driver.Open(tmp, lines.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -120,8 +123,9 @@ func Test_lines_error(t *testing.T) {
|
|||||||
|
|
||||||
func Test_lines_read(t *testing.T) {
|
func Test_lines_read(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", lines.Register)
|
db, err := driver.Open(tmp, lines.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -151,8 +155,9 @@ func Test_lines_read(t *testing.T) {
|
|||||||
|
|
||||||
func Test_lines_test(t *testing.T) {
|
func Test_lines_test(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", lines.Register)
|
db, err := driver.Open(tmp, lines.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
func TestRegister(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -45,8 +47,9 @@ func TestRegister(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegister_errors(t *testing.T) {
|
func TestRegister_errors(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_generate(t *testing.T) {
|
func Test_generate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -130,8 +132,9 @@ func Test_generate(t *testing.T) {
|
|||||||
|
|
||||||
func Test_convert(t *testing.T) {
|
func Test_convert(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", Register)
|
db, err := driver.Open(tmp, Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
"github.com/ncruces/go-sqlite3/ext/zorder"
|
"github.com/ncruces/go-sqlite3/ext/zorder"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRegister_zorder(t *testing.T) {
|
func TestRegister_zorder(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", zorder.Register)
|
db, err := driver.Open(tmp, zorder.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -57,8 +59,9 @@ func TestRegister_zorder(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegister_unzorder(t *testing.T) {
|
func TestRegister_unzorder(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", zorder.Register)
|
db, err := driver.Open(tmp, zorder.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -84,8 +87,9 @@ func TestRegister_unzorder(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegister_error(t *testing.T) {
|
func TestRegister_error(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", zorder.Register)
|
db, err := driver.Open(tmp, zorder.Register)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDialector(t *testing.T) {
|
func TestDialector(t *testing.T) {
|
||||||
// This is the DSN of the in-memory SQLite database for these tests.
|
tmp := memdb.TestDB(t)
|
||||||
const InMemoryDSN = "file:testdatabase?mode=memory&cache=shared"
|
|
||||||
|
|
||||||
// Custom connection with a custom function called "my_custom_function".
|
// Custom connection with a custom function called "my_custom_function".
|
||||||
db, err := driver.Open(InMemoryDSN, func(conn *sqlite3.Conn) error {
|
db, err := driver.Open(tmp, func(conn *sqlite3.Conn) error {
|
||||||
return conn.CreateFunction("my_custom_function", 0, sqlite3.DETERMINISTIC,
|
return conn.CreateFunction("my_custom_function", 0, sqlite3.DETERMINISTIC,
|
||||||
func(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
func(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||||
ctx.ResultText("my-result")
|
ctx.ResultText("my-result")
|
||||||
@@ -36,14 +36,14 @@ func TestDialector(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "Default driver",
|
description: "Default driver",
|
||||||
dialector: Open(InMemoryDSN),
|
dialector: Open(tmp),
|
||||||
openSuccess: true,
|
openSuccess: true,
|
||||||
query: "SELECT 1",
|
query: "SELECT 1",
|
||||||
querySuccess: true,
|
querySuccess: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Custom function",
|
description: "Custom function",
|
||||||
dialector: Open(InMemoryDSN),
|
dialector: Open(tmp),
|
||||||
openSuccess: true,
|
openSuccess: true,
|
||||||
query: "SELECT my_custom_function()",
|
query: "SELECT my_custom_function()",
|
||||||
querySuccess: false,
|
querySuccess: false,
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/ncruces/go-sqlite3"
|
"github.com/ncruces/go-sqlite3"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Example_json() {
|
func Example_json() {
|
||||||
db, err := sqlite3.Open("file:/test.db?vfs=memdb")
|
db, err := sqlite3.Open(":memory:")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (t params) mustExec(sql string, args ...interface{}) sql.Result {
|
|||||||
|
|
||||||
func (sqliteDB) RunTest(t *testing.T, fn func(params)) {
|
func (sqliteDB) RunTest(t *testing.T, fn func(params)) {
|
||||||
db, err := sql.Open("sqlite3", "file:"+
|
db, err := sql.Open("sqlite3", "file:"+
|
||||||
filepath.Join(t.TempDir(), "foo.db")+
|
filepath.ToSlash(filepath.Join(t.TempDir(), "foo.db"))+
|
||||||
"?_pragma=busy_timeout(10000)&_pragma=synchronous(off)")
|
"?_pragma=busy_timeout(10000)&_pragma=synchronous(off)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("foo.db open fail: %v", err)
|
t.Fatalf("foo.db open fail: %v", err)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -727,8 +729,11 @@ func TestConn_DBName(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_AutoVacuumPages(t *testing.T) {
|
func TestConn_AutoVacuumPages(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t, url.Values{
|
||||||
|
"_pragma": {"auto_vacuum(full)"},
|
||||||
|
})
|
||||||
|
|
||||||
db, err := sqlite3.Open("file:test.db?vfs=memdb&_pragma=auto_vacuum(full)")
|
db, err := sqlite3.Open(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
_ "github.com/ncruces/go-sqlite3/vfs/adiantum"
|
_ "github.com/ncruces/go-sqlite3/vfs/adiantum"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ func TestDB_utf16(t *testing.T) {
|
|||||||
|
|
||||||
func TestDB_memdb(t *testing.T) {
|
func TestDB_memdb(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
testDB(t, "file:test.db?vfs=memdb")
|
testDB(t, memdb.TestDB(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDB_adiantum(t *testing.T) {
|
func TestDB_adiantum(t *testing.T) {
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDriver(t *testing.T) {
|
func TestDriver(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := driver.Open(":memory:", nil, func(c *sqlite3.Conn) error {
|
db, err := driver.Open(tmp, nil, func(c *sqlite3.Conn) error {
|
||||||
return c.Exec(`PRAGMA optimize`)
|
return c.Exec(`PRAGMA optimize`)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,16 +11,18 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
"github.com/ncruces/julianday"
|
"github.com/ncruces/julianday"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJSON(t *testing.T) {
|
func TestJSON(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := driver.Open(":memory:")
|
db, err := driver.Open(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tests
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -31,7 +32,7 @@ func Test_parallel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := "file:" +
|
name := "file:" +
|
||||||
filepath.Join(t.TempDir(), "test.db") +
|
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db")) +
|
||||||
"?_pragma=busy_timeout(10000)" +
|
"?_pragma=busy_timeout(10000)" +
|
||||||
"&_pragma=journal_mode(truncate)" +
|
"&_pragma=journal_mode(truncate)" +
|
||||||
"&_pragma=synchronous(off)"
|
"&_pragma=synchronous(off)"
|
||||||
@@ -45,7 +46,7 @@ func Test_wal(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
name := "file:" +
|
name := "file:" +
|
||||||
filepath.Join(t.TempDir(), "test.db") +
|
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db")) +
|
||||||
"?_pragma=busy_timeout(10000)" +
|
"?_pragma=busy_timeout(10000)" +
|
||||||
"&_pragma=journal_mode(wal)" +
|
"&_pragma=journal_mode(wal)" +
|
||||||
"&_pragma=synchronous(off)"
|
"&_pragma=synchronous(off)"
|
||||||
@@ -61,8 +62,9 @@ func Test_memdb(t *testing.T) {
|
|||||||
iter = 5000
|
iter = 5000
|
||||||
}
|
}
|
||||||
|
|
||||||
memdb.Create("test.db", nil)
|
name := memdb.TestDB(t, url.Values{
|
||||||
name := "file:/test.db?vfs=memdb"
|
"_pragma": {"busy_timeout(10000)"},
|
||||||
|
})
|
||||||
testParallel(t, name, iter)
|
testParallel(t, name, iter)
|
||||||
testIntegrity(t, name)
|
testIntegrity(t, name)
|
||||||
}
|
}
|
||||||
@@ -82,7 +84,10 @@ func Test_adiantum(t *testing.T) {
|
|||||||
name := "file:" +
|
name := "file:" +
|
||||||
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db")) +
|
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db")) +
|
||||||
"?vfs=adiantum" +
|
"?vfs=adiantum" +
|
||||||
"&_pragma=hexkey(e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)"
|
"&_pragma=hexkey(e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)" +
|
||||||
|
"&_pragma=busy_timeout(10000)" +
|
||||||
|
"&_pragma=journal_mode(truncate)" +
|
||||||
|
"&_pragma=synchronous(off)"
|
||||||
testParallel(t, name, iter)
|
testParallel(t, name, iter)
|
||||||
testIntegrity(t, name)
|
testIntegrity(t, name)
|
||||||
}
|
}
|
||||||
@@ -98,7 +103,7 @@ func TestMultiProcess(t *testing.T) {
|
|||||||
file := filepath.Join(t.TempDir(), "test.db")
|
file := filepath.Join(t.TempDir(), "test.db")
|
||||||
t.Setenv("TestMultiProcess_dbfile", file)
|
t.Setenv("TestMultiProcess_dbfile", file)
|
||||||
|
|
||||||
name := "file:" + file +
|
name := "file:" + filepath.ToSlash(file) +
|
||||||
"?_pragma=busy_timeout(10000)" +
|
"?_pragma=busy_timeout(10000)" +
|
||||||
"&_pragma=journal_mode(truncate)" +
|
"&_pragma=journal_mode(truncate)" +
|
||||||
"&_pragma=synchronous(off)"
|
"&_pragma=synchronous(off)"
|
||||||
@@ -133,7 +138,7 @@ func TestChildProcess(t *testing.T) {
|
|||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
name := "file:" + file +
|
name := "file:" + filepath.ToSlash(file) +
|
||||||
"?_pragma=busy_timeout(10000)" +
|
"?_pragma=busy_timeout(10000)" +
|
||||||
"&_pragma=journal_mode(truncate)" +
|
"&_pragma=journal_mode(truncate)" +
|
||||||
"&_pragma=synchronous(off)"
|
"&_pragma=synchronous(off)"
|
||||||
@@ -177,8 +182,9 @@ func Benchmark_memdb(b *testing.B) {
|
|||||||
sqlite3.Initialize()
|
sqlite3.Initialize()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
memdb.Create("test.db", nil)
|
name := memdb.TestDB(b, url.Values{
|
||||||
name := "file:/test.db?vfs=memdb"
|
"_pragma": {"busy_timeout(10000)"},
|
||||||
|
})
|
||||||
testParallel(b, name, b.N)
|
testParallel(b, name, b.N)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +224,6 @@ func testParallel(t testing.TB, name string, n int) {
|
|||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err = db.BusyTimeout(10 * time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, _, err := db.Prepare(`SELECT id, name FROM users`)
|
stmt, _, err := db.Prepare(`SELECT id, name FROM users`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3/driver"
|
"github.com/ncruces/go-sqlite3/driver"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTimeFormat_Encode(t *testing.T) {
|
func TestTimeFormat_Encode(t *testing.T) {
|
||||||
@@ -132,11 +133,12 @@ func TestTimeFormat_Decode(t *testing.T) {
|
|||||||
|
|
||||||
func TestTimeFormat_Scanner(t *testing.T) {
|
func TestTimeFormat_Scanner(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
db, err := driver.Open(":memory:")
|
db, err := driver.Open(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/ncruces/go-sqlite3"
|
"github.com/ncruces/go-sqlite3"
|
||||||
_ "github.com/ncruces/go-sqlite3/embed"
|
_ "github.com/ncruces/go-sqlite3/embed"
|
||||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||||
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConn_Transaction_exec(t *testing.T) {
|
func TestConn_Transaction_exec(t *testing.T) {
|
||||||
@@ -254,14 +254,15 @@ func TestConn_Transaction_interrupted(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_Transaction_busy(t *testing.T) {
|
func TestConn_Transaction_busy(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
tmp := memdb.TestDB(t)
|
||||||
|
|
||||||
db1, err := sqlite3.Open("file:/test.db?vfs=memdb")
|
db1, err := sqlite3.Open(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer db1.Close()
|
defer db1.Close()
|
||||||
|
|
||||||
db2, err := sqlite3.Open("file:/test.db?vfs=memdb&_pragma=busy_timeout(10000)")
|
db2, err := sqlite3.Open(tmp + "&_pragma=busy_timeout(10000)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
package memdb
|
package memdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
)
|
)
|
||||||
@@ -66,3 +69,30 @@ func Delete(name string) {
|
|||||||
defer memoryMtx.Unlock()
|
defer memoryMtx.Unlock()
|
||||||
delete(memoryDBs, name)
|
delete(memoryDBs, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDB creates an empty shared memory database for the test to use.
|
||||||
|
// The database is automatically deleted when the test and all its subtests complete.
|
||||||
|
// Each subsequent call to TestDB returns a unique database.
|
||||||
|
func TestDB(tb testing.TB, params ...url.Values) string {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
name := fmt.Sprintf("%s_%p", tb.Name(), tb)
|
||||||
|
tb.Cleanup(func() { Delete(name) })
|
||||||
|
Create(name, nil)
|
||||||
|
|
||||||
|
p := url.Values{"vfs": {"memdb"}}
|
||||||
|
for _, v := range params {
|
||||||
|
for k, v := range v {
|
||||||
|
for _, v := range v {
|
||||||
|
p.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (&url.URL{
|
||||||
|
Scheme: "file",
|
||||||
|
OmitHost: true,
|
||||||
|
Path: "/" + name,
|
||||||
|
RawQuery: p.Encode(),
|
||||||
|
}).String()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user