# Implementation Plan: Integration Tests Restructure

## Background & Motivation
To prepare the Yorvana repository for being made private on GitHub, we need to drastically reduce GitHub Actions CI runtime, specifically the expensive emulator boot times required by Gradle Managed Devices (GMD) to stay within the free tier limits.

## Scope & Impact
- Move the vast majority of integration tests (`*IT.kt`, `*Test.kt`) from `androidTest` to `test` to run on the JVM using Robolectric.
- Introduce a minimal End-to-End (E2E) smoke test suite in `androidTest` that runs via GMD.
- Update code coverage to utilize a pre-generated coverage file for GMD tests, stored in Git LFS, so PRs do not need to run the emulator. **Note:** JaCoCo execution data is tied to class structure; if classes under GMD coverage change significantly in a PR, the pre-generated coverage might be dropped as stale. We accept this temporary limitation for PRs, as the true coverage will be recalculated correctly upon merge to `master` when GMD is run.
- Overhaul GitHub Actions workflows to separate PR checks (fast JVM tests) from `master`/manual checks (GMD smoke tests).

## Testing Strategy & Responsibilities

To maintain high confidence while minimizing CI costs, we will use a tiered testing approach:

### 1. Unit Tests (JVM)
- **What to test**: Business logic, data transformations, domain models, and any code with zero Android dependencies.
- **Execution**: Local JVM (fastest).
- **Scope**: Class-level isolation.

### 2. Integration Tests (Robolectric / JVM)
- **What to test**: UI components (Composables), ViewModel-View interactions, Repository-Storage interactions, and multi-component navigation flows. This is our primary safety net for feature correctness.
- **Execution**: JVM using Robolectric to simulate the Android environment.
- **Scope**: Feature-level integration. Most tests currently in `androidTest` fall into this category.

### 3. E2E Smoke Tests (GMD / Emulator)
- **What to test**: Critical "Happy Path" user journeys (e.g., first-time setup, adding a vehicle, adding a record). These verify that the app actually launches, connects to the filesystem via SAF, and renders correctly on a real Android OS.
- **Execution**: Gradle Managed Devices (Emulator).
- **Scope**: System-level verification.

## Estimated Migration Scope
Of the 14 test files currently in `androidTest`, 13 are candidates for migration to Robolectric/JVM (ViewModel ITs, Screen ITs, storage/repository ITs). Only `MainActivityTest.kt` uses `ActivityScenario`/`createAndroidComposeRule` and must remain as the instrumented smoke test.

## Phased Implementation Plan

### Phase 1: Git LFS and Gradle Task Configuration
**Goal:** Set up the infrastructure to store and generate GMD coverage without modifying existing tests yet.
1. **LFS Setup**: Initialize Git LFS for the repository (`git lfs install`), then create `.gitattributes` if it does not already exist and add/update the Git LFS tracking rule for static coverage files (for example via `git lfs track "app/coverage-baselines/*.ec"` so `.gitattributes` contains the matching entry).
2. **GMD Coverage Generation**: Add a new Gradle task (e.g., `generateGmdCoverage`) in `app/build.gradle.kts`. This task should:
   - Depend on `pixel2api33DebugAndroidTest`.
   - Ensure Android test coverage is enabled when this task runs, either by requiring invocation with `-Pcoverage` or by expanding the `isCoverageEnabled`/`enableAndroidTestCoverage` condition to also include `generateGmdCoverage`.
   - Locate the dynamically generated `.ec` file in `app/build/outputs/managed_device_code_coverage/` only after coverage has been enabled for the Android test run.
   - Copy this `.ec` file to the static LFS tracked location (e.g., `app/coverage-baselines/gmd_smoke.ec`).
3. **Coverage Aggregation**: Update the existing `verifyWithCoverage` task:
   - Remove its dependency on `pixel2api33DebugAndroidTest`.
   - Modify its `executionData` to explicitly include the static LFS file (`app/coverage-baselines/gmd_smoke.ec`) instead of the dynamically generated GMD output files.
   - **Local caveat:** After this change, running `verifyWithCoverage` locally will use whichever `.ec` file is checked out in LFS. If it is stale or missing, coverage from GMD tests will be absent or inaccurate. Developers who need fresh GMD coverage locally should run `generateGmdCoverage` first.

### Phase 2: Migrate Existing Integration Tests
**Goal:** Move the bulk of non-E2E integration tests to run on the JVM via Robolectric while preserving minimal emulator-backed smoke coverage.
1. **Move Files by Intent/Runner**: Move only the `androidTest` files that are feature/integration tests suitable for JVM execution under Robolectric from `app/src/androidTest/java/com/yorvana/` (e.g., `data`, `ui/records`, `ui/settings`, `ui/vehicles`) to `app/src/test/java/com/yorvana/`. Do **not** migrate tests solely because they match `*IT.kt` or `*Test.kt`; keep emulator-required smoke/E2E coverage in `androidTest`, including the existing `MainActivityTest.kt` if it is serving as the instrumented launch/smoke check.

   **Disqualification checklist** — a test must stay in `androidTest` if it:
   - Uses `ActivityScenario`, `createAndroidComposeRule`, or `createComposeRule` backed by an Activity (real Activity lifecycle).
   - Depends on `UiDevice` or UiAutomator APIs.
   - Interacts with the real filesystem via SAF (`ContentResolver`, `DocumentsProvider`, or non-mocked `DocumentFile`).
   - Requires a running `Instrumentation` instance (e.g., calls `InstrumentationRegistry.getInstrumentation()`).
   - Tests hardware-dependent behavior (camera, sensors, Bluetooth).

   If a test only *mocks* these APIs (e.g., `mockk<DocumentFile>()`), it is still eligible for migration.
