Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Binary file added .DS_Store
Binary file not shown.
Binary file added .github/.DS_Store
Binary file not shown.
122 changes: 106 additions & 16 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,101 @@ name: Deploy WatchParty Stack
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
# ----------------------------------------------------------------
# JOB 1: INFRASTRUCTURE SANITY CHECK
# JOB 1: TEST GO BACKEND
# ----------------------------------------------------------------
test-go:
runs-on: ubuntu-latest
services:
redis:
image: redis
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache-dependency-path: backend-go/go.sum

- name: Start Pub/Sub Emulator
run: |
docker run -d -p 8085:8085 gcr.io/google.com/cloudsdktool/cloud-sdk:emulators gcloud beta emulators pubsub start --host-port=0.0.0.0:8085
sleep 5 # Give it a few seconds to boot up

- name: Run Go Tests
env:
PUBSUB_EMULATOR_HOST: localhost:8085
GCP_PROJECT_ID: test-project
run: |
cd backend-go
go mod tidy
go test -v ./...

# ----------------------------------------------------------------
# JOB 2: TEST PYTHON BACKEND
# ----------------------------------------------------------------
test-python:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_DB: watchparty
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: backend-python/requirements.txt

- name: Start Pub/Sub Emulator
run: |
docker run -d -p 8085:8085 gcr.io/google.com/cloudsdktool/cloud-sdk:emulators gcloud beta emulators pubsub start --host-port=0.0.0.0:8085
sleep 5

- name: Install dependencies
run: |
cd backend-python
pip install -r requirements.txt
pip install pytest pytest-mock responses

- name: Run Python Tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/watchparty
PYTHONPATH: .
PUBSUB_EMULATOR_HOST: localhost:8085
GCP_PROJECT_ID: test-project
run: |
cd backend-python
pytest tests -v

# ----------------------------------------------------------------
# JOB 3: INFRASTRUCTURE SANITY CHECK
# ----------------------------------------------------------------
infrastructure-check:
needs: [test-go, test-python]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
Expand All @@ -32,11 +120,15 @@ jobs:
echo "✅ Infrastructure verified."

# ----------------------------------------------------------------
# JOB 2: DEPLOY BACKENDS (Requires Infra Check)
# JOB 4: DEPLOY BACKENDS (Requires Tests & Infra Check)
# ----------------------------------------------------------------
deploy-backends:
needs: infrastructure-check
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
outputs:
python_url: ${{ steps.deploy-python.outputs.url }}
go_url: ${{ steps.deploy-go.outputs.url }}
Expand All @@ -50,30 +142,27 @@ jobs:
- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2

# 🚨 Authorize Docker to push to your new Artifact Registry
- name: Configure Docker
run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

# ---------------------------------------------------
# 🚀 FAST DEPLOY: PYTHON API
# ---------------------------------------------------
- name: Build and Push Python Image
run: |
cd backend-python
IMAGE="us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/watchparty-repo/watchparty-api:latest"
docker build -t $IMAGE .
docker push $IMAGE
docker build -t ghcr.io/${{ github.repository }}/watchparty-api:latest .
docker push ghcr.io/${{ github.repository }}/watchparty-api:latest

- name: Deploy Python to Cloud Run
id: deploy-python
uses: google-github-actions/deploy-cloudrun@v2
with:
service: watchparty-api
region: us-central1
# 🚨 Deploy the pre-built image instead of building from source
image: us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/watchparty-repo/watchparty-api:latest
image: ghcr.io/${{ github.repository }}/watchparty-api:latest
flags: |
--add-cloudsql-instances=watchparty-482106:us-central1:watchparty-db
--port=8080
env_vars: |
DATABASE_URL=${{ secrets.DATABASE_URL }}
Expand All @@ -87,29 +176,30 @@ jobs:
- name: Build and Push Go Image
run: |
cd backend-go
IMAGE="us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/watchparty-repo/watchparty-ws:latest"
docker build -t $IMAGE .
docker push $IMAGE
docker build -t ghcr.io/${{ github.repository }}/watchparty-ws:latest .
docker push ghcr.io/${{ github.repository }}/watchparty-ws:latest

