commit cd9478f3b3893090aab15305e948ea875f8e9649
parent 2108b77e5392058c82acf98871aaf951a6bdc65e
Author: lash <dev@holbrook.no>
Date: Sun, 1 Sep 2024 17:02:04 +0100
WIP db test vectors
Diffstat:
7 files changed, 148 insertions(+), 56 deletions(-)
diff --git a/db/db.go b/db/db.go
@@ -8,6 +8,10 @@ import (
)
const (
+ safeLock =DATATYPE_BIN | DATATYPE_MENU | DATATYPE_TEMPLATE | DATATYPE_STATICLOAD
+)
+
+const (
// Invalid datatype, must raise error if attempted used.
DATATYPE_UNKNOWN = 0
// Bytecode
@@ -21,11 +25,11 @@ const (
// State and cache from persister
DATATYPE_STATE = 16
// Application data
- DATATYPE_USERSTART = 32
+ DATATYPE_USERDATA = 32
)
const (
- datatype_sessioned_threshold = DATATYPE_TEMPLATE
+ datatype_sessioned_threshold = DATATYPE_STATICLOAD
)
// Db abstracts all data storage and retrieval as a key-value store
@@ -45,7 +49,11 @@ type Db interface {
// * DATATYPE_STATE
// * DATATYPE_USERSTART
SetSession(sessionId string)
- SetLock(typ uint8, locked bool)
+ // SetLock disables modification of data that is readonly in the vm context.
+ // If called with typ value 0, it will permanently lock all readonly members.
+ SetLock(typ uint8, locked bool) error
+ // Safe returns true if db is safe for use with a vm.
+ Safe() bool
}
type lookupKey struct {
@@ -73,30 +81,44 @@ type baseDb struct {
sid []byte
lock uint8
lang *lang.Language
+ seal bool
}
// ensures default locking of read-only entries
func(db *baseDb) defaultLock() {
- db.lock = DATATYPE_BIN | DATATYPE_MENU | DATATYPE_TEMPLATE | DATATYPE_STATICLOAD
+ db.lock |= safeLock
+}
+
+func(db *baseDb) Safe() bool {
+ return db.lock & safeLock == safeLock
}
-// SetPrefix implements Db.
+// SetPrefix implements the Db interface.
func(db *baseDb) SetPrefix(pfx uint8) {
db.pfx = pfx
}
-// SetSession implements Db.
+// SetSession implements the Db interface.
func(db *baseDb) SetSession(sessionId string) {
db.sid = append([]byte(sessionId), 0x2E)
}
-// SetSafety disables modification of data that
-func(db *baseDb) SetLock(pfx uint8, lock bool) {
+// SetLock implements the Db interface.
+func(db *baseDb) SetLock(pfx uint8, lock bool) error {
+ if db.seal {
+ return errors.New("SetLock on sealed db")
+ }
+ if pfx == 0 {
+ db.seal = true
+ db.defaultLock()
+ return nil
+ }
if lock {
db.lock |= pfx
} else {
db.lock &= ^pfx
}
+ return nil
}
func(db *baseDb) checkPut() bool {
@@ -125,6 +147,6 @@ func(db *baseDb) ToKey(ctx context.Context, key []byte) (lookupKey, error) {
if ok {
lk.Translation = ToDbKey(db.pfx, b, &ln)
}
- }
+ }
return lk, nil
}
diff --git a/db/db_test.go b/db/db_test.go
@@ -1,9 +1,16 @@
package db
import (
+ "bytes"
"context"
"encoding/hex"
+ "errors"
+ "fmt"
"path"
+ "strconv"
+ "testing"
+
+ "git.defalsify.org/vise.git/lang"
)
type testCase struct {
@@ -11,12 +18,15 @@ type testCase struct {
s string
k []byte
v []byte
+ x []byte
+ l *lang.Language
}
type testVector struct {
c map[string]*testCase
v []string
i int
+ s string
}
func(tc *testCase) Key() []byte {
@@ -35,17 +45,36 @@ func(tc *testCase) Session() string {
return tc.s
}
-func(tv *testVector) add(typ uint8, k string, v string, session string) {
+func(tc *testCase) Expect() []byte {
+ return tc.x
+}
+
+func(tv *testVector) add(typ uint8, k string, v string, session string, expect string, language string) {
var b []byte
+ var x []byte
var err error
+ var ln *lang.Language
if typ == DATATYPE_BIN {
b, err = hex.DecodeString(v)
if err != nil {
panic(err)
}
+ x, err = hex.DecodeString(expect)
+ if err != nil {
+ panic(err)
+ }
} else {
b = []byte(v)
+ x = []byte(expect)
+ }
+
+ if language != "" {
+ lo, err := lang.LanguageFromCode(language)
+ if err != nil {
+ panic(err)
+ }
+ ln = &lo
}
o := &testCase {
@@ -53,10 +82,15 @@ func(tv *testVector) add(typ uint8, k string, v string, session string) {
k: []byte(k),
v: b,
s: session,
+ x: x,
+ l: ln,
}
- s := path.Join(session, k)
+ s := path.Join(strconv.Itoa(int(typ)), session)
+ s = path.Join(s, k)
tv.c[s] = o
+ i := len(tv.v)
tv.v = append(tv.v, s)
+ logg.Tracef("add testcase", "i", i, "s", s, "k", o.k)
}
func(tv *testVector) next() (int, *testCase) {
@@ -94,16 +128,78 @@ func(tv *testVector) put(ctx context.Context, db Db) error {
return nil
}
-func generateTestVectors() testVector {
- tv := testVector{c: make(map[string]*testCase)}
- tv.add(DATATYPE_BIN, "foo", "deadbeef", "")
- tv.add(DATATYPE_BIN, "foo", "beeffeed", "tinkywinky")
- tv.add(DATATYPE_TEMPLATE, "foo", "inky", "")
- tv.add(DATATYPE_TEMPLATE, "foo", "pinky", "dipsy")
- tv.add(DATATYPE_MENU, "foo", "blinky", "")
- tv.add(DATATYPE_MENU, "foo", "clyde", "lala")
- tv.add(DATATYPE_STATICLOAD, "foo", "bar", "")
- tv.add(DATATYPE_STATICLOAD, "foo", "baz", "po")
+func(tv *testVector) label() string {
+ return tv.s
+}
+
+func generateSessionTestVectors() testVector {
+ tv := testVector{c: make(map[string]*testCase), s: "session"}
+ tv.add(DATATYPE_BIN, "foo", "deadbeef", "", "beeffeed", "")
+ tv.add(DATATYPE_BIN, "foo", "beeffeed", "inky", "beeffeed", "")
+ tv.add(DATATYPE_TEMPLATE, "foo", "tinkywinky", "", "dipsy", "")
+ tv.add(DATATYPE_TEMPLATE, "foo", "dipsy", "pinky", "dipsy", "")
+ tv.add(DATATYPE_MENU, "foo", "lala", "", "pu", "")
+ tv.add(DATATYPE_MENU, "foo", "pu", "blinky", "pu", "")
+ tv.add(DATATYPE_STATICLOAD, "foo", "bar", "", "baz", "")
+ tv.add(DATATYPE_STATICLOAD, "foo", "baz", "clyde", "baz", "")
+ tv.add(DATATYPE_STATE, "foo", "xyzzy", "", "xyzzy", "")
+ tv.add(DATATYPE_STATE, "foo", "plugh", "sue", "plugh", "")
+ tv.add(DATATYPE_USERDATA, "foo", "itchy", "", "itchy", "")
+ tv.add(DATATYPE_USERDATA, "foo", "scratchy", "poochie", "scratchy", "")
+ return tv
+}
+
+func generateLanguageTestVectors() testVector {
+ tv := testVector{c: make(map[string]*testCase), s: "language"}
+ tv.add(DATATYPE_BIN, "foo", "deadbeef", "", "beeffeed", "")
+ tv.add(DATATYPE_BIN, "foo", "deadbeef", "", "deadbeef", "nor")
return tv
}
+func runTest(t *testing.T, ctx context.Context, db Db, vs testVector) error {
+ err := vs.put(ctx, db)
+ if err != nil {
+ return err
+ }
+ for true {
+ i, tc := vs.next()
+ if i == -1 {
+ break
+ }
+ s := fmt.Sprintf("Test%sTyp%dKey%s", vs.label(), tc.Typ(), tc.Key())
+ if tc.Session() != "" {
+ s += "Session" + tc.Session()
+ } else {
+ s += "NoSession"
+ }
+ r := t.Run(s, func(t *testing.T) {
+ db.SetPrefix(tc.Typ())
+ db.SetSession(tc.Session())
+ v, err := db.Get(ctx, tc.Key())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(tc.Expect(), v) {
+ t.Fatalf("expected %s, got %s", tc.Expect(), v)
+ }
+ })
+ if !r {
+ return errors.New("subtest fail")
+ }
+ }
+ return nil
+
+}
+func runTests(t *testing.T, ctx context.Context, db Db) error {
+ err := runTest(t, ctx, db, generateSessionTestVectors())
+ if err != nil {
+ return err
+ }
+
+ err = runTest(t, ctx, db, generateLanguageTestVectors())
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/db/fs_test.go b/db/fs_test.go
@@ -18,7 +18,7 @@ func TestPutGetFs(t *testing.T) {
t.Fatal(err)
}
db := NewFsDb()
- db.SetPrefix(DATATYPE_USERSTART)
+ db.SetPrefix(DATATYPE_USERDATA)
db.SetSession(sid)
dbi = db
diff --git a/db/gdbm_test.go b/db/gdbm_test.go
@@ -16,7 +16,7 @@ func TestPutGetGdbm(t *testing.T) {
t.Fatal(err)
}
db := NewGdbmDb()
- db.SetPrefix(DATATYPE_USERSTART)
+ db.SetPrefix(DATATYPE_USERDATA)
db.SetSession(sid)
dbi = db
diff --git a/db/mem.go b/db/mem.go
@@ -42,6 +42,7 @@ func(mdb *memDb) toHexKey(ctx context.Context, key []byte) (memLookupKey, error)
if lk.Translation != nil {
mk.Translation = hex.EncodeToString(lk.Translation)
}
+ logg.TraceCtxf(ctx, "converted key", "orig", key, "b", lk, "s", mk)
return mk, err
}
diff --git a/db/mem_test.go b/db/mem_test.go
@@ -3,48 +3,21 @@ package db
import (
"bytes"
"context"
- "fmt"
"testing"
)
func TestCasesMem(t *testing.T) {
- var i int
- var tc *testCase
-
ctx := context.Background()
- vs := generateTestVectors()
+
db := NewMemDb()
err := db.Connect(ctx, "")
if err != nil {
t.Fatal(err)
}
- err = vs.put(ctx, db)
- for true {
- i, tc = vs.next()
- if i == -1 {
- break
- }
- s := fmt.Sprintf("TestTyp%dKey%s", tc.Typ(), tc.Key())
- if tc.Session() != "" {
- s += "Session" + tc.Session()
- } else {
- s += "NoSession"
- }
- r := t.Run(s, func(t *testing.T) {
- db.SetPrefix(tc.Typ())
- db.SetSession(tc.Session())
- v, err := db.Get(ctx, tc.Key())
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(tc.Val(), v) {
- t.Fatalf("expected %x, got %x", tc.Val(), v)
- }
- })
- if !r {
- t.Fatalf("subtest fail")
- }
+ err = runTests(t, ctx, db)
+ if err != nil {
+ t.Fatal(err)
}
}
@@ -53,7 +26,7 @@ func TestPutGetMem(t *testing.T) {
ctx := context.Background()
sid := "ses"
db := NewMemDb()
- db.SetPrefix(DATATYPE_USERSTART)
+ db.SetPrefix(DATATYPE_USERDATA)
db.SetSession(sid)
dbi = db
diff --git a/db/pg_test.go b/db/pg_test.go
@@ -10,7 +10,7 @@ func TestPutGetPg(t *testing.T) {
var dbi Db
ses := "xyzzy"
db := NewPgDb().WithSchema("vvise")
- db.SetPrefix(DATATYPE_USERSTART)
+ db.SetPrefix(DATATYPE_USERDATA)
db.SetSession(ses)
ctx := context.Background()