removal of python, uses golang testing
This commit is contained in:
259
tests/budget_test.go
Normal file
259
tests/budget_test.go
Normal file
@@ -0,0 +1,259 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"Engine/internal/database"
|
||||
"Engine/internal/handler"
|
||||
"Engine/internal/model"
|
||||
"Engine/internal/service"
|
||||
"Engine/tests/internal/testutil"
|
||||
)
|
||||
|
||||
// ── wire helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
func newBudgetServer(t *testing.T) *httptest.Server {
|
||||
t.Helper()
|
||||
db := testutil.NewTestDB(t)
|
||||
repo := database.NewBudgetRepo(db)
|
||||
h := handler.NewBudgetHandler(service.NewBudgetService(repo))
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("POST /api/v1/budgets", h.Create)
|
||||
mux.HandleFunc("PUT /api/v1/budgets/{id}", h.Update)
|
||||
mux.HandleFunc("DELETE /api/v1/budgets/{id}", h.Delete)
|
||||
|
||||
srv := httptest.NewServer(mux)
|
||||
t.Cleanup(srv.Close)
|
||||
return srv
|
||||
}
|
||||
|
||||
func newBudgetHandler(t *testing.T) *handler.BudgetHandler {
|
||||
t.Helper()
|
||||
return handler.NewBudgetHandler(service.NewBudgetService(database.NewBudgetRepo(testutil.NewTestDB(t))))
|
||||
}
|
||||
|
||||
func validBudget() map[string]any {
|
||||
return map[string]any{
|
||||
"fiscal_year": 2024,
|
||||
"fiscal_period": 1,
|
||||
"version": "original", // adjust to match your BudgetVersion values
|
||||
"department_id": 1,
|
||||
"gl_account_id": 1,
|
||||
"amount": 5000.00,
|
||||
"currency": "USD",
|
||||
"notes": "",
|
||||
"created_by": "test",
|
||||
}
|
||||
}
|
||||
|
||||
// ── Create ────────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestCreateBudget_OK(t *testing.T) {
|
||||
h := newBudgetHandler(t)
|
||||
|
||||
w := testutil.Do(t, http.HandlerFunc(h.Create), http.MethodPost, "/", validBudget())
|
||||
testutil.AssertStatus(t, w, http.StatusCreated)
|
||||
|
||||
var got model.Budget
|
||||
testutil.DecodeJSON(t, w, &got)
|
||||
if got.ID == 0 {
|
||||
t.Error("expected non-zero ID")
|
||||
}
|
||||
if got.Amount != 5000.00 {
|
||||
t.Errorf("amount: got %v, want 5000.00", got.Amount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBudget_InvalidJSON(t *testing.T) {
|
||||
h := newBudgetHandler(t)
|
||||
w := testutil.Do(t, http.HandlerFunc(h.Create), http.MethodPost, "/", nil)
|
||||
testutil.AssertStatus(t, w, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func TestCreateBudget_MissingPeriod(t *testing.T) {
|
||||
h := newBudgetHandler(t)
|
||||
body := validBudget()
|
||||
delete(body, "fiscal_period")
|
||||
w := testutil.Do(t, http.HandlerFunc(h.Create), http.MethodPost, "/", body)
|
||||
testutil.AssertStatus(t, w, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func TestCreateBudget_ZeroAmount(t *testing.T) {
|
||||
h := newBudgetHandler(t)
|
||||
body := validBudget()
|
||||
body["amount"] = 0
|
||||
w := testutil.Do(t, http.HandlerFunc(h.Create), http.MethodPost, "/", body)
|
||||
// Whether 0 is rejected or accepted depends on your business rule — adjust to match
|
||||
t.Logf("zero amount response: %d — verify against your handler", w.Code)
|
||||
}
|
||||
|
||||
func TestCreateBudget_NegativeAmount(t *testing.T) {
|
||||
h := newBudgetHandler(t)
|
||||
body := validBudget()
|
||||
body["amount"] = -100
|
||||
w := testutil.Do(t, http.HandlerFunc(h.Create), http.MethodPost, "/", body)
|
||||
testutil.AssertStatus(t, w, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// ── Update ────────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestUpdateBudget_OK(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
client := srv.Client()
|
||||
|
||||
// Create first
|
||||
resp, err := client.Post(srv.URL+"/api/v1/budgets", "application/json",
|
||||
mustJSON(t, validBudget()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var created model.Budget
|
||||
json.NewDecoder(resp.Body).Decode(&created)
|
||||
resp.Body.Close()
|
||||
|
||||
// Update amount
|
||||
updated := validBudget()
|
||||
updated["amount"] = 9999.99
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPut,
|
||||
fmt.Sprintf("%s/api/v1/budgets/%d", srv.URL, created.ID),
|
||||
mustJSON(t, updated))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp2, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp2.Body.Close()
|
||||
|
||||
if resp2.StatusCode != http.StatusOK && resp2.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("update: got %d, want 200 or 204", resp2.StatusCode)
|
||||
}
|
||||
|
||||
if resp2.StatusCode == http.StatusOK {
|
||||
var got model.Budget
|
||||
json.NewDecoder(resp2.Body).Decode(&got)
|
||||
if got.Amount != 9999.99 {
|
||||
t.Errorf("updated amount: got %v, want 9999.99", got.Amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateBudget_NotFound(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPut, srv.URL+"/api/v1/budgets/9999",
|
||||
mustJSON(t, validBudget()))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := srv.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNotFound && resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("update non-existent: got %d, want 404 or 204", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateBudget_InvalidJSON(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
|
||||
// Create one first so the ID exists
|
||||
resp, _ := srv.Client().Post(srv.URL+"/api/v1/budgets", "application/json",
|
||||
mustJSON(t, validBudget()))
|
||||
var created model.Budget
|
||||
json.NewDecoder(resp.Body).Decode(&created)
|
||||
resp.Body.Close()
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPut,
|
||||
fmt.Sprintf("%s/api/v1/budgets/%d", srv.URL, created.ID),
|
||||
bytes.NewBufferString("not-json"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp2, err := srv.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2.Body.Close()
|
||||
if resp2.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("invalid JSON update: got %d, want 400", resp2.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Delete ────────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestDeleteBudget_OK(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
client := srv.Client()
|
||||
|
||||
resp, err := client.Post(srv.URL+"/api/v1/budgets", "application/json",
|
||||
mustJSON(t, validBudget()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var created model.Budget
|
||||
json.NewDecoder(resp.Body).Decode(&created)
|
||||
resp.Body.Close()
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete,
|
||||
srv.URL+"/api/v1/budgets/"+strconv.Itoa(created.ID), nil)
|
||||
resp2, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2.Body.Close()
|
||||
|
||||
if resp2.StatusCode != http.StatusNoContent && resp2.StatusCode != http.StatusOK {
|
||||
t.Errorf("delete: got %d, want 200 or 204", resp2.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBudget_NotFound(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete, srv.URL+"/api/v1/budgets/9999", nil)
|
||||
resp, err := srv.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNotFound && resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("delete non-existent: got %d, want 404 or 204", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBudget_DoubleDelete(t *testing.T) {
|
||||
srv := newBudgetServer(t)
|
||||
client := srv.Client()
|
||||
|
||||
resp, _ := client.Post(srv.URL+"/api/v1/budgets", "application/json",
|
||||
mustJSON(t, validBudget()))
|
||||
var created model.Budget
|
||||
json.NewDecoder(resp.Body).Decode(&created)
|
||||
resp.Body.Close()
|
||||
|
||||
url := srv.URL + "/api/v1/budgets/" + strconv.Itoa(created.ID)
|
||||
|
||||
req1, _ := http.NewRequest(http.MethodDelete, url, nil)
|
||||
resp1, _ := client.Do(req1)
|
||||
resp1.Body.Close()
|
||||
|
||||
// Second delete — should not panic, should return 404 or 204
|
||||
req2, _ := http.NewRequest(http.MethodDelete, url, nil)
|
||||
resp2, err := client.Do(req2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2.Body.Close()
|
||||
|
||||
if resp2.StatusCode != http.StatusNotFound && resp2.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("double delete: got %d, want 404 or 204", resp2.StatusCode)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user