mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-02 03:22:09 +00:00
test(tx): regression tests for per-request transactions across all specs
This commit is contained in:
@@ -11,12 +11,14 @@ import (
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect/pgdialect"
|
||||
|
||||
"github.com/bitechdev/ResolveSpec/pkg/cache"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||
)
|
||||
|
||||
func TestAfterReadRunsInSecondTransaction(t *testing.T) {
|
||||
resetTotalCache(t)
|
||||
sqlDB, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -60,3 +62,100 @@ func TestAfterReadRunsInSecondTransaction(t *testing.T) {
|
||||
t.Fatal("BeforeRead must run on the first tx and AfterRead on the second")
|
||||
}
|
||||
}
|
||||
|
||||
// resetTotalCache empties the process-wide query-total cache. Its key ignores the
|
||||
// record id, so a cached total would skip the count query and desync the mock.
|
||||
func resetTotalCache(t *testing.T) {
|
||||
t.Helper()
|
||||
_ = cache.GetDefaultCache().Clear(context.Background())
|
||||
t.Cleanup(func() { _ = cache.GetDefaultCache().Clear(context.Background()) })
|
||||
}
|
||||
|
||||
func newBunHarness(t *testing.T) (*Handler, sqlmock.Sqlmock) {
|
||||
t.Helper()
|
||||
sqlDB, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(1)
|
||||
t.Cleanup(func() { _ = sqlDB.Close() })
|
||||
return NewHandler(database.NewBunAdapter(bun.NewDB(sqlDB, pgdialect.New())), modelregistry.NewModelRegistry()), mock
|
||||
}
|
||||
|
||||
func itemCtx(t *testing.T) context.Context {
|
||||
t.Helper()
|
||||
base, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
t.Cleanup(cancel)
|
||||
ctx := WithSchema(base, "public")
|
||||
ctx = WithEntity(ctx, "items")
|
||||
ctx = WithTableName(ctx, "items")
|
||||
return WithModel(ctx, delItem{})
|
||||
}
|
||||
|
||||
func TestAfterReadErrorFailsRequestOnSecondTransaction(t *testing.T) {
|
||||
resetTotalCache(t)
|
||||
h, mock := newBunHarness(t)
|
||||
h.Hooks().Register(AfterRead, func(*HookContext) error { return http.ErrAbortHandler })
|
||||
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(7, "a"))
|
||||
mock.ExpectCommit()
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectRollback()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
w, _ := common.WrapHTTPRequest(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
h.handleRead(itemCtx(t), w, "7", ExtendedRequestOptions{})
|
||||
|
||||
if rec.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("status %d body %s", rec.Code, rec.Body)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeforeReadErrorRollsBackWithoutQueries(t *testing.T) {
|
||||
h, mock := newBunHarness(t)
|
||||
h.Hooks().Register(BeforeRead, func(*HookContext) error { return http.ErrAbortHandler })
|
||||
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectRollback()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
w, _ := common.WrapHTTPRequest(rec, httptest.NewRequest(http.MethodGet, "/", nil))
|
||||
h.handleRead(itemCtx(t), w, "7", ExtendedRequestOptions{})
|
||||
|
||||
if rec.Code == http.StatusOK {
|
||||
t.Fatalf("a failing BeforeRead must not return data: %s", rec.Body)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAfterUpdateErrorFailsRequest(t *testing.T) {
|
||||
h, mock := newBunHarness(t)
|
||||
h.Hooks().Register(AfterUpdate, func(*HookContext) error { return http.ErrAbortHandler })
|
||||
|
||||
cols := []string{"id", "name"}
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows(cols).AddRow(7, "a"))
|
||||
mock.ExpectExec(`UPDATE`).WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.ExpectCommit()
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows(cols).AddRow(7, "b"))
|
||||
mock.ExpectRollback()
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
w, _ := common.WrapHTTPRequest(rec, httptest.NewRequest(http.MethodPut, "/", nil))
|
||||
h.handleUpdate(itemCtx(t), w, "7", nil, map[string]interface{}{"name": "b"}, ExtendedRequestOptions{})
|
||||
|
||||
if rec.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("status %d body %s", rec.Code, rec.Body)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user