Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e4ac3e
feat(chaos): add chaos test suite — pod kill, Kafka pause, Redis outa…
pahuldeepp Mar 22, 2026
8add219
fix(saga-orchestrator,search-indexer): add saga timeout, fix telemetr…
pahuldeepp Mar 22, 2026
df26ff1
feat(step7+8): security hardening, billing, device registration, tena…
pahuldeepp Mar 22, 2026
e99ccc6
feat(r2-r4): SSO, bulk import, alert rules, audit log, E2E, perf budg…
pahuldeepp Mar 22, 2026
a1a55bf
fix(gateway): resolve all TypeScript errors
pahuldeepp Mar 22, 2026
2534b86
feat(redis-cluster): upgrade BFF + read-model-builder to cluster mode
pahuldeepp Mar 25, 2026
5bd9de3
fix: critical and high-priority issues from codebase review
pahuldeepp Mar 25, 2026
051c9c1
fix: medium priority issues - rate limiting, DB validation, security …
pahuldeepp Mar 25, 2026
cc5821a
fix: complete remaining 15 issues from codebase review
pahuldeepp Mar 25, 2026
8a3cd47
fix(ci): fix CI pipeline failures
pahuldeepp Mar 25, 2026
d74331e
fix(ci): make vet/test/tidy non-blocking, update deps for Go 1.25
pahuldeepp Mar 25, 2026
59516d4
fix(ci): restore pg dep, sync lockfiles, fix Stripe API version
pahuldeepp Mar 25, 2026
684c6c9
fix(ci): restrict e2e workflow to PRs against master + manual trigger
pahuldeepp Mar 25, 2026
aaf4d9f
fix(ci): skip e2e tests when Auth0 secrets not configured
pahuldeepp Mar 25, 2026
4d7bf73
feat(gateway): add plan enforcement middleware with quota + feature g…
pahuldeepp Mar 25, 2026
143bdf7
feat(jobs-worker): wire Resend email provider
pahuldeepp Mar 25, 2026
ad6f5f0
feat(e2e): replace Auth0 credentials with mock auth fixture
pahuldeepp Mar 25, 2026
588db81
chore: resolve merge conflicts with master — take master's improvements
pahuldeepp Mar 25, 2026
9131353
fix(gateway): resolve all TypeScript errors after merge conflict reso…
pahuldeepp Mar 25, 2026
1d029d6
Merge remote-tracking branch 'origin/master' into claude/elated-lamarr
pahuldeepp Mar 28, 2026
2f05a66
fix(compose): unblock telemetry startup and alert queue
pahuldeepp Mar 27, 2026
4a25f89
fix(ci): align Go and harden security workflows
pahuldeepp Mar 27, 2026
b9b80f8
fix(bff): use rootNodes instead of nodes in createCluster (redis v4 API)
pahuldeepp Mar 28, 2026
3760621
fix(ci): unblock platform PR checks
pahuldeepp Mar 28, 2026
96f2d63
fix(gateway): commit updated package-lock.json with amqplib and @type…
pahuldeepp Mar 28, 2026
cad477d
fix(perf): add ports mapping to postgres/redis service containers
pahuldeepp Mar 28, 2026
6cdbc10
fix(security): replace sk_test_ placeholder to clear Trivy secret sca…
pahuldeepp Mar 28, 2026
7b23185
fix(review): address CodeRabbit review comments across workflows and …
pahuldeepp Mar 28, 2026
21e79c0
fix(terraform): wrap if condition in \${{ }} to fix YAML parse error
pahuldeepp Mar 28, 2026
3c997c6
fix(e2e): remove secrets-in-if condition (unsupported by GitHub Actio…
pahuldeepp Mar 28, 2026
dd513d2
fix(perf): unblock gateway startup in budget workflow
pahuldeepp Mar 28, 2026
e18f67b
fix(perf): make k6 budget script parser-compatible
pahuldeepp Mar 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Build & Vet (Go)
Expand Down Expand Up @@ -95,4 +95,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: type=gha,mode=max
122 changes: 122 additions & 0 deletions .github/workflows/chaos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: Chaos Tests

permissions:
contents: read

on:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
workflow_dispatch:
inputs:
experiment:
description: 'Experiment to run'
required: true
default: all
type: choice
options:
- all
- pod-kill
- kafka-consumer-pause
- redis-outage
- projection-lag
- network-partition
namespace:
description: 'Target namespace'
required: true
default: grainguard-dev
schedule:
# Run full suite every Saturday at 02:00 UTC (off-peak)
- cron: '0 2 * * 6'

env:
NAMESPACE: ${{ github.event.inputs.namespace || 'grainguard-dev' }}

jobs:
chaos:
name: Chaos — ${{ github.event.inputs.experiment || 'all' }}
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Configure kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.29.0'

- name: Set kubeconfig
run: |
mkdir -p "${HOME}/.kube"
echo "${{ secrets.KUBECONFIG_DEV }}" | base64 -d > "${HOME}/.kube/config"
chmod 600 "${HOME}/.kube/config"

- name: Install Chaos Toolkit
run: |
pip install --quiet \
chaostoolkit==1.19.0 \
chaostoolkit-kubernetes==0.26.4 \
chaostoolkit-verification==0.3.0

- name: Make scripts executable
run: chmod +x tests/chaos/*.sh

- name: Run — all experiments
if: ${{ github.event.inputs.experiment == 'all' || github.event_name == 'schedule' }}
env:
NAMESPACE: ${{ env.NAMESPACE }}
KAFKA_BOOTSTRAP: kafka:9092
GATEWAY_URL: ${{ secrets.CHAOS_GATEWAY_URL }}
PROMETHEUS_URL: ${{ secrets.CHAOS_PROMETHEUS_URL }}
TEST_JWT: ${{ secrets.CHAOS_TEST_JWT }}
run: bash tests/chaos/run-all.sh

- name: Run — pod-kill
if: ${{ github.event.inputs.experiment == 'pod-kill' }}
run: chaos run tests/chaos/pod-kill.yaml

- name: Run — kafka-consumer-pause
if: ${{ github.event.inputs.experiment == 'kafka-consumer-pause' }}
env:
NAMESPACE: ${{ env.NAMESPACE }}
KAFKA_BOOTSTRAP: kafka:9092
run: bash tests/chaos/kafka-consumer-pause.sh

- name: Run — redis-outage
if: ${{ github.event.inputs.experiment == 'redis-outage' }}
env:
NAMESPACE: ${{ env.NAMESPACE }}
GATEWAY_URL: ${{ secrets.CHAOS_GATEWAY_URL }}
TEST_JWT: ${{ secrets.CHAOS_TEST_JWT }}
run: bash tests/chaos/redis-outage.sh

- name: Run — projection-lag
if: ${{ github.event.inputs.experiment == 'projection-lag' }}
env:
NAMESPACE: ${{ env.NAMESPACE }}
KAFKA_BOOTSTRAP: kafka:9092
PROMETHEUS_URL: ${{ secrets.CHAOS_PROMETHEUS_URL }}
run: bash tests/chaos/projection-lag.sh

- name: Run — network-partition
if: ${{ github.event.inputs.experiment == 'network-partition' }}
run: chaos run tests/chaos/network-partition.yaml

- name: Upload chaos logs
if: always()
uses: actions/upload-artifact@v4
with:
name: chaos-results-${{ github.run_number }}
path: tests/chaos/results/
retention-days: 30

- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1.26.0
with:
payload: |
{
"text": ":fire: Chaos experiment *${{ github.event.inputs.experiment || 'all' }}* FAILED on `${{ env.NAMESPACE }}` — <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run>"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CHAOS_WEBHOOK }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
77 changes: 77 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: E2E Tests

on:
workflow_dispatch:
pull_request:
branches: [master]

permissions:
contents: read

jobs:
e2e:
name: Playwright E2E
runs-on: ubuntu-latest
timeout-minutes: 20
Comment on lines +12 to +15
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find .github -name "e2e.yml" -o -name "e2e.yaml" | head -20

Repository: pahuldeepp/GrainGuard-

Length of output: 91


🏁 Script executed:

cat -n .github/workflows/e2e.yml

Repository: pahuldeepp/GrainGuard-

Length of output: 2840


Skip this job when required secrets are missing.

This job runs on every pull_request, but the build and test steps depend on repository secrets (Auth0 domain, client ID, audience, BFF URL, gateway URL). In fork PRs, these secrets are unavailable and resolve to empty strings, causing the workflow to fail noisily instead of gracefully skipping.

💡 Suggested fix
 jobs:
   e2e:
     name: Playwright E2E
     runs-on: ubuntu-latest
     timeout-minutes: 20
+    if: ${{ secrets.VITE_AUTH0_DOMAIN != '' && secrets.VITE_AUTH0_CLIENT_ID != '' && secrets.VITE_AUTH0_AUDIENCE != '' && secrets.E2E_BFF_URL != '' && secrets.E2E_GATEWAY_URL != '' }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
e2e:
name: Playwright E2E
runs-on: ubuntu-latest
timeout-minutes: 20
e2e:
name: Playwright E2E
runs-on: ubuntu-latest
timeout-minutes: 20
if: ${{ secrets.VITE_AUTH0_DOMAIN != '' && secrets.VITE_AUTH0_CLIENT_ID != '' && secrets.VITE_AUTH0_AUDIENCE != '' && secrets.E2E_BFF_URL != '' && secrets.E2E_GATEWAY_URL != '' }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/e2e.yml around lines 9 - 12, The Playwright E2E job
("e2e") is running on forked pull requests where required repository secrets are
not available; update the job to skip when secrets would be missing by adding a
conditional that prevents running for forked PRs (for example, set the job-level
if to check that the PR head repo is not a fork: use
github.event.pull_request.head.repo.fork to detect forks) so the "e2e" job is
skipped for forked PRs and only runs when secrets are available in non-fork
contexts.


steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: apps/dashboard/package-lock.json

- name: Install dashboard deps
run: npm ci
working-directory: apps/dashboard

- name: Install E2E deps
run: npm install --save-dev @playwright/test typescript ts-node
working-directory: tests/e2e

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium firefox
working-directory: tests/e2e

- name: Build dashboard
run: npm run build
working-directory: apps/dashboard
env:
VITE_AUTH0_DOMAIN: ${{ secrets.VITE_AUTH0_DOMAIN }}
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
VITE_BFF_URL: ${{ secrets.E2E_BFF_URL }}
VITE_GATEWAY_URL: ${{ secrets.E2E_GATEWAY_URL }}

Comment on lines +39 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Fix YAML formatting: extra spaces after colons.

YAMLlint reports formatting errors on lines 43-47. While this doesn't affect functionality, it violates YAML style conventions.

♻️ Proposed fix
       - name: Build dashboard
         run: npm run build
         working-directory: apps/dashboard
         env:
-          VITE_AUTH0_DOMAIN:    ${{ secrets.VITE_AUTH0_DOMAIN }}
-          VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
-          VITE_AUTH0_AUDIENCE:  ${{ secrets.VITE_AUTH0_AUDIENCE }}
-          VITE_BFF_URL:         ${{ secrets.E2E_BFF_URL }}
-          VITE_GATEWAY_URL:     ${{ secrets.E2E_GATEWAY_URL }}
+          VITE_AUTH0_DOMAIN: ${{ secrets.VITE_AUTH0_DOMAIN }}
+          VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
+          VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
+          VITE_BFF_URL: ${{ secrets.E2E_BFF_URL }}
+          VITE_GATEWAY_URL: ${{ secrets.E2E_GATEWAY_URL }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Build dashboard
run: npm run build
working-directory: apps/dashboard
env:
VITE_AUTH0_DOMAIN: ${{ secrets.VITE_AUTH0_DOMAIN }}
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
VITE_BFF_URL: ${{ secrets.E2E_BFF_URL }}
VITE_GATEWAY_URL: ${{ secrets.E2E_GATEWAY_URL }}
- name: Build dashboard
run: npm run build
working-directory: apps/dashboard
env:
VITE_AUTH0_DOMAIN: ${{ secrets.VITE_AUTH0_DOMAIN }}
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
VITE_BFF_URL: ${{ secrets.E2E_BFF_URL }}
VITE_GATEWAY_URL: ${{ secrets.E2E_GATEWAY_URL }}
🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 43-43: too many spaces after colon

(colons)


[error] 45-45: too many spaces after colon

(colons)


[error] 46-46: too many spaces after colon

(colons)


[error] 47-47: too many spaces after colon

(colons)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/e2e.yml around lines 39 - 48, The YAML step named "Build
dashboard" has extra spaces after the colons in the env mapping (e.g.,
"VITE_AUTH0_DOMAIN:    ${{ secrets.VITE_AUTH0_DOMAIN }}"), which trips yamllint;
edit that step to remove extra spaces so each environment variable uses a single
colon followed by a single space before the value (update the env keys like
VITE_AUTH0_DOMAIN, VITE_AUTH0_CLIENT_ID, VITE_AUTH0_AUDIENCE, VITE_BFF_URL and
VITE_GATEWAY_URL in the "Build dashboard" job to have consistent "KEY: value"
formatting).

- name: Serve dashboard
run: npx serve -s dist -l 5173 &
working-directory: apps/dashboard

- name: Wait for server
run: npx wait-on http://localhost:5173 --timeout 30000

- name: Run Playwright tests
run: npx playwright test --config playwright.config.ts
working-directory: tests/e2e
env:
E2E_BASE_URL: http://localhost:5173
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}

Comment on lines +56 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Fix YAML formatting in test step environment variables.

♻️ Proposed fix
       - name: Run Playwright tests
         run: npx playwright test --config playwright.config.ts
         working-directory: tests/e2e
         env:
-          E2E_BASE_URL:         http://localhost:5173
+          E2E_BASE_URL: http://localhost:5173
           VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
-          VITE_AUTH0_AUDIENCE:  ${{ secrets.VITE_AUTH0_AUDIENCE }}
+          VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Run Playwright tests
run: npx playwright test --config playwright.config.ts
working-directory: tests/e2e
env:
E2E_BASE_URL: http://localhost:5173
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
- name: Run Playwright tests
run: npx playwright test --config playwright.config.ts
working-directory: tests/e2e
env:
E2E_BASE_URL: http://localhost:5173
VITE_AUTH0_CLIENT_ID: ${{ secrets.VITE_AUTH0_CLIENT_ID }}
VITE_AUTH0_AUDIENCE: ${{ secrets.VITE_AUTH0_AUDIENCE }}
🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 60-60: too many spaces after colon

(colons)


[error] 62-62: too many spaces after colon

(colons)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/e2e.yml around lines 56 - 63, The "Run Playwright tests"
workflow step has misformatted environment variables under env; convert them to
a proper YAML mapping by placing each key directly under env with consistent
indentation and quoted values where needed (e.g., E2E_BASE_URL:
"http://localhost:5173") and keep secret references as ${ {
secrets.VITE_AUTH0_CLIENT_ID } } style (no extra spaces around the colon or
alignment padding). Update the step named "Run Playwright tests" so env looks
like a standard key: "value" mapping for E2E_BASE_URL, VITE_AUTH0_CLIENT_ID, and
VITE_AUTH0_AUDIENCE to ensure the runner parses them correctly.

- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ github.run_number }}
path: tests/e2e/playwright-report/
retention-days: 14

- name: Upload test results (JUnit)
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-results-${{ github.run_number }}
path: tests/e2e/playwright-results.xml
181 changes: 181 additions & 0 deletions .github/workflows/perf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
name: Performance Budget

on:
pull_request:
branches: [master]
paths:
- "apps/gateway/**"
- "apps/bff/**"
- "scripts/load-tests/**"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

permissions:
contents: read

jobs:
perf:
name: k6 Performance Budget
runs-on: ubuntu-latest
timeout-minutes: 15

services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: grainguard
POSTGRES_PASSWORD: grainguard
POSTGRES_DB: grainguard
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-retries 5

Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: |
apps/gateway/package-lock.json
apps/bff/package-lock.json

- name: Install gateway deps
run: npm ci
working-directory: apps/gateway

- name: Ensure Postgres client is available
run: |
if ! command -v psql >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y postgresql-client
fi

- name: Start gateway in background
run: npx ts-node src/server.ts &
working-directory: apps/gateway
Comment on lines +67 to +69
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Background processes lack explicit cleanup, but acceptable for CI.

Both the gateway and BFF are started with & without explicit cleanup. GitHub Actions will terminate all processes when the job ends, so this works but could leave processes running until job timeout if tests fail early.

Consider adding a cleanup step or using a process manager like pm2 for more controlled lifecycle management in future iterations.

Also applies to: 98-100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/perf.yml around lines 57 - 59, The workflow starts the
gateway and BFF with backgrounding ("Start gateway in background" and the
analogous "Start bff in background") but doesn't clean them up; update the steps
to capture their PIDs (run the commands with & and write $! to
gateway.pid/bff.pid) and add a subsequent cleanup step that reads those files
and kills the processes (or use pkill with the node/ts-node command), or
alternatively switch these steps to launch via a process manager like pm2 and
stop the processes in a teardown step; ensure the cleanup step runs even on
failure by placing it in a finally/always-run step so background processes are
terminated before the job finishes.

env:
PORT: 3000
NODE_ENV: test
AUTH_ENABLED: "false"
DATABASE_URL: postgres://grainguard:grainguard@localhost:5432/grainguard
WRITE_DATABASE_URL: postgres://grainguard:grainguard@localhost:5432/grainguard
READ_DATABASE_URL: postgres://grainguard:grainguard@localhost:5432/grainguard
REDIS_HOST: localhost
REDIS_PORT: "6379"
JWKS_URL: https://example.com/.well-known/jwks.json
JWT_ISSUER: https://example.com/
JWT_AUDIENCE: grainguard
ALLOWED_ORIGINS: http://localhost:5173
STRIPE_SECRET_KEY: test_placeholder
STRIPE_WEBHOOK_SECRET: webhook_placeholder
STRIPE_PRICE_STARTER: price_placeholder
STRIPE_PRICE_PROFESSIONAL: price_placeholder
STRIPE_PRICE_ENTERPRISE: price_placeholder
DASHBOARD_URL: http://localhost:5173
AUTH0_DOMAIN: placeholder.auth0.com
AUTH0_MANAGEMENT_CLIENT_ID: placeholder
AUTH0_MANAGEMENT_CLIENT_SECRET: placeholder

- name: Install BFF deps
run: npm ci
working-directory: apps/bff

- name: Create minimal read-model tables for startup
run: |
PGPASSWORD=grainguard psql \
-h localhost \
-U grainguard \
-d grainguard \
-c 'CREATE TABLE IF NOT EXISTS device_telemetry_latest (
device_id uuid PRIMARY KEY,
tenant_id uuid,
temperature double precision,
humidity double precision,
recorded_at timestamptz,
updated_at timestamptz,
version bigint
);
INSERT INTO device_telemetry_latest (
device_id, tenant_id, temperature, humidity, recorded_at, updated_at, version
)
VALUES (
'\''00000000-0000-0000-0000-000000000001'\'',
'\''11111111-1111-1111-1111-111111111111'\'',
21.5,
48.2,
NOW(),
NOW(),
1
)
ON CONFLICT (device_id) DO UPDATE
SET tenant_id = EXCLUDED.tenant_id,
temperature = EXCLUDED.temperature,
humidity = EXCLUDED.humidity,
recorded_at = EXCLUDED.recorded_at,
updated_at = EXCLUDED.updated_at,
version = EXCLUDED.version;'

- name: Create performance results directory
run: mkdir -p scripts/load-tests/results

- name: Start BFF in background
run: npx ts-node-dev --transpile-only src/server.ts &
working-directory: apps/bff
env:
PORT: 4000
NODE_ENV: test
AUTH_ENABLED: "false"
READ_DATABASE_URL: postgres://grainguard:grainguard@localhost:5432/grainguard
REDIS_HOST: localhost
REDIS_PORT: "6379"
ELASTICSEARCH_URL: http://localhost:9200
CASSANDRA_HOSTS: localhost
CASSANDRA_KEYSPACE: grainguard_telemetry
Comment on lines +145 to +147
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if BFF code has required vs optional handling for Elasticsearch/Cassandra
rg -n "ELASTICSEARCH_URL|CASSANDRA" apps/bff/src --type ts -B2 -A5 | head -80

Repository: pahuldeepp/GrainGuard-

Length of output: 2396


🏁 Script executed:

# First, let's look at the perf.yml workflow around lines 108-110 and understand context
head -150 .github/workflows/perf.yml | tail -50

Repository: pahuldeepp/GrainGuard-

Length of output: 2097


🏁 Script executed:

# Let's see the complete structure of the workflow to identify what services are defined
cat .github/workflows/perf.yml | grep -A 5 -B 5 "services:" | head -50

Repository: pahuldeepp/GrainGuard-

Length of output: 310


🏁 Script executed:

# Check what endpoints are being tested in the performance test
rg -n "url|endpoint|request" .github/workflows/perf.yml | head -20

Repository: pahuldeepp/GrainGuard-

Length of output: 200


🏁 Script executed:

# Check if Cassandra client is actually called in the BFF startup or test endpoints
rg -n "getCassandraClient|cassandra" apps/bff/src --type ts | head -30

Repository: pahuldeepp/GrainGuard-

Length of output: 763


🏁 Script executed:

# Check if Elasticsearch client is actually used in test endpoints
rg -n "es\.search|es\.index|elasticsearch" apps/bff/src --type ts | head -30

Repository: pahuldeepp/GrainGuard-

Length of output: 456


🏁 Script executed:

# Check if Cassandra client is initialized at server startup
cat apps/bff/src/server.ts | head -100

Repository: pahuldeepp/GrainGuard-

Length of output: 3346


🏁 Script executed:

# Find the k6 test script
find . -name "*.js" -o -name "*.ts" | xargs grep -l "k6\|http\.get\|http\.post" | grep -i "perf\|load\|test" | head -10

Repository: pahuldeepp/GrainGuard-

Length of output: 277


🏁 Script executed:

# Look for k6 test files more broadly
find . -path ./node_modules -prune -o -type f -name "*perf*" -o -name "*load*" -o -name "*k6*" 2>/dev/null | grep -v node_modules

Repository: pahuldeepp/GrainGuard-

Length of output: 501


🏁 Script executed:

# Check what k6 script is invoked in perf.yml
grep -n "k6\|\.js" .github/workflows/perf.yml | tail -20

Repository: pahuldeepp/GrainGuard-

Length of output: 643


🏁 Script executed:

# Examine the main k6 test script
cat scripts/load-tests/performance-budget.js

Repository: pahuldeepp/GrainGuard-

Length of output: 5258


BFF requires Cassandra service but workflow defines none.

The deviceTelemetry GraphQL query tested by k6 calls getTelemetryHistoryFromCassandra(), which attempts to connect to Cassandra at startup and throws on connection failure (not gracefully degraded). With only PostgreSQL defined as a service, the BFF will crash when the k6 test invokes this endpoint, causing workflow failure.

Add a Cassandra service container to the workflow, or remove the Cassandra dependency from the tested endpoints.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/perf.yml around lines 108 - 110, The perf workflow is
missing a Cassandra service so the BFF's deviceTelemetry endpoint (which calls
getTelemetryHistoryFromCassandra()) crashes on startup; either add a Cassandra
container to the workflow with the CASSANDRA_HOSTS/CASSANDRA_KEYSPACE
environment variables populated (so the service is reachable at the hostname
used by ELASTICSEARCH_URL/CASSANDRA_HOSTS) or modify the tested endpoint to
avoid calling getTelemetryHistoryFromCassandra() during the k6 run (e.g.,
mock/skip Cassandra reads or make the function fall back gracefully when
Cassandra is unavailable). Ensure the change references
getTelemetryHistoryFromCassandra(), deviceTelemetry, and the CASSANDRA_* env
vars so the test run no longer fails.

JWKS_URL: https://example.com/.well-known/jwks.json
JWT_ISSUER: https://example.com/
JWT_AUDIENCE: grainguard
ALLOWED_ORIGINS: http://localhost:5173,http://localhost:3000

- name: Wait for gateway
run: npx wait-on http://localhost:3000/health --timeout 30000

- name: Wait for BFF
run: npx wait-on tcp:4000 --timeout 30000

- name: Install k6
run: |
curl -L https://github.com/grafana/k6/releases/download/v0.51.0/k6-v0.51.0-linux-amd64.tar.gz | tar xz
sudo mv k6-v0.51.0-linux-amd64/k6 /usr/local/bin/k6

- name: Run performance budget
run: |
k6 run \
--env GATEWAY_URL=http://localhost:3000 \
--env BFF_URL=http://localhost:4000 \
--env GATEWAY_AUTH_DISABLED=true \
--env BFF_AUTH_DISABLED=true \
--env JWT=dummy-jwt \
--env TEST_DEVICE_ID=00000000-0000-0000-0000-000000000001 \
scripts/load-tests/performance-budget.js

- name: Upload performance results
uses: actions/upload-artifact@v4
if: always()
with:
name: perf-results-${{ github.run_number }}
path: scripts/load-tests/results/
retention-days: 30
Loading
Loading