# Solution Design Document

## Validation Checklist

- [x] All required sections are complete
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] All context sources are listed with relevance ratings
- [x] Project commands are discovered from actual project files
- [x] Constraints → Strategy → Design → Implementation path is logical
- [x] Architecture pattern is clearly stated with rationale
- [x] Every component in diagram has directory mapping
- [x] Every interface has specification
- [x] Error handling covers all error types
- [x] Quality requirements are specific and measurable
- [x] Every quality requirement has test coverage
- [x] **All architecture decisions confirmed by user** (ADR-1 through ADR-5 pre-approved - existing patterns)
- [x] Component names consistent across diagrams
- [x] A developer could implement from this design

---

## Constraints

**CON-1: Framework & Tooling**
- Flutter 3.38.3 / Dart 3.10.1 - must use `flutter_test` SDK
- Riverpod 3.x - requires `ProviderContainer` testing pattern with `addTearDown(container.dispose)`
- Mocktail 1.0.3 (already in pubspec) - no code generation, must use `registerFallbackValue()`
- Freezed 3.x models - generated code excluded from coverage

**CON-2: Execution & Performance**
- Full test suite must complete in < 2 minutes (target: < 60 seconds)
- Tests must run on macOS, Linux (CI), and Windows
- No network access - all external calls must be mocked
- Tests must be deterministic - no time-dependent flakiness

**CON-3: Coverage & Quality**
- Minimum 60% coverage threshold for PR approval (measured on changed files)
- Generated files excluded: `**/*.g.dart`, `**/*.freezed.dart`
- GitHub Actions CI/CD already configured (`.github/workflows/test.yml`)
- Codecov integration ready (needs repository registration)

**CON-4: Team & Process**
- Implementation by existing development team (no dedicated QA)
- Developers must be able to write tests using provided templates
- Pre-existing example tests and documentation must be leveraged

## Implementation Context

**IMPORTANT**: You MUST read and analyze ALL listed context sources to understand constraints, patterns, and existing architecture.

### Required Context Sources

- **ICO-1: Testing Documentation (CRITICAL - Already Created)**
```yaml
# Internal documentation - MUST READ FIRST
- doc: TESTING_README.md
  relevance: CRITICAL
  why: "Entry point - navigation guide for all testing resources"

- doc: TESTING_GUIDE.md
  relevance: CRITICAL
  why: "Comprehensive 1,333-line guide with 150+ code examples"

- doc: TESTING_CHEAT_SHEET.md
  relevance: HIGH
  why: "Quick reference for commands and patterns"

- doc: docs/patterns/testing-strategy.md
  relevance: HIGH
  why: "High-priority test targets and exception hierarchy"
```

- **ICO-2: Existing Test Infrastructure (Ready to Use)**
```yaml
# Test fixtures and templates
- file: test/fixtures/test_mocks.dart
  relevance: CRITICAL
  why: "251-line mock setup with 8 mock classes, stubbing helpers, factories"

- file: test/presentation/providers/dashboard_provider_test_example.dart
  relevance: HIGH
  why: "357-line Riverpod AsyncNotifier testing pattern template"

- file: test/presentation/screens/dashboard_screen_test_example.dart
  relevance: HIGH
  why: "Widget testing with GoRouter mocking template"

- file: test/integration/auth_flow_integration_test_example.dart
  relevance: MEDIUM
  why: "Integration testing pattern template"
```

- **ICO-3: CI/CD Configuration**
```yaml
- file: .github/workflows/test.yml
  relevance: HIGH
  why: "Complete CI/CD pipeline with coverage enforcement"

- file: pubspec.yaml
  relevance: MEDIUM
  sections: [dev_dependencies]
  why: "Testing packages: mocktail, flutter_test, flutter_lints"
```

- **ICO-4: Application Code to Test**
```yaml
# Core utilities (4 files)
- file: lib/core/utils/currency_utils.dart
  relevance: HIGH
  why: "Critical financial formatting - must test edge cases"

- file: lib/core/utils/date_utils.dart
  relevance: HIGH
  why: "Date parsing with multiple formats"

# Error handling (2 files, 26 types)
- file: lib/core/errors/exceptions.dart
  relevance: HIGH
  why: "13 exception types with toString() formatting"

- file: lib/core/errors/failures.dart
  relevance: HIGH
  why: "13 failure types with userMessage, canRetry, requiresAuth"

# Providers (17 files)
- file: lib/presentation/providers/
  relevance: CRITICAL
  why: "17 AsyncNotifier providers requiring mock injection"

# Mappers (13 files)
- file: lib/data/models/mappers/
  relevance: HIGH
  why: "13 mapper extensions for model-to-entity transformation"
```

### Implementation Boundaries

