2026-07-14 11:46:06 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2026-07-14 12:41:44 +00:00
|
|
|
"database/sql"
|
2026-07-14 11:46:06 +00:00
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
2026-07-14 12:41:44 +00:00
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
|
"github.com/google/uuid"
|
2026-07-14 11:46:06 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
func setupCRMHandler(t *testing.T) (*CRMHandler, sqlmock.Sqlmock, *sql.DB) {
|
2026-07-14 11:46:06 +00:00
|
|
|
db, mock, err := sqlmock.New()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
handler := NewCRMHandler(db)
|
2026-07-14 12:41:44 +00:00
|
|
|
return handler, mock, db
|
2026-07-14 11:46:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCRMHandler_CreateCustomer(t *testing.T) {
|
2026-07-14 12:41:44 +00:00
|
|
|
handler, mock, db := setupCRMHandler(t)
|
2026-07-14 11:46:06 +00:00
|
|
|
defer db.Close()
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
// Expect INSERT — matches actual handler SQL
|
2026-07-14 11:46:06 +00:00
|
|
|
mock.ExpectQuery("INSERT INTO boc_customers").
|
2026-07-14 12:41:44 +00:00
|
|
|
WithArgs("Test Corp", "contact@test.com", "+1234567890", "", "", "lead", "", sqlmock.AnyArg()).
|
|
|
|
|
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
|
|
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
|
"name": "Test Corp",
|
|
|
|
|
"email": "contact@test.com",
|
|
|
|
|
"phone": "+1234567890",
|
|
|
|
|
"status": "lead",
|
2026-07-14 11:46:06 +00:00
|
|
|
}
|
2026-07-14 12:41:44 +00:00
|
|
|
jsonBody, _ := json.Marshal(body)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/customers", bytes.NewReader(jsonBody))
|
2026-07-14 11:46:06 +00:00
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
rr := httptest.NewRecorder()
|
2026-07-14 11:46:06 +00:00
|
|
|
handler.CreateCustomer(rr, req)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusCreated, rr.Code)
|
|
|
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCRMHandler_GetCustomer(t *testing.T) {
|
2026-07-14 12:41:44 +00:00
|
|
|
handler, mock, db := setupCRMHandler(t)
|
2026-07-14 11:46:06 +00:00
|
|
|
defer db.Close()
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
customerID := uuid.New().String()
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
// Expect SELECT — matches actual handler SQL with pq.StringArray
|
|
|
|
|
mock.ExpectQuery("SELECT (.+) FROM boc_customers WHERE id = \\$(.+)").
|
|
|
|
|
WithArgs(customerID).
|
2026-07-14 11:46:06 +00:00
|
|
|
WillReturnRows(sqlmock.NewRows([]string{
|
2026-07-14 12:41:44 +00:00
|
|
|
"id", "name", "email", "phone", "company", "org_number",
|
|
|
|
|
"status", "source", "tags", "assigned_to", "created_at", "updated_at",
|
2026-07-14 11:46:06 +00:00
|
|
|
}).AddRow(
|
2026-07-14 12:41:44 +00:00
|
|
|
customerID, "Test Corp", "test@test.com", "+1234567890",
|
|
|
|
|
"", "", "active", "", "{tag1,tag2}", nil, time.Now(), time.Now(),
|
2026-07-14 11:46:06 +00:00
|
|
|
))
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
// Use chi router to set URL param
|
|
|
|
|
r := chi.NewRouter()
|
|
|
|
|
r.Get("/api/customers/{id}", handler.GetCustomer)
|
|
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/customers/"+customerID, nil)
|
|
|
|
|
|
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
r.ServeHTTP(rr, req)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
2026-07-14 11:46:06 +00:00
|
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
func TestCRMHandler_GetCustomer_NotFound(t *testing.T) {
|
|
|
|
|
handler, mock, db := setupCRMHandler(t)
|
2026-07-14 11:46:06 +00:00
|
|
|
defer db.Close()
|
|
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
customerID := uuid.New().String()
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
mock.ExpectQuery("SELECT (.+) FROM boc_customers WHERE id = \\$(.+)").
|
|
|
|
|
WithArgs(customerID).
|
|
|
|
|
WillReturnError(sql.ErrNoRows)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
r := chi.NewRouter()
|
|
|
|
|
r.Get("/api/customers/{id}", handler.GetCustomer)
|
|
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/customers/"+customerID, nil)
|
2026-07-14 11:46:06 +00:00
|
|
|
rr := httptest.NewRecorder()
|
2026-07-14 12:41:44 +00:00
|
|
|
r.ServeHTTP(rr, req)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusNotFound, rr.Code)
|
|
|
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCRMHandler_UpdateCustomer(t *testing.T) {
|
|
|
|
|
handler, mock, db := setupCRMHandler(t)
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
customerID := uuid.New().String()
|
|
|
|
|
|
|
|
|
|
// Expect UPDATE — matches actual handler SQL (10 args)
|
|
|
|
|
// Use AnyArg for pq.Array since nil slice encoding varies
|
|
|
|
|
mock.ExpectExec("UPDATE boc_customers SET").
|
|
|
|
|
WithArgs(sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
|
|
|
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
|
|
|
|
|
|
body := map[string]interface{}{
|
|
|
|
|
"name": "Updated Corp",
|
|
|
|
|
"email": "updated@test.com",
|
|
|
|
|
}
|
|
|
|
|
jsonBody, _ := json.Marshal(body)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
// Use chi router
|
|
|
|
|
r := chi.NewRouter()
|
|
|
|
|
r.Put("/api/customers/{id}", handler.UpdateCustomer)
|
|
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodPut, "/api/customers/"+customerID, bytes.NewReader(jsonBody))
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
r.ServeHTTP(rr, req)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
2026-07-14 12:41:44 +00:00
|
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
|
|
|
}
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
func TestCRMHandler_DeleteCustomer(t *testing.T) {
|
|
|
|
|
handler, mock, db := setupCRMHandler(t)
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
customerID := uuid.New().String()
|
|
|
|
|
|
|
|
|
|
// Expect DELETE
|
|
|
|
|
mock.ExpectExec("DELETE FROM boc_customers WHERE id = \\$(.+)").
|
|
|
|
|
WithArgs(customerID).
|
|
|
|
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
|
|
|
|
|
|
// Use chi router
|
|
|
|
|
r := chi.NewRouter()
|
|
|
|
|
r.Delete("/api/customers/{id}", handler.DeleteCustomer)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/customers/"+customerID, nil)
|
2026-07-14 11:46:06 +00:00
|
|
|
|
2026-07-14 12:41:44 +00:00
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
|
r.ServeHTTP(rr, req)
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, http.StatusOK, rr.Code)
|
2026-07-14 11:46:06 +00:00
|
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
|
|
|
}
|