Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb8ccc0784 |
@@ -0,0 +1,272 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/Molecule-AI/molecule-monorepo/platform/internal/db"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// setupAbilitiesDB creates a sqlmock with QueryMatcherEqual (quoted literals
|
||||
// are not used by PatchAbilities but using the same pattern as
|
||||
// workspace_broadcast_test.go keeps conventions consistent).
|
||||
func setupAbilitiesDB(t *testing.T) sqlmock.Sqlmock {
|
||||
t.Helper()
|
||||
mockDB, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create sqlmock: %v", err)
|
||||
}
|
||||
prevDB := db.DB
|
||||
db.DB = mockDB
|
||||
t.Cleanup(func() { db.DB = prevDB; mockDB.Close() })
|
||||
return mock
|
||||
}
|
||||
|
||||
// buildAbilitiesCtx creates a gin.Context wired for PATCH /workspaces/:id/abilities.
|
||||
func buildAbilitiesCtx(id, body string) (*gin.Context, *httptest.ResponseRecorder) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
req := httptest.NewRequest(http.MethodPatch, "/workspaces/"+id+"/abilities", bytes.NewBufferString(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
c.Request = req.WithContext(context.Background())
|
||||
c.Params = gin.Params{{Key: "id", Value: id}}
|
||||
return c, w
|
||||
}
|
||||
|
||||
// ─── Validation ────────────────────────────────────────────────────────────────
|
||||
|
||||
func TestPatchAbilities_InvalidWorkspaceID(t *testing.T) {
|
||||
c, w := buildAbilitiesCtx("not-a-uuid", `{"broadcast_enabled":true}`)
|
||||
PatchAbilities(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("want 400, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_InvalidJSON(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `not json`)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("want 400, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_EmptyBody(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{}`)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("want 400, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
// No DB queries should fire for an empty-body rejection.
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_BothFieldsNil(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"other_field":true}`)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("want 400, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Workspace not found ────────────────────────────────────────────────────────
|
||||
|
||||
func TestPatchAbilities_WorkspaceNotFound(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":true}`)
|
||||
|
||||
// Workspace lookup returns exists=false.
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(false))
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("want 404, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_WorkspaceLookupQueryError(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":true}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Errorf("want 404, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Update errors ─────────────────────────────────────────────────────────────
|
||||
|
||||
func TestPatchAbilities_BroadcastUpdateError(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":true}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET broadcast_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", true).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("want 500, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_TalkToUserUpdateError(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"talk_to_user_enabled":false}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET talk_to_user_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", false).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("want 500, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Success paths ─────────────────────────────────────────────────────────────
|
||||
|
||||
func TestPatchAbilities_BroadcastEnabledTrue(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":true}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET broadcast_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", true).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("want 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_BroadcastEnabledFalse(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":false}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET broadcast_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", false).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("want 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_TalkToUserEnabled(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"talk_to_user_enabled":true}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET talk_to_user_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", true).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("want 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchAbilities_BothFields(t *testing.T) {
|
||||
mock := setupAbilitiesDB(t)
|
||||
c, w := buildAbilitiesCtx("bbbbbbbb-0001-0001-0001-000000000001", `{"broadcast_enabled":true,"talk_to_user_enabled":false}`)
|
||||
|
||||
mock.ExpectQuery("SELECT EXISTS(SELECT 1 FROM workspaces WHERE id = $1 AND status != 'removed')").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET broadcast_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", true).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
mock.ExpectExec("UPDATE workspaces SET talk_to_user_enabled = $2, updated_at = now() WHERE id = $1").
|
||||
WithArgs("bbbbbbbb-0001-0001-0001-000000000001", false).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
PatchAbilities(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("want 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet mock expectations: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user