mirror of
https://github.com/hanzoai/orm.git
synced 2026-10-30 15:01:22 +00:00
_entities.id is a bare primary key, and the put upsert replaced data whatever kind held the id, keeping that row's kind: a key written at a user's id became that user's row. Put, PutMulti and the transaction's Put update only a row of their own kind and answer ErrKindMismatch when another kind holds the id, leaving it byte for byte; a batch with one such key writes nothing. The ZAP SQL backend carries the same guard, held on a real PostgreSQL through the ZAP client, and the document backend updates by (_id, kind) and reports an insert conflict as ErrKindMismatch.
64 lines
2.3 KiB
Go
64 lines
2.3 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// row reads an id's stored kind and data exactly as the table holds them.
|
|
func row(t *testing.T, db *SQLiteDB, id string) (kind, data string) {
|
|
t.Helper()
|
|
if err := db.readDB.QueryRow(`SELECT kind, data FROM _entities WHERE id = ?`, id).Scan(&kind, &data); err != nil {
|
|
t.Fatalf("read %s: %v", id, err)
|
|
}
|
|
return kind, data
|
|
}
|
|
|
|
// A put at an id another kind holds is refused on every writer, and the row it
|
|
// would have replaced is left byte for byte as it was. A key written at a user's
|
|
// id used to become that user's row.
|
|
func TestPutRefusesAnotherKindsId(t *testing.T) {
|
|
db := newTestDB(t)
|
|
ctx := context.Background()
|
|
user := db.NewKey("user", "acme/alice", 0, nil)
|
|
if _, err := db.Put(ctx, user, &testEntity{Name: "alice", Email: "[email protected]"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
kind, data := row(t, db, "acme/alice")
|
|
|
|
key := db.NewKey("key", "acme/alice", 0, nil)
|
|
if _, err := db.Put(ctx, key, &testEntity{Name: "a key"}); !errors.Is(err, ErrKindMismatch) {
|
|
t.Fatalf("put: %v, want ErrKindMismatch", err)
|
|
}
|
|
other := db.NewKey("key", "acme/other", 0, nil)
|
|
if _, err := db.PutMulti(ctx, []Key{other, key}, []*testEntity{{Name: "fine"}, {Name: "a key"}}); !errors.Is(err, ErrKindMismatch) {
|
|
t.Fatalf("put multi: %v, want ErrKindMismatch", err)
|
|
}
|
|
var got testEntity
|
|
if err := db.Get(ctx, other, &got); !errors.Is(err, ErrNoSuchEntity) {
|
|
t.Fatalf("a refused batch wrote its other rows: %+v (%v)", got, err)
|
|
}
|
|
if err := db.RunInTransaction(ctx, func(tx Transaction) error {
|
|
_, err := tx.Put(key, &testEntity{Name: "a key"})
|
|
return err
|
|
}, nil); !errors.Is(err, ErrKindMismatch) {
|
|
t.Fatalf("transaction put: %v, want ErrKindMismatch", err)
|
|
}
|
|
|
|
if k, d := row(t, db, "acme/alice"); k != kind || d != data {
|
|
t.Fatalf("the user row changed under refusals: kind %q data %s, was %q %s", k, d, kind, data)
|
|
}
|
|
if err := db.Get(ctx, user, &got); err != nil || got.Email != "[email protected]" {
|
|
t.Fatalf("user = %+v (%v)", got, err)
|
|
}
|
|
|
|
// The same kind still updates its own row.
|
|
if _, err := db.Put(ctx, user, &testEntity{Name: "alice", Email: "[email protected]"}); err != nil {
|
|
t.Fatalf("same-kind put: %v", err)
|
|
}
|
|
if err := db.Get(ctx, user, &got); err != nil || got.Email != "[email protected]" {
|
|
t.Fatalf("same-kind update = %+v (%v)", got, err)
|
|
}
|
|
}
|