2. **Robolectric Configuration**: Ensure each migrated JVM-side test is annotated with `@RunWith(RobolectricTestRunner::class)` where appropriate. For UI/rendering tests, rely on the existing Gradle-wide `robolectric.graphicsMode=NATIVE` configuration in `app/build.gradle.kts` as the default for JVM `Test` tasks; add `@GraphicsMode(GraphicsMode.Mode.NATIVE)` only in exceptional cases where a test must override or explicitly document its graphics behavior. Leave the retained `androidTest` smoke tests on the instrumentation runner.
3. **Local Verification**: Locally run `./gradlew testDebugUnitTest` and fix any Robolectric specific issues, missing configuration, graphics mode mismatches, or flakiness. Also run `./gradlew pixel2api33DebugAndroidTest` to confirm the retained smoke coverage in `androidTest` still passes.
4. **Update `TESTING_SETUP.md`**: Document that Robolectric UI tests inherit the Gradle-wide native graphics mode by default, and note that `@GraphicsMode` should only be added for exceptional cases or explicit overrides. Also document the migration disqualification checklist so the test setup guide stays aligned with this plan.

### Phase 3: Create GMD Smoke Test Suite
**Goal:** Establish a minimal E2E test suite to run on the Android emulator.
1. **Consolidate Existing Smoke Coverage**: Review the existing instrumented smoke-style test at `app/src/androidTest/java/com/yorvana/MainActivityTest.kt` and convert it into the canonical smoke suite entry point. Prefer renaming/migrating it to `app/src/androidTest/java/com/yorvana/SmokeTest.kt`, or explicitly expand it in place if keeping the current filename is less disruptive, but do not leave both as overlapping smoke-test entry points.
2. **Implement Flow**: Ensure the canonical smoke suite covers a high-level user flow such as launching the app, navigating, adding a vehicle, and adding a service record.
3. **Local Verification**: Run the canonical GMD smoke suite locally using `./gradlew pixel2api33DebugAndroidTest` to verify it passes. If other repo docs reference `./gradlew pixel2api33Check`, treat that as the broader device-specific check task; use `pixel2api33DebugAndroidTest` here when you want to run only the smoke suite itself during local validation.
4. **Initial Baseline**: Run the `./gradlew generateGmdCoverage` task to generate the initial `gmd_smoke.ec` file and commit it to Git LFS.

### Phase 4: CI Pipeline Updates
**Goal:** Update GitHub Actions to implement the fast PR strategy and the manual GMD workflow.
1. **Update `ci.yml` (PR Flow)**:
   - Update workflow triggers (e.g., `on: pull_request`) to remove `master` push if `ci.yml` is meant to be PR-only, or explicitly clarify that `ci.yml` will still run fast checks on `master` alongside `smoke-tests.yml`.
   - Ensure Git LFS is enabled during checkout (e.g., configuring `lfs: true` on `actions/checkout` or explicitly running `git lfs pull`) so the tracked `.ec` file is available.
   - Remove KVM setup steps and KVM related commands since the emulator will no longer boot.
   - Adjust the `./gradlew verifyWithCoverage` command to remove GMD-specific parameters (e.g., `-Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect`).
2. **Create `smoke-tests.yml` (GMD Flow)**:
   - Set triggers to `push` on `master` and `workflow_dispatch` (allowing manual runs from the GitHub UI).
   - Include KVM permission steps.
   - Run the `./gradlew generateGmdCoverage` task.
   - Upload the generated `gmd_smoke.ec` file as a workflow artifact for review instead of committing and pushing from CI.
   - Keep baseline updates as a deliberate maintainer action: download the artifact, verify it, and commit the refreshed `gmd_smoke.ec` to Git LFS manually when appropriate.
   - To prevent the baseline from drifting silently, add a scheduled CI job (e.g., a monthly `schedule:` cron trigger, or a reminder in `smoke-tests.yml`) that opens an issue when the `.ec` file is older than a configurable threshold.
3. **Update Documentation**: Update `TESTING_SETUP.md` to reflect the new testing strategy, naming conventions, and CI structure. This includes:
   - Formally documenting the **Testing Strategy & Responsibilities** (what should be tested with Unit Tests vs. Robolectric vs. GMD).
   - Instructions on how to manually trigger GMD tests via GitHub Actions.
   - How coverage artifacts are produced in CI and how maintainers deliberately update the Git LFS baseline from a reviewed artifact.

## Verification & Testing
- **Local Verification:** Running `./gradlew verifyWithCoverage` locally should complete quickly without booting an emulator and correctly aggregate the coverage using the `.ec` file in LFS.
- **CI Verification:** Creating a PR should trigger only `ci.yml` (fast JVM tests). Manually triggering `smoke-tests.yml` from GitHub should successfully run GMD tests and upload an updated `gmd_smoke.ec` artifact for maintainer review.

## Migration & Rollback
- Reverting these commits will safely return the codebase to its original state, utilizing dynamically generated GMD coverage on every PR.