- **Must Preserve**:
  - Existing `test/widget_test.dart` smoke test (working baseline)
  - `test/fixtures/test_mocks.dart` structure (extend, don't replace)
  - CI/CD workflow in `.github/workflows/test.yml` (enhance, don't break)
  - All testing documentation (TESTING_*.md files)

- **Can Modify**:
  - Create new test files in `test/` directory structure
  - Add new fixtures to `test/fixtures/`
  - Rename `*_example.dart` files to active tests by removing `_example` suffix
  - Add new mock classes to `test_mocks.dart`
  - Enhance CI/CD workflow with additional steps

- **Must Not Touch**:
  - Application source code in `lib/` (test-only changes)
  - Generated files (`*.g.dart`, `*.freezed.dart`)
  - Production configuration files
  - API documentation files in `docs/` (except spec files)

### External Interfaces

**Note**: This is a testing infrastructure project. External interfaces are the testing tools and CI/CD systems, not application APIs.

#### System Context Diagram

```mermaid
graph TB
    Developer[Developer] --> TestRunner[Flutter Test Runner]
    TestRunner --> TestSuite[Test Suite]
    TestSuite --> Mocks[Mocktail Mocks]

    GitHub[GitHub PR/Push] --> CICD[GitHub Actions]
    CICD --> TestRunner
    CICD --> Coverage[Coverage Report]
    Coverage --> Codecov[Codecov Dashboard]

    TestSuite --> Fixtures[Test Fixtures]
    Mocks --> Repositories[(Mocked Repositories)]
```

#### Interface Specifications

```yaml
# Testing Framework Interfaces
testing:
  - name: "Flutter Test Runner"
    type: CLI
    command: "flutter test"
    doc: TESTING_CHEAT_SHEET.md
    data_flow: "Executes test files, produces results"

  - name: "Mocktail Mocking Library"
    type: Dart Package
    version: "^1.0.3"
    doc: TESTING_GUIDE.md#mocktail-best-practices
    data_flow: "Creates mock objects for dependency injection"

  - name: "Coverage Generator"
    type: CLI
    command: "flutter test --coverage"
    output: "coverage/lcov.info"
    doc: TESTING_GUIDE.md#coverage-integration

# CI/CD Interfaces
cicd:
  - name: "GitHub Actions"
    type: Workflow
    file: ".github/workflows/test.yml"
    triggers: [push, pull_request]
    data_flow: "Automated test execution on PR/push"
    criticality: HIGH

  - name: "Codecov"
    type: External Service
    integration: "codecov/codecov-action@v3"
    data_flow: "Coverage tracking and reporting"
    criticality: MEDIUM
    status: "Configured, needs repo registration"

# Mocked Application Interfaces (what tests simulate)
mocked_dependencies:
  - name: "Repository Interfaces"
    count: 9
    pattern: "MockXxxRepository extends Mock implements XxxRepository"
    doc: "test/fixtures/test_mocks.dart"

  - name: "External Services"
    mocked: [Dio, ApiClient, SecureStorage, GoRouter, PushNotificationService]
    doc: "test/fixtures/test_mocks.dart"
```

### Cross-Component Boundaries (if applicable)

**Test Layer Boundaries**:
- **Unit Tests** (`test/core/`, `test/data/`): Test single functions/classes in isolation
- **Provider Tests** (`test/presentation/providers/`): Test state management with mocked repositories
- **Widget Tests** (`test/presentation/screens/`): Test UI rendering with mocked providers
- **Integration Tests** (`test/integration/`): Test multi-component flows with minimal mocking

**Shared Resources**:
- `test/fixtures/test_mocks.dart` - Central mock definitions (all test files import this)
- `test/fixtures/api_responses/` - JSON fixtures shared across mapper and provider tests
- Coverage threshold (60%) - Enforced across all test types equally

### Project Commands

```bash
# Flutter Testing Commands (from pubspec.yaml and TESTING_CHEAT_SHEET.md)
Location: /Users/rvanvuren/Projects/buyerkiosk-live-flutter/

## Environment Setup
flutter pub get                              # Install dependencies
dart run build_runner build --delete-conflicting-outputs  # Generate Freezed/Riverpod code

## Testing Commands
flutter test                                 # Run all tests
flutter test --coverage                      # Run tests with coverage
flutter test test/core/                      # Run specific directory
flutter test test/presentation/providers/dashboard_provider_test.dart  # Run single file
flutter test --concurrency=4                 # Parallel execution (CI uses this)

## Coverage Commands
flutter test --coverage                      # Generate coverage/lcov.info
genhtml coverage/lcov.info -o coverage/html  # Generate HTML report
open coverage/html/index.html                # View report (macOS)
lcov --summary coverage/lcov.info            # Show coverage percentage

## Code Quality Commands
flutter analyze                              # Static analysis
dart format --set-exit-if-changed .          # Check formatting (CI enforces)
dart format .                                # Apply formatting

## Build Commands
flutter build apk --debug                    # Android debug build
flutter build ios --debug --no-codesign      # iOS debug build

## CI/CD Commands (from .github/workflows/test.yml)
# These run automatically on PR/push:
# 1. flutter pub get
# 2. dart run build_runner build
# 3. flutter analyze
# 4. dart format --set-exit-if-changed
# 5. flutter test --coverage --concurrency=4
# 6. Coverage upload to Codecov
# 7. Coverage threshold check (70% minimum)
```

## Solution Strategy

- **Architecture Pattern**: **Layered Test Architecture** mirroring the application's Clean Architecture
  - `test/core/` → Tests for `lib/core/` (utilities, errors)
  - `test/data/` → Tests for `lib/data/` (mappers, models)
  - `test/presentation/` → Tests for `lib/presentation/` (providers, screens)
  - `test/integration/` → Cross-layer flow tests

- **Integration Approach**: **Leverage Existing Infrastructure**
  - Use pre-built templates in `test/*_example.dart` as starting points
  - Extend `test/fixtures/test_mocks.dart` for new mock classes
  - Follow patterns in `TESTING_GUIDE.md` (1,333 lines of documentation)
  - CI/CD already configured - tests run automatically on PR

- **Justification**:
  - 80% of infrastructure already exists (templates, mocks, CI/CD, docs)
  - Templates provide proven patterns for Riverpod 3.x testing
  - Incremental approach - start with unit tests, add widget/integration later
  - No changes to production code required

- **Key Decisions**:
  1. **ADR-1**: Use Mocktail (not Mockito) - already configured, no code generation
  2. **ADR-2**: 60% coverage threshold on PR diffs - achievable starting point
  3. **ADR-3**: Prioritize unit tests first (utilities, mappers, errors) - highest ROI
  4. **ADR-4**: Convert `*_example.dart` to real tests - leverage existing work

## Building Block View

### Components

```mermaid
graph TB
    subgraph "Test Suite Components"
        Fixtures[test/fixtures/]
        UnitTests[test/core/ + test/data/]
        ProviderTests[test/presentation/providers/]
        WidgetTests[test/presentation/screens/]
        IntegrationTests[test/integration/]
    end

    subgraph "Application Under Test"
        CoreUtils[lib/core/utils/]
        Errors[lib/core/errors/]
        Mappers[lib/data/models/mappers/]
        Providers[lib/presentation/providers/]
        Screens[lib/presentation/screens/]
    end

    Fixtures --> UnitTests
    Fixtures --> ProviderTests
    Fixtures --> WidgetTests
    Fixtures --> IntegrationTests

    UnitTests --> CoreUtils
    UnitTests --> Errors
    UnitTests --> Mappers
    ProviderTests --> Providers
    WidgetTests --> Screens
    IntegrationTests --> Providers
    IntegrationTests --> Screens
```

### Directory Map

**Test Directory Structure** (43 new test files needed)
```
test/
├── widget_test.dart                           # EXISTING: Smoke test
├── fixtures/
│   ├── test_mocks.dart                        # EXISTING: Mock classes (extend this)
│   └── api_responses/                         # NEW: JSON fixtures for mappers
│       ├── today_performance_success.json     # NEW
│       ├── workbook_notes_page1.json          # NEW
│       ├── tasks_list.json                    # NEW
│       └── null_fields.json                   # NEW
│
├── core/                                      # NEW: Core layer tests
│   ├── utils/
│   │   ├── currency_utils_test.dart           # NEW: 15+ tests
│   │   ├── date_utils_test.dart               # NEW: 20+ tests
│   │   ├── time_utils_test.dart               # NEW: 15+ tests
│   │   └── hash_utils_test.dart               # NEW: 10+ tests
│   └── errors/
│       ├── exceptions_test.dart               # NEW: 13 exception types
│       └── failures_test.dart                 # NEW: 13 failure types
│
├── data/
│   └── models/
│       └── mappers/                           # NEW: 13 mapper tests
│           ├── today_performance_mapper_test.dart
│           ├── workbook_note_mapper_test.dart
│           ├── task_mapper_test.dart
│           ├── store_mapper_test.dart
│           ├── store_detail_mapper_test.dart
│           ├── queue_item_mapper_test.dart
│           ├── completed_buy_mapper_test.dart
│           ├── buyer_stats_mapper_test.dart
│           ├── workbook_comment_mapper_test.dart
│           ├── task_group_mapper_test.dart
│           ├── workbook_task_list_mapper_test.dart
│           ├── store_stats_mapper_test.dart
│           └── backstock_mapper_test.dart
│
├── presentation/
│   ├── providers/
│   │   ├── dashboard_provider_test_example.dart  # EXISTING: Rename to remove _example
│   │   ├── auth_provider_test.dart               # NEW
│   │   ├── permission_provider_test.dart         # NEW
│   │   ├── store_detail_provider_test.dart       # NEW
│   │   ├── queue_provider_test.dart              # NEW
│   │   ├── completed_buys_provider_test.dart     # NEW
│   │   ├── buyer_stats_provider_test.dart        # NEW
│   │   ├── workbook_notes_provider_test.dart     # NEW
│   │   ├── today_performance_provider_test.dart  # NEW
│   │   ├── task_provider_test.dart               # NEW
│   │   └── today_tasks_provider_test.dart        # NEW
│   │
│   └── screens/
│       ├── dashboard_screen_test_example.dart    # EXISTING: Rename to remove _example
│       ├── store_detail_screen_test.dart         # NEW
│       └── workbook_notes_screen_test.dart       # NEW
│
└── integration/
    ├── auth_flow_integration_test_example.dart   # EXISTING: Rename to remove _example
    └── store_flow_integration_test.dart          # NEW
```

### Interface Specifications

**Note**: This is a testing infrastructure project. No application interfaces are being modified. Test interfaces are internal to the test suite.

#### Interface Documentation References

```yaml
# Existing testing documentation (CRITICAL - already created)
interfaces:
  - name: "Mock Class Interfaces"
    doc: test/fixtures/test_mocks.dart
    relevance: CRITICAL
    sections: [MockStoreRepository, MockAuthRepository, registerFallbackValues]
    why: "Central mock setup - all tests import this"

  - name: "Provider Testing Patterns"
    doc: TESTING_GUIDE.md
    relevance: CRITICAL
    sections: [riverpod-asyncnotifier-testing, mocktail-best-practices]
    why: "Documented patterns for Riverpod 3.x testing"

  - name: "Widget Testing Patterns"
    doc: TESTING_GUIDE.md
    relevance: HIGH
    sections: [widget-testing-best-practices, gorouter-testing]
    why: "Documented patterns for widget and navigation testing"
```

#### Data Storage Changes

**No database/storage changes required.** This is a test-only implementation.

Test fixtures will be stored as:
```yaml
# Test fixture files (NEW - JSON format)
Location: test/fixtures/api_responses/
Files:
  - today_performance_success.json   # Valid KPI response
  - workbook_notes_page1.json        # Paginated notes
  - tasks_list.json                  # Task definitions
  - null_fields.json                 # Edge case: null fields
  - string_numbers.json              # Edge case: numbers as strings
```

#### Internal API Changes

**No API changes required.** Tests mock the existing API interfaces.

```yaml
# Mocked Repository Interfaces (existing contracts)
mocked_interfaces:
  - AuthRepository: checkInstallation(), validateApiKey(), clearCredentials()
  - DashboardRepository: fetchDashboard(), getStores()
  - StoreRepository: getStoreDetail(), getQueue(), getCompletedBuys()
  - WorkbookNotesRepository: getNotes(), createNote(), addReaction()
  - TaskRepository: getTasks(), createTask(), updateTask()
  - TodayPerformanceRepository: getTodayPerformance()
  - PermissionRepository: loadPermissions(), savePermissions()
```

#### Application Data Models

**No model changes required.** Tests validate existing model behavior.

```pseudocode
# Test Data Factories (NEW - for test fixtures)
FACTORY: TestDataFactory (in test/fixtures/test_mocks.dart)
  METHODS:
    + createStore(overrides): Store
    + createStoreList(count): List<Store>
    + createQueueItem(overrides): QueueItem
    + createWorkbookNote(overrides): WorkbookNote
    + createTask(overrides): Task
    + createTodayPerformance(overrides): TodayPerformance
```

#### Integration Points

**No external integrations.** Tests are fully isolated.

```yaml
# Test Isolation Strategy
isolation:
  - All HTTP calls: Mocked via MockDio
  - All storage: Mocked via MockSecureStorage
  - All real-time: Mocked via MockAblyRealtime
  - All navigation: Mocked via MockGoRouter

# CI/CD Integration (existing)
cicd:
  - GitHub Actions triggers tests on PR/push
  - Codecov receives coverage reports
  - PR checks block merge on test failure
```

### Implementation Examples

**Purpose**: Provide strategic code examples for the three main testing patterns. These are adapted from `TESTING_GUIDE.md` and the existing template files.

#### Example 1: Riverpod AsyncNotifier Provider Test Pattern

**Why this example**: Testing Riverpod 3.x AsyncNotifier providers is the most complex pattern and requires specific setup to avoid memory leaks and flaky tests.

```dart
// From: test/presentation/providers/dashboard_provider_test_example.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../fixtures/test_mocks.dart';

void main() {
  // CRITICAL: Register fallback values ONCE before all tests
  setUpAll(() => registerFallbackValues());

  late MockDashboardRepository mockRepository;
  late ProviderContainer container;

  setUp(() {
    mockRepository = MockDashboardRepository();
    container = ProviderContainer(
      overrides: [
        dashboardRepositoryProvider.overrideWithValue(mockRepository),
      ],
    );
    // CRITICAL: Prevent memory leaks
    addTearDown(container.dispose);
  });

  test('fetches stores successfully', () async {
    // Arrange
    final stores = TestDataFactory.createStoreList(3);
    when(() => mockRepository.fetchDashboard())
        .thenAnswer((_) async => stores);

    // Act - Listen triggers the build() method
    final listener = Listener<AsyncValue<List<Store>>>();
    container.listen(dashboardProvider, listener, fireImmediately: true);

    // CRITICAL: Pump microtasks to complete async operations
    await container.pump();

    // Assert - Verify state transitions
    verify(() => listener(null, any(that: isA<AsyncLoading>()))).called(1);
    verify(() => listener(any(), AsyncData(stores))).called(1);
  });

  test('caches auth errors for 30 seconds', () async {
    // Arrange - Simulate 403 auth error
    when(() => mockRepository.fetchDashboard())
        .thenThrow(AuthException('Invalid API key'));

    // Act - First call triggers error
    container.listen(dashboardProvider, (_, __) {});
    await container.pump();

    // Assert - Second call within 30s returns cached error (no new API call)
    verify(() => mockRepository.fetchDashboard()).called(1); // Only once!
  });
}

// Helper extension for async pumping
extension ProviderContainerPump on ProviderContainer {
  Future<void> pump() async {
    await Future<void>.value();
    await Future<void>.value();
  }
}
```

#### Example 2: Unit Test for Utility Functions

**Why this example**: Utility tests are the simplest pattern and should be written first for quick coverage wins.

```dart
// From: test/core/utils/currency_utils_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:buyer_kiosk_live/core/utils/currency_utils.dart';

void main() {
  group('parseCurrency', () {
    test('parses standard format: \$1,234.56', () {
      expect(parseCurrency('\$1,234.56'), 1234.56);
    });

    test('parses negative in parentheses: (\$1,234.56)', () {
      expect(parseCurrency('(\$1,234.56)'), -1234.56);
    });

    test('parses negative with minus: -\$1,234.56', () {
      expect(parseCurrency('-\$1,234.56'), -1234.56);
    });

    test('handles null gracefully', () {
      expect(parseCurrency(null), 0.0);
    });

    test('handles empty string gracefully', () {
      expect(parseCurrency(''), 0.0);
    });

    test('handles numbers as strings (API quirk)', () {
      expect(parseCurrency('1234.56'), 1234.56);
    });
  });

  group('formatCompactCurrency', () {
    test('formats thousands as K', () {
      expect(formatCompactCurrency(1234), '\$1.2K');
    });

    test('formats millions as M', () {
      expect(formatCompactCurrency(1234567), '\$1.2M');
    });

    test('handles zero', () {
      expect(formatCompactCurrency(0), '\$0');
    });
  });
}
```

#### Example 3: Mapper Test Pattern

**Why this example**: Mappers transform API responses to domain entities - testing them catches JSON parsing bugs before they reach production.

```dart
// From: test/data/models/mappers/today_performance_mapper_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:buyer_kiosk_live/data/models/today_performance_model.dart';
import 'package:buyer_kiosk_live/data/models/mappers/today_performance_mapper.dart';

void main() {
  group('TodayPerformanceMapper', () {
    test('maps valid model to entity', () {
      // Arrange - API returns numbers as strings (real API quirk)
      final model = TodayPerformanceModel(
        sales: SalesModel(current: '1875.25', goal: '2000.00'),
        avgTrans: AvgTransModel(current: '45.50'),
      );

      // Act
      final entity = model.toEntity();

      // Assert
      expect(entity.sales.current, 1875.25);
      expect(entity.sales.goal, 2000.0);
      expect(entity.avgTrans.current, 45.5);
    });

    test('handles null fields with sensible defaults', () {
      // Arrange - API may return nulls for missing data
      final model = TodayPerformanceModel(
        sales: null,
        avgTrans: null,
      );

      // Act
      final entity = model.toEntity();

      // Assert - Should not crash, should use defaults
      expect(entity.sales.current, 0.0);
      expect(entity.sales.goal, 0.0);
    });

    test('handles numbers as strings (API quirk)', () {
      // The API sometimes returns numeric values as strings
      final json = {'current': '1234.56', 'goal': '2000'};
      final model = SalesModel.fromJson(json);
      final entity = model.toEntity();

      expect(entity.current, 1234.56);
      expect(entity.goal, 2000.0);
    });
  });
}
```

## Runtime View

### Primary Flow

#### Primary Flow: Developer Runs Tests

The test execution flow follows Flutter's standard test runner with Riverpod-specific patterns:

1. Developer triggers test run (`flutter test` or CI pipeline)
2. Test framework initializes and loads test files
3. Each test file sets up mocks via `registerFallbackValues()`
4. Tests create isolated `ProviderContainer` instances
5. Tests execute assertions against provider states
6. Containers are disposed via `addTearDown()`
7. Results aggregated and reported (console, JUnit XML, lcov)

```mermaid
sequenceDiagram
    actor Dev as Developer
    participant CLI as flutter test
    participant Runner as Test Runner
    participant Setup as setUpAll/setUp
    participant Container as ProviderContainer
    participant Mock as Mock Classes
    participant SUT as System Under Test

    Dev->>CLI: flutter test --coverage
    CLI->>Runner: Load test files
    Runner->>Setup: setUpAll()
    Setup->>Mock: registerFallbackValues()

    loop Each Test
        Runner->>Setup: setUp()
        Setup->>Container: Create with overrides
        Setup->>Mock: Configure mock behavior
        Runner->>SUT: Execute test
        SUT->>Container: Access providers
        Container->>Mock: Delegate to mocks
        Mock-->>SUT: Return test data
        SUT-->>Runner: Assert results
        Runner->>Container: addTearDown(dispose)
    end

    Runner-->>CLI: Test results + coverage
    CLI-->>Dev: Report (pass/fail + lcov)
```

### Error Handling

Testing framework error handling strategies:

| Error Type | Test Strategy | Example |
|------------|---------------|---------|
| **Mock Setup Errors** | `registerFallbackValues()` prevents "no matching stub" errors | Missing `when()` for unmocked method |
| **Async Timeout** | Use `timeout` parameter, default 30s | `test('...', () async { ... }, timeout: Timeout(Duration(seconds: 60)))` |
| **Provider State Errors** | Assert `AsyncError` state with `expectLater` | `expect(state, isA<AsyncError>())` |
| **Container Disposal** | Always use `addTearDown(container.dispose)` | Prevents resource leaks between tests |
| **Exception Testing** | Use `throwsA(isA<ExceptionType>())` matcher | `expect(() => sut.method(), throwsA(isA<AuthException>()))` |

```dart
// Example: Testing error state in provider
test('handles network error gracefully', () async {
  // Arrange
  when(() => mockRepo.fetchData()).thenThrow(NetworkException('No connection'));

  // Act
  await container.read(dataProvider.future).catchError((_) {});
  final state = container.read(dataProvider);

  // Assert
  expect(state, isA<AsyncError>());
  expect(state.error, isA<NetworkException>());
});
```

### CI/CD Integration Flow

The GitHub Actions workflow executes tests as part of the PR process:

```
FLOW: CI Test Execution
INPUT: Pull Request with code changes
OUTPUT: Pass/Fail status with coverage report

1. TRIGGER: PR opened/updated to main branch
2. SETUP: Install Flutter SDK, restore dependencies
3. ANALYZE: Run dart analyze for static analysis
4. TEST: Execute flutter test --coverage
5. COVERAGE:
   - Generate lcov.info report
   - Upload to Codecov
   - Check 70% threshold on changed files
6. REPORT: Post results as PR check status
7. GATE: Block merge if tests fail or coverage < 70%
```

## Deployment View

**No runtime deployment changes** - This specification adds test infrastructure only, which does not affect production deployment.

### Test Infrastructure Deployment

Tests run in the following environments:

| Environment | Purpose | Trigger |
|-------------|---------|---------|
| **Local Development** | Developer runs tests manually | `flutter test`, IDE test runner |
| **GitHub Actions CI** | Automated validation on PR | Push to PR branch |
| **Pre-merge Gate** | Final validation before merge | PR approval + status checks |

### CI/CD Configuration

The existing `.github/workflows/test.yml` requires one configuration change:

- **Codecov Registration**: Register repository at codecov.io and add `CODECOV_TOKEN` to GitHub Secrets
- No other deployment changes required

## Cross-Cutting Concepts

### Pattern Documentation

```yaml
# Existing patterns used in this feature
- pattern: @docs/patterns/architecture-patterns.md
  relevance: CRITICAL
  why: "Clean Architecture layers dictate test organization and mock boundaries"

- pattern: @docs/patterns/testing-strategy.md
  relevance: CRITICAL
  why: "Defines test categories, coverage targets, and priority areas"

- pattern: @TESTING_GUIDE.md
  relevance: CRITICAL
  why: "Comprehensive 1,333-line guide with 150+ code examples"

- pattern: @TESTING_CHEAT_SHEET.md
  relevance: HIGH
  why: "Quick reference for common testing patterns"

# New patterns created for this feature
- pattern: test/fixtures/test_mocks.dart (EXISTS)
  relevance: CRITICAL
  why: "Centralized mock definitions - extend as needed for new tests"
```

### Interface Specifications

```yaml
# External interfaces this feature integrates with
- interface: @docs/interfaces/api-contracts.md
  relevance: HIGH
  why: "API response formats are mocked in repository tests"

# Testing framework interfaces (no new documentation needed)
- interface: flutter_test (built-in)
  relevance: CRITICAL
  why: "Core test framework"

- interface: mocktail 1.0.3
  relevance: CRITICAL
  why: "Mock creation and verification"

- interface: flutter_riverpod 3.x (ProviderContainer)
  relevance: CRITICAL
  why: "Provider isolation and override mechanism"
```

### System-Wide Patterns

Testing patterns that apply across all test categories:

- **Test Isolation**: Each test creates its own `ProviderContainer` with overrides
- **Mock Registration**: `registerFallbackValues()` in `setUpAll()` for all mock types
- **Async Testing**: Use `container.read(provider.future)` for AsyncNotifier tests
- **Error Verification**: Test both success paths and error handling
- **Coverage Enforcement**: 70% minimum on changed files via CI

### Multi-Layer Testing Strategy

Tests are organized by Clean Architecture layer:

| Layer | Test Focus | Mock Strategy |
|-------|-----------|---------------|
| **Core** | Pure functions, utilities | No mocks needed |
| **Data** | Mappers, model parsing | Mock JSON input |
| **Domain** | Entity behavior | No mocks (pure logic) |
| **Presentation** | Provider state, UI logic | Mock repositories |

### Implementation Patterns

#### Code Patterns and Conventions

**Test File Naming**:
- Unit tests: `{source_file}_test.dart`
- Integration tests: `{feature}_integration_test.dart`
- Widget tests: `{widget_name}_test.dart`

**Test Group Organization**:
```dart
void main() {
  group('ClassName', () {
    group('methodName', () {
      test('returns expected value when condition', () {});
      test('throws exception when invalid input', () {});
    });
  });
}
```

**Arrange-Act-Assert Pattern**:
```dart
test('description', () {
  // Arrange - set up test data and mocks
  final input = TestData.validInput;
  when(() => mock.method()).thenReturn(expected);

  // Act - execute the code under test
  final result = sut.execute(input);

  // Assert - verify the outcome
  expect(result, expected);
  verify(() => mock.method()).called(1);
});
```

#### State Management Testing Patterns

**Riverpod AsyncNotifier Testing**:
```dart
// Pattern: Test provider state transitions
test('provider transitions through loading to data', () async {
  // Arrange
  when(() => mockRepo.fetch()).thenAnswer((_) async => testData);

  // Act - trigger build
  final future = container.read(myProvider.future);

  // Assert - verify loading state
  expect(container.read(myProvider), isA<AsyncLoading>());

  // Wait and verify data state
  await future;
  expect(container.read(myProvider).value, testData);
});
```

**Family Provider Testing**:
```dart
// Pattern: Test family providers with parameters
test('family provider uses correct parameter', () async {
  when(() => mockRepo.fetchById(any())).thenAnswer((_) async => testData);

  // Access family provider with specific argument
  await container.read(myFamilyProvider('store123').future);

  verify(() => mockRepo.fetchById('store123')).called(1);
});
```

#### Test Data Factory Pattern

**Centralized Test Data**:
```dart
// In test/fixtures/test_mocks.dart
class TestDataFactory {
  static Store createStore({
    String typeNum = 'PC00',
    String storeName = 'Test Store',
    int queueCount = 5,
  }) => Store(
    typeNum: typeNum,
    storeName: storeName,
    queueCount: queueCount,
  );

  static List<Store> createStoreList({int count = 3}) =>
    List.generate(count, (i) => createStore(typeNum: 'PC0$i'));
}
```

#### Error Testing Patterns

**Exception Verification**:
```dart
// Pattern: Test that exceptions are thrown correctly
test('throws AuthException on 403 response', () async {
  when(() => mockClient.post(any())).thenThrow(
    DioException(
      response: Response(statusCode: 403, requestOptions: RequestOptions()),
      requestOptions: RequestOptions(),
    ),
  );

  expect(
    () => repository.authenticate('invalid-key'),
    throwsA(isA<AuthException>()),
  );
});
```

**AsyncError State Testing**:
```dart
// Pattern: Verify provider handles errors correctly
test('provider exposes error state on failure', () async {
  when(() => mockRepo.fetch()).thenThrow(NetworkException('No connection'));

  await container.read(myProvider.future).catchError((_) {});
  final state = container.read(myProvider);

  expect(state, isA<AsyncError>());
  expect(state.error, isA<NetworkException>());
});
```

### Integration Points

- **Build System**: Tests run via `flutter test` command
- **CI/CD**: GitHub Actions workflow at `.github/workflows/test.yml`
- **Coverage**: Codecov integration for PR coverage reports
- **IDE**: VS Code and IntelliJ test runners work with standard Flutter test structure

## Architecture Decisions

- [x] **ADR-1: Mocktail over Mockito**
  - **Choice**: Use Mocktail 1.0.3 for mock creation
  - **Rationale**: No code generation required, better null-safety support, cleaner syntax
  - **Trade-offs**: Less IDE auto-completion than generated mocks
  - **Status**: Already implemented in test_mocks.dart

- [x] **ADR-2: ProviderContainer for Isolation**
  - **Choice**: Create fresh `ProviderContainer` per test with `addTearDown(container.dispose)`
  - **Rationale**: Ensures complete test isolation, prevents state leakage
  - **Trade-offs**: Slightly more boilerplate than shared containers
  - **Status**: Pattern documented in TESTING_GUIDE.md

- [x] **ADR-3: Centralized Mock Registry**
  - **Choice**: Single `test/fixtures/test_mocks.dart` file with `registerFallbackValues()`
  - **Rationale**: DRY principle, consistent mock setup across all tests
  - **Trade-offs**: Large file may grow; can split by domain if needed later
  - **Status**: Already implemented with 8 mock classes

- [x] **ADR-4: 70% Coverage Threshold**
  - **Choice**: Enforce 70% coverage on PR diff in CI, target 60% overall
  - **Rationale**: Balances quality with development velocity; focuses on new code
  - **Trade-offs**: Existing uncovered code not immediately addressed
  - **Status**: Configured in .github/workflows/test.yml

- [x] **ADR-5: Test Organization by Layer**
  - **Choice**: Mirror `lib/` structure in `test/` directory
  - **Rationale**: Easy to find tests, clear correspondence with source files
  - **Trade-offs**: Deep nesting for some test paths
  - **Status**: Directory structure defined in Building Block View

## Quality Requirements

### Coverage Metrics

| Metric | Target | Measurement |
|--------|--------|-------------|
| **Overall Code Coverage** | 60% minimum | `flutter test --coverage` lcov output |
| **PR Diff Coverage** | 70% minimum | Codecov PR comment check |
| **Core Layer Coverage** | 80% | High-value business logic |
| **Domain Layer Coverage** | 90% | Entity computed properties |
| **Critical Path Coverage** | 100% | Auth flow, permission checks |

### Test Performance

| Metric | Target | Rationale |
|--------|--------|-----------|
| **Unit Test Execution** | < 30 seconds total | Fast feedback loop |
| **Single Test File** | < 5 seconds | Incremental test runs |
| **CI Pipeline Total** | < 5 minutes | Reasonable PR wait time |

### Reliability

| Requirement | Target | Verification |
|-------------|--------|--------------|
| **Test Determinism** | 100% repeatable | No flaky tests in CI |
| **Isolation** | Zero cross-test pollution | Container disposal pattern |
| **Mock Completeness** | All external dependencies mocked | No network calls in unit tests |

### Maintainability

| Requirement | Approach |
|-------------|----------|
| **Test Readability** | Arrange-Act-Assert pattern with clear comments |
| **DRY Test Code** | Shared fixtures in `test_mocks.dart` |
| **Discoverable Tests** | Mirror `lib/` structure in `test/` |
| **Self-Documenting** | Group and test names describe behavior |

## Risks and Technical Debt

### Known Technical Issues

| Issue | Impact | Mitigation |
|-------|--------|------------|
| **Zero existing test coverage** | No safety net for refactoring | This spec addresses directly |
| **Example files need conversion** | Templates exist but aren't active tests | Rename `_example.dart` → `_test.dart` |
| **Codecov not registered** | CI coverage check won't report | Register repo at codecov.io |
| **Generated code in coverage** | Freezed `.g.dart` files inflate uncovered lines | Already excluded in CI workflow |

### Technical Debt

| Debt Item | Location | Resolution |
|-----------|----------|------------|
| **Smoke test only** | `test/widget_test.dart` | Add comprehensive tests per this spec |
| **Example templates inactive** | `test/**/*_example.dart` files | Convert to real tests by removing `_example` suffix |
| **Missing mock classes** | test_mocks.dart has 8 mocks | Add mocks as tests require (WorkbookNote, Task repos) |
| **No widget tests** | UI layer untested | Phase 3 adds screen tests |

### Implementation Gotchas

| Gotcha | Details | Solution |
|--------|---------|----------|
| **AsyncNotifier testing** | Can't directly instantiate; must use `ProviderContainer` | Always use `container.read(provider.future)` |
| **Family provider parameters** | Parameter equality matters for caching | Test with same and different parameters |
| **registerFallbackValues timing** | Must be called before any `when()` with `any()` | Always call in `setUpAll()` |
| **Container disposal** | Memory leaks if not disposed | Always use `addTearDown(container.dispose)` |
| **Async test completion** | Tests may complete before async operations | Use `await container.read(provider.future)` |
| **Mock verification order** | `verify()` must come after `await` completes | Place verifications at end of test |

## Test Specifications

### Critical Test Scenarios

**Scenario 1: Permission System Validation**
```gherkin
Given: A user with Manager access level (value: 2)
When: The permission system checks canAccess for EditTaskList page (requires Manager)
Then: Access is granted (user.accessLevel.value <= requiredLevel.value)

Given: A user with Employee access level (value: 4)
When: The permission system checks canAccess for EditTaskList page (requires Manager)
Then: Access is denied (4 > 2)
```

**Scenario 2: Dashboard Provider Error Caching**
```gherkin
Given: The dashboard provider is in loading state
When: An AuthException occurs during fetch
Then: The error is cached for 30 seconds
And: Subsequent refresh attempts within 30 seconds return cached error
And: Refresh after 30 seconds makes a new API call
```

**Scenario 3: JSON Parsing Edge Cases**
```gherkin
Given: API returns numeric value as string "1875.25"
When: _parseDouble() processes the value
Then: The result is 1875.25 (double)

Given: API returns null for a numeric field
When: _parseDouble() processes the value
Then: The result is 0.0 (default)

Given: API returns unparseable string "N/A"
When: _parseDouble() processes the value
Then: The result is 0.0 (fallback)
```

**Scenario 4: Mapper Null Handling**
```gherkin
Given: A TodayPerformanceModel with null sales field
When: toEntity() mapper is called
Then: Entity has sales with current=0.0, goal=0.0 (sensible defaults)
And: No exception is thrown
```

### Test Coverage Requirements

| Category | Coverage Target | Critical Areas |
|----------|----------------|----------------|
| **Core Utilities** | 80% | `_parseDouble`, `_parseInt`, currency parsing |
| **Permission System** | 100% | `AccessLevel.canAccess`, page permission checks |
| **Entity Computed Properties** | 90% | Progress bars, visibility checks, derived values |
| **Mappers** | 85% | All 11 mappers, null handling, type coercion |
| **Provider State Transitions** | 70% | Loading → Data → Error flows |
| **Error Handling** | 80% | All exception types, interceptor behavior |

---

## Glossary

### Testing Terms

| Term | Definition | Context |
|------|------------|---------|
| **SUT** | System Under Test - the component being tested | Used in Arrange-Act-Assert pattern |
| **Mock** | Fake implementation that records calls and returns preset values | Created with Mocktail `class MockX extends Mock implements X` |
| **Stub** | Preset return value for a mock method | `when(() => mock.method()).thenReturn(value)` |
| **Fixture** | Reusable test data or setup code | Centralized in `test/fixtures/test_mocks.dart` |
| **Coverage** | Percentage of code executed during tests | Measured with `flutter test --coverage` |

### Riverpod Testing Terms

| Term | Definition | Context |
|------|------------|---------|
| **ProviderContainer** | Isolated container for provider instances | Created per-test for isolation |
| **Override** | Replacement of a provider's implementation | `container = ProviderContainer(overrides: [...])` |
| **AsyncNotifier** | Provider that manages async state (Loading/Data/Error) | Primary pattern in this codebase |
| **Family Provider** | Parameterized provider that creates instances per parameter | Used for store-specific data |

### CI/CD Terms

| Term | Definition | Context |
|------|------------|---------|
| **Codecov** | Code coverage reporting service | Integrates with GitHub PRs |
| **lcov** | Coverage report format | Generated by `flutter test --coverage` |
| **PR Gate** | Required check before PR can be merged | Coverage threshold enforcement |

### Architecture Terms

| Term | Definition | Context |
|------|------------|---------|
| **Clean Architecture** | Layered design with core/data/domain/presentation | Test organization mirrors these layers |
| **Mapper** | Extension method converting Model to Entity | `StoreModelX.toEntity()` pattern |
| **Entity** | Domain object using Equatable | Tested for computed properties |
| **Model** | Data object using Freezed | Tested for JSON parsing |