- name: Deploy Go to Cloud Run
id: deploy-go
uses: google-github-actions/deploy-cloudrun@v2
with:
service: watchparty-ws
region: us-central1
# 🚨 Deploy the pre-built image
image: us-central1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/watchparty-repo/watchparty-ws:latest
image: ghcr.io/${{ github.repository }}/watchparty-ws:latest
flags: |
--timeout=3600
env_vars: |
REDIS_ADDR=${{ secrets.REDIS_ADDR }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
GCP_PROJECT_ID=${{ secrets.GCP_PROJECT_ID }}

# ----------------------------------------------------------------
# JOB 3: DEPLOY FRONTEND
# JOB 5: DEPLOY FRONTEND
# ----------------------------------------------------------------
deploy-frontend:
needs: deploy-backends
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build React
Expand Down
Binary file added backend-go/.DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions backend-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/go-chi/chi/v5 v5.2.5
github.com/go-chi/cors v1.2.2
github.com/go-playground/validator/v10 v10.30.1
github.com/go-redis/redismock/v9 v9.2.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
Expand Down Expand Up @@ -35,6 +36,7 @@ require (
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
Expand Down
14 changes: 14 additions & 0 deletions backend-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
Expand All @@ -57,6 +59,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw=
github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
Expand Down Expand Up @@ -102,6 +106,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y=
github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
Expand All @@ -112,6 +122,8 @@ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
Expand Down Expand Up @@ -217,6 +229,8 @@ google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
101 changes: 101 additions & 0 deletions backend-go/internal/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package api_test

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"wpbe/internal/api"
"wpbe/internal/domain"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

// MockRoomManager implements domain.RoomManager
type MockRoomManager struct {
mock.Mock
}

func (m *MockRoomManager) RegisterLocalClient(roomID string, client *domain.Client) {
m.Called(roomID, client)
}

func (m *MockRoomManager) RemoveLocalClient(roomID string, client *domain.Client) {
m.Called(roomID, client)
}

func (m *MockRoomManager) HandleVideoCommand(ctx context.Context, roomID string, msg domain.Message) error {
args := m.Called(ctx, roomID, msg)
return args.Error(0)
}

func (m *MockRoomManager) PublishDirectEvent(ctx context.Context, msg domain.Message) error {
args := m.Called(ctx, msg)
return args.Error(0)
}

func (m *MockRoomManager) JoinRoom(ctx context.Context, roomID string, action string, client *domain.Client) error {
args := m.Called(ctx, roomID, action, client)
return args.Error(0)
}

func (m *MockRoomManager) LeaveRoom(ctx context.Context, roomID string, client *domain.Client) {
m.Called(ctx, roomID, client)
}

func (m *MockRoomManager) GetActiveRooms(ctx context.Context) ([]domain.RoomSummary, error) {
args := m.Called(ctx)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.RoomSummary), args.Error(1)
}

func (m *MockRoomManager) HandleChangeVideo(ctx context.Context, roomID string, msg domain.Message) error {
args := m.Called(ctx, roomID, msg)
return args.Error(0)
}

func (m *MockRoomManager) HandleHostChange(ctx context.Context, roomID string, msg domain.Message) error {
args := m.Called(ctx, roomID, msg)
return args.Error(0)
}

func TestServer_HealthCheck(t *testing.T) {
mockManager := new(MockRoomManager)
server := api.NewServer(mockManager)

req, _ := http.NewRequest("GET", "/health", nil)
rr := httptest.NewRecorder()

server.Router.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `{"status":"healthy"}`, rr.Body.String())
}

func TestServer_GetRooms(t *testing.T) {
mockManager := new(MockRoomManager)
server := api.NewServer(mockManager)

rooms := []domain.RoomSummary{
{Name: "room1", Count: 5},
}
mockManager.On("GetActiveRooms", mock.Anything).Return(rooms, nil)

req, _ := http.NewRequest("GET", "/rooms", nil)
rr := httptest.NewRecorder()

server.Router.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)

var response []domain.RoomSummary
err := json.Unmarshal(rr.Body.Bytes(), &response)
assert.NoError(t, err)
assert.Equal(t, 1, len(response))
assert.Equal(t, "room1", response[0].Name)
assert.Equal(t, 5, response[0].Count)
}
6 changes: 3 additions & 3 deletions backend-go/internal/repository/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ func NewRedisRepo(addr string) (*RedisRepo, error) {
return nil, fmt.Errorf("failed to parse upstash url: %v", err)
}

// Explicitly set TLS for Upstash compatibility
opt.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
// Explicitly set TLS for Upstash compatibility if using rediss://
if opt.TLSConfig != nil {
opt.TLSConfig.MinVersion = tls.VersionTLS12
}

client := redis.NewClient(opt)
Expand Down
Loading
Loading