removal of python, uses golang testing
This commit is contained in:
133
tests/internal/testutil/testutil.go
Normal file
133
tests/internal/testutil/testutil.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package testutil
|
||||
|
||||
// Package testutil provides test helpers for handler and integration tests.
|
||||
// It wires up an in-memory SQLite database, a lightweight HTTP request helper,
|
||||
// and common assertion utilities.
|
||||
|
||||
import (
|
||||
"Engine/internal/database"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
_ "modernc.org/sqlite" // pure-Go SQLite driver, no CGO required
|
||||
)
|
||||
|
||||
// ── Database ──────────────────────────────────────────────────────────────────
|
||||
|
||||
// NewTestDB opens a fresh in-memory SQLite database, runs your schema
|
||||
// migrations, and registers a t.Cleanup to close it when the test ends.
|
||||
//
|
||||
// Each call gets its own isolated database — tests never share state.
|
||||
func NewTestDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
|
||||
// "file::memory:?cache=shared" would share across connections;
|
||||
// the plain ":memory:" gives a fully isolated DB per open call.
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("testutil.NewTestDB: open: %v", err)
|
||||
}
|
||||
|
||||
// SQLite in-memory works best with a single connection.
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
if err := database.Migrate(db); err != nil {
|
||||
db.Close()
|
||||
t.Fatalf("testutil.NewTestDB: migrate: %v", err)
|
||||
}
|
||||
|
||||
t.Cleanup(func() { db.Close() })
|
||||
return db
|
||||
}
|
||||
|
||||
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
// Do fires an HTTP request directly at handler h and returns the recorded
|
||||
// response. body is marshalled to JSON; pass nil to send no body.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// w := testutil.Do(t, http.HandlerFunc(h.Ingest), http.MethodPost, "/", payload)
|
||||
func Do(t *testing.T, h http.Handler, method, path string, body any) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
|
||||
var r io.Reader
|
||||
if body != nil {
|
||||
b, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
t.Fatalf("testutil.Do: marshal body: %v", err)
|
||||
}
|
||||
r = bytes.NewReader(b)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(method, path, r)
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, req)
|
||||
return w
|
||||
}
|
||||
|
||||
// MustJSON marshals v to JSON and returns it as an io.Reader.
|
||||
// Intended for use with http.Client.Post in full-server integration tests.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// resp, _ := client.Post(srv.URL+"/api/v1/budgets", "application/json", testutil.MustJSON(t, payload))
|
||||
func MustJSON(t *testing.T, v any) io.Reader {
|
||||
t.Helper()
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatalf("testutil.MustJSON: %v", err)
|
||||
}
|
||||
return bytes.NewReader(b)
|
||||
}
|
||||
|
||||
// ── Assertions ────────────────────────────────────────────────────────────────
|
||||
|
||||
// AssertStatus fails the test if the recorder's status code does not match want.
|
||||
func AssertStatus(t *testing.T, w *httptest.ResponseRecorder, want int) {
|
||||
t.Helper()
|
||||
if w.Code != want {
|
||||
t.Errorf("status: got %d, want %d\n\tbody: %s", w.Code, want, w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// AssertJSONField decodes the recorder's body as a JSON object and checks that
|
||||
// the top-level key equals the expected string value. Useful for quick smoke
|
||||
// checks without defining a full response struct.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// testutil.AssertJSONField(t, w, "status", "ok")
|
||||
func AssertJSONField(t *testing.T, w *httptest.ResponseRecorder, key, want string) {
|
||||
t.Helper()
|
||||
var m map[string]any
|
||||
if err := json.NewDecoder(w.Body).Decode(&m); err != nil {
|
||||
t.Fatalf("AssertJSONField: decode body: %v", err)
|
||||
}
|
||||
got, ok := m[key]
|
||||
if !ok {
|
||||
t.Errorf("AssertJSONField: key %q not found in response", key)
|
||||
return
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("AssertJSONField: %q = %q, want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeJSON decodes the recorder's body into dst.
|
||||
// Fails the test immediately if decoding errors.
|
||||
func DecodeJSON(t *testing.T, w *httptest.ResponseRecorder, dst any) {
|
||||
t.Helper()
|
||||
if err := json.NewDecoder(w.Body).Decode(dst); err != nil {
|
||||
t.Fatalf("DecodeJSON: %v\n\tbody: %s", err, w.Body.String())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user