diff --git a/.github/workflows/backend-ci-cd.yml b/.github/workflows/backend-ci-cd.yml index 5d54b44f6..5cbf28f45 100644 --- a/.github/workflows/backend-ci-cd.yml +++ b/.github/workflows/backend-ci-cd.yml @@ -1,33 +1,30 @@ -name: Backend CI/CD Pipeline - -on: - push: - branches: - - develop - - main - tags: - - "v*" - paths: - - "backend/**" - - ".github/workflows/backend-ci-cd.yml" - pull_request: - branches: - - develop - - main - paths: - - "backend/**" - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}/backend +name: backend-dist jobs: - # ============================================================================ - # TEST: Unit Tests - # ============================================================================ - test: + e2e: runs-on: ubuntu-latest - name: Test + name: E2E + needs: build + + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + POSTGRES_DB: nestera_test + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U user -d nestera_test" + --health-interval=10s + --health-timeout=5s + --health-retries=5 + + redis: + image: redis:7 + ports: + - 6379:6379 steps: - name: Checkout code @@ -46,50 +43,10 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run unit tests - run: cd backend && pnpm run test - - - name: Generate coverage report - run: cd backend && pnpm run test:cov - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - files: ./backend/coverage/coverage-final.json - flags: unittests - fail_ci_if_error: false - - # ============================================================================ - # BUILD: Ensure the NestJS project compiles cleanly - # ============================================================================ - build: - runs-on: ubuntu-latest - name: Build - needs: test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: "pnpm" - cache-dependency-path: "**/pnpm-lock.yaml" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: cd backend && pnpm run build - - - name: Upload build artifact - uses: actions/upload-artifact@v4 - with: - name: backend-dist - path: backend/dist - retention-days: 7 + - name: Run backend E2E tests + env: + NODE_ENV: test + DATABASE_URL: postgresql://user:pass@localhost:5432/nestera_test + REDIS_URL: redis://localhost:6379 + JWT_SECRET: super-secret-key-for-testing-purposes-must-be-long-enough + run: cd backend && pnpm run test:e2e diff --git a/backend/docs/E2E_TEST_PLAN.md b/backend/docs/E2E_TEST_PLAN.md new file mode 100644 index 000000000..05c2b27b1 --- /dev/null +++ b/backend/docs/E2E_TEST_PLAN.md @@ -0,0 +1,22 @@ +# Backend E2E Test Plan + +Critical E2E paths: +1. Authentication flow +2. Wallet linking lifecycle +3. Savings subscription lifecycle +4. Withdrawal process +5. Governance proposal + voting +6. Transaction processing + +## Test environment +- PostgreSQL test database (`DATABASE_URL`) +- Redis test instance (`REDIS_URL`) +- Stellar testnet/soroban endpoints (or mocked adapters) + +## Coverage target +- Minimum 80% of defined critical-path scenarios. + +## Implementation notes +- Reuse `backend/test/factories/e2e.factory.ts` for deterministic test data. +- Keep tests independent and idempotent. +- Use database cleanup hooks between suites. diff --git a/backend/test/factories/e2e.factory.ts b/backend/test/factories/e2e.factory.ts new file mode 100644 index 000000000..3659e0f85 --- /dev/null +++ b/backend/test/factories/e2e.factory.ts @@ -0,0 +1,20 @@ +export const E2EFactory = { + authPayload: () => ({ + email: `e2e_${Date.now()}@example.com`, + password: 'StrongPass123!', + firstName: 'E2E', + lastName: 'User', + }), + walletLinkPayload: () => ({ + address: 'GBRPYHIL2C2YJ7Y3A4YQ7QO6FSVUIZXHTU2JZ5XW5ITQMRWJQN6M5J7N', + signature: 'test-signature', + nonce: `nonce-${Date.now()}`, + }), + savingsSubscriptionPayload: (productId: string) => ({ productId, amount: 100 }), + withdrawalPayload: (subscriptionId: string) => ({ subscriptionId, amount: 50 }), + governanceProposalPayload: () => ({ + title: 'E2E Governance Proposal', + description: 'Proposal created by e2e tests', + }), + votePayload: () => ({ support: true }), +};