Skip to content

feat: broadcast Item CRUD events over WebSocket#12

Merged
omattsson merged 2 commits intomainfrom
feat/websocket-item-broadcast
Mar 14, 2026
Merged

feat: broadcast Item CRUD events over WebSocket#12
omattsson merged 2 commits intomainfrom
feat/websocket-item-broadcast

Conversation

@omattsson
Copy link
Owner

Summary

Implements WebSocket broadcast for Item CRUD events (issue #5). After every successful create, update, or delete, the handler emits a JSON event to all connected WebSocket clients:

{"event": "item.created", "data": {...}}
{"event": "item.updated", "data": {...}}
{"event": "item.deleted", "data": {...}}

Changes

  • handlers/items.go — Added BroadcastSender interface and broadcastSender field to Handler; added NewHandlerWithHub constructor for hub injection; added broadcast calls after successful Create, Update, and Delete operations
  • handlers/mock_broadcast_sender.go (new) — Thread-safe MockBroadcastSender test double that records all messages for assertion in unit tests
  • handlers/mock_repository.go — Extended mock to support the new broadcast test scenarios
  • **handlers/items- **handlers/items- **handlers/items- **handlers/itapp- **handlers/items- **handlers/items- **handlers/itemsep- **handlers/itn err- **handlers/items- **handlers/items- **handlers/items- **handlers/itapp- **handlers/items- **handlers/items- **handlers/itemsep- **handlers/itn err- **`handlers/-v -short

Closes #5

Handler gains a broadcastSender interface field and NewHandlerWithHub
constructor so a websocket hub can be injected without breaking existing
callers that use NewHandler.

Broadcast calls are made after every successful Create, Update, and
Delete operation, emitting JSON events of the form:
  {"event":"item.created","data":{...}}

MockBroadcastSender (mock_broadcast_sender.go) provides a thread-safe
test double that records all messages for assertion.

MockRepository and items_test.go are extended with full coverage of the
broadcast paths, including error-path tests that verify no broadcast
occurs when the repository returns an error.

Refs #5
Copilot AI review requested due to automatic review settings March 14, 2026 19:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds WebSocket event broadcasting to the Items CRUD endpoints so connected clients can react to creates/updates/deletes in real time, and extends unit tests/mocks to validate this behavior.

Changes:

  • Inject WebSocket hub into the items handler and broadcast item.created|updated|deleted events on successful mutations.
  • Add a broadcast sender test double and expand handler tests to assert event type + payload.
  • Extend MockRepository to simulate update-specific failures.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
backend/internal/api/routes/routes.go Wires items handler with the WebSocket hub for broadcasting.
backend/internal/api/handlers/items.go Adds hub-enabled handler constructor + broadcast helper; emits events on CRUD mutations.
backend/internal/api/handlers/mock_broadcast_sender.go New mock hub/broadcast sender for unit tests.
backend/internal/api/handlers/mock_repository.go Adds updateError injection to simulate update failures in tests.
backend/internal/api/handlers/items_test.go Adds tests for broadcasts (happy paths, nil hub, and error paths) + new router setup helper.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +25 to +26
result := make([][]byte, len(m.messages))
copy(result, m.messages)
Comment on lines +17 to +31
// broadcastSender broadcasts serialised WebSocket messages to all connected clients.
type broadcastSender interface {
Broadcast(message []byte)
}

type Handler struct {
repository models.Repository
hub broadcastSender
}

func NewHandler(repository models.Repository) *Handler {
return &Handler{repository: repository}
}

func NewHandlerWithHub(repository models.Repository, hub broadcastSender) *Handler {
Comment on lines +936 to +955
// setupTestRouterWithHub creates a test router wired with the given BroadcastSender.
func setupTestRouterWithHub(hub *MockBroadcastSender) (*gin.Engine, *MockRepository) {
gin.SetMode(gin.TestMode)
router := gin.Default()
mockRepo := NewMockRepository()
handler := NewHandlerWithHub(mockRepo, hub)

rateLimiter := NewRateLimiter(30, time.Second)

items := router.Group("/api/v1/items")
items.Use(rateLimiter.RateLimit())
{
items.GET("", handler.GetItems)
items.GET("/:id", handler.GetItem)
items.POST("", handler.CreateItem)
items.PUT("/:id", handler.UpdateItem)
items.DELETE("/:id", handler.DeleteItem)
}

return router, mockRepo
@omattsson omattsson merged commit 3de6444 into main Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants