name: Test
on:
  workflow_dispatch:
  pull_request:
  push:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  e2e-tests:
    name: Run end-to-end test suites

    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Run Immich Server E2E Test
        run: docker-compose -f ./docker/docker-compose.test.yml --env-file ./docker/.env.test up --abort-on-container-exit --exit-code-from immich-server-test

  server-unit-tests:
    name: Run server unit test suites and checks
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Run tests
        run: cd server && npm ci && npm run check:all

  web-unit-tests:
    name: Run web unit test suites and checks
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Run tests
        run: cd web && npm ci && npm run check:all

  mobile-unit-tests:
    name: Run mobile unit tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Flutter SDK
        uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
          flutter-version: '3.7.3'
      - name: Run tests
        working-directory: ./mobile
        run: flutter test

  generated-api-up-to-date:
    name: Check generated files are up-to-date
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run API generation
        run: cd server && npm ci && npm run api:generate
      - name: Find file changes
        uses: tj-actions/verify-changed-files@v13.1
        id: verify-changed-files
        with:
          files: |
            mobile/openapi
            web/src/api/open-api
      - name: Verify files have not changed
        if: steps.verify-changed-files.outputs.files_changed == 'true'
        run: |
          echo "ERROR: Generated files not up to date!"
          echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
          exit 1

  generated-typeorm-migrations-up-to-date:
    name: Check generated TypeORM migrations are up-to-date
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_USER: postgres
          POSTGRES_DB: immich
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    steps:
      - uses: actions/checkout@v3
      - name: Install server dependencies
        run: |
          cd server
          npm ci
      - name: Run existing migrations
        run: |
          cd server
          npm run typeorm:migrations:run
      - name: Generate new migrations
        continue-on-error: true
        run: |
          cd server
          npm run typeorm:migrations:generate ./libs/infra/src/db/migrations/TestMigration
      - name: Find file changes
        uses: tj-actions/verify-changed-files@v13.1
        id: verify-changed-files
        with:
          files: |
            server/libs/infra/src/db/migrations/
      - name: Verify files have not changed
        if: steps.verify-changed-files.outputs.files_changed == 'true'
        run: |
          echo "ERROR: Generated files not up to date!"
          echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
          exit 1

  mobile-integration-tests:
    name: Run mobile end-to-end integration tests
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '12.x'
          cache: 'gradle'
      - name: Cache android SDK
        uses: actions/cache@v3
        id: android-sdk
        with:
          key: android-sdk
          path: |
            /usr/local/lib/android/
            ~/.android
      - name: Cache Gradle
        uses: actions/cache@v3
        with:
          path: |
            ./mobile/build/
            ./mobile/android/.gradle/
          key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }}
      - name: Setup Android SDK
        if: steps.android-sdk.outputs.cache-hit != 'true'
        uses: android-actions/setup-android@v2
      - name: AVD cache
        uses: actions/cache@v3
        id: avd-cache
        with:
          path: |
            ~/.android/avd/*
            ~/.android/adb*
          key: avd-29
      - name: create AVD and generate snapshot for caching
        if: steps.avd-cache.outputs.cache-hit != 'true'
        uses: reactivecircus/android-emulator-runner@v2.27.0
        with:
          working-directory: ./mobile
          cores: 2
          api-level: 29
          arch: x86_64
          profile: pixel
          target: default
          force-avd-creation: false
          emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
          disable-animations: false
          script: echo "Generated AVD snapshot for caching."
      - name: Setup Flutter SDK
        uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
          flutter-version: '3.7.3'
          cache: true
      - name: Run integration tests
        uses: Wandalen/wretry.action@master
        with:
          action: reactivecircus/android-emulator-runner@v2.27.0
          with: |
            working-directory: ./mobile
            cores: 2
            api-level: 29
            arch: x86_64
            profile: pixel
            target: default
            force-avd-creation: false
            emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
            disable-animations: true
            script: |
              flutter pub get
              flutter test integration_test
          attempt_limit: 3