molecule-core/workspace-server/internal/handlers/events_test.go
Hongming Wang 479a027e4b chore: open-source restructure — rename dirs, remove internal files, scrub secrets
Renames:
- platform/ → workspace-server/ (Go module path stays as "platform" for
  external dep compat — will update after plugin module republish)
- workspace-template/ → workspace/

Removed (moved to separate repos or deleted):
- PLAN.md — internal roadmap (move to private project board)
- HANDOFF.md, AGENTS.md — one-time internal session docs
- .claude/ — gitignored entirely (local agent config)
- infra/cloudflare-worker/ → Molecule-AI/molecule-tenant-proxy
- org-templates/molecule-dev/ → standalone template repo
- .mcp-eval/ → molecule-mcp-server repo
- test-results/ — ephemeral, gitignored

Security scrubbing:
- Cloudflare account/zone/KV IDs → placeholders
- Real EC2 IPs → <EC2_IP> in all docs
- CF token prefix, Neon project ID, Fly app names → redacted
- Langfuse dev credentials → parameterized
- Personal runner username/machine name → generic

Community files:
- CONTRIBUTING.md — build, test, branch conventions
- CODE_OF_CONDUCT.md — Contributor Covenant 2.1

All Dockerfiles, CI workflows, docker-compose, railway.toml, render.yaml,
README, CLAUDE.md updated for new directory names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:24:44 -07:00

159 lines
4.2 KiB
Go

package handlers
import (
"database/sql"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/gin-gonic/gin"
)
// ==================== GET /events (List) ====================
func TestEventsList_Success(t *testing.T) {
mock := setupTestDB(t)
setupTestRedis(t)
handler := NewEventsHandler()
now := time.Now()
wsID := "ws-evt-1"
rows := sqlmock.NewRows([]string{"id", "event_type", "workspace_id", "payload", "created_at"}).
AddRow("evt-1", "WORKSPACE_ONLINE", &wsID, []byte(`{"name":"Agent A"}`), now).
AddRow("evt-2", "WORKSPACE_OFFLINE", &wsID, []byte(`{"name":"Agent A"}`), now)
mock.ExpectQuery("SELECT id, event_type, workspace_id, payload, created_at").
WillReturnRows(rows)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest("GET", "/events", nil)
handler.List(c)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d: %s", w.Code, w.Body.String())
}
var resp []map[string]interface{}
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
if len(resp) != 2 {
t.Errorf("expected 2 events, got %d", len(resp))
}
if resp[0]["event_type"] != "WORKSPACE_ONLINE" {
t.Errorf("expected event_type 'WORKSPACE_ONLINE', got %v", resp[0]["event_type"])
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unmet sqlmock expectations: %v", err)
}
}
func TestEventsList_Empty(t *testing.T) {
mock := setupTestDB(t)
setupTestRedis(t)
handler := NewEventsHandler()
mock.ExpectQuery("SELECT id, event_type, workspace_id, payload, created_at").
WillReturnRows(sqlmock.NewRows([]string{"id", "event_type", "workspace_id", "payload", "created_at"}))
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest("GET", "/events", nil)
handler.List(c)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d", w.Code)
}
var resp []map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &resp)
if len(resp) != 0 {
t.Errorf("expected empty list, got %d events", len(resp))
}
}
func TestEventsList_DBError(t *testing.T) {
mock := setupTestDB(t)
setupTestRedis(t)
handler := NewEventsHandler()
mock.ExpectQuery("SELECT id, event_type, workspace_id, payload, created_at").
WillReturnError(sql.ErrConnDone)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = httptest.NewRequest("GET", "/events", nil)
handler.List(c)
if w.Code != http.StatusInternalServerError {
t.Errorf("expected 500, got %d", w.Code)
}
}
// ==================== GET /events/:workspaceId (ListByWorkspace) ====================
func TestEventsListByWorkspace_Success(t *testing.T) {
mock := setupTestDB(t)
setupTestRedis(t)
handler := NewEventsHandler()
now := time.Now()
wsID := "ws-by-ws"
rows := sqlmock.NewRows([]string{"id", "event_type", "workspace_id", "payload", "created_at"}).
AddRow("evt-3", "WORKSPACE_PROVISIONING", &wsID, []byte(`{"name":"Agent B"}`), now)
mock.ExpectQuery("SELECT id, event_type, workspace_id, payload, created_at").
WithArgs("ws-by-ws").
WillReturnRows(rows)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = gin.Params{{Key: "workspaceId", Value: "ws-by-ws"}}
c.Request = httptest.NewRequest("GET", "/events/ws-by-ws", nil)
handler.ListByWorkspace(c)
if w.Code != http.StatusOK {
t.Errorf("expected 200, got %d: %s", w.Code, w.Body.String())
}
var resp []map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &resp)
if len(resp) != 1 {
t.Errorf("expected 1 event, got %d", len(resp))
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unmet sqlmock expectations: %v", err)
}
}
func TestEventsListByWorkspace_DBError(t *testing.T) {
mock := setupTestDB(t)
setupTestRedis(t)
handler := NewEventsHandler()
mock.ExpectQuery("SELECT id, event_type, workspace_id, payload, created_at").
WithArgs("ws-err").
WillReturnError(sql.ErrConnDone)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Params = gin.Params{{Key: "workspaceId", Value: "ws-err"}}
c.Request = httptest.NewRequest("GET", "/events/ws-err", nil)
handler.ListByWorkspace(c)
if w.Code != http.StatusInternalServerError {
t.Errorf("expected 500, got %d", w.Code)
}
}