# Task Recon: Implement auth and tenants CLI integration tests

**Task ID:** 35.2
**Status:** pending
**Generated:** 2026-01-30T21:30:00Z
**Agent:** claude-opus-4-5-20251101

---

## Task Summary

Create integration tests for auth commands (login/logout/status/refresh) and tenants commands (list/show/add/remove/switch/test-connection) with table and JSON output validation.

### Parent Task Context

**Parent Task:** Write CLI Integration Tests (ID: 35)
**Parent Goal:** Create integration tests for CLI commands using CliRunner with mocked services. Test all commands with default (table) output, JSON output, error cases (not found, validation), and global flags (--tenant, --verbose).
**This Subtask's Role:** Implements the authentication and tenant management CLI integration tests, which are foundational commands for the CLI tool. These tests validate the auth workflow (login flow, token management) and multi-tenant configuration (add/remove/switch tenants).

### Dependencies
- Depends on: 35.1 (Create CLI integration test infrastructure with conftest.py and shared fixtures) - **COMPLETED**

### Acceptance Criteria
1. Create `tests/integration/cli/test_auth_integration.py` with TestAuthWorkflows class
2. Create `tests/integration/cli/test_tenants_integration.py` with TestTenantsWorkflows class
3. All tests use the composite fixtures from conftest.py (35.1)
4. Test both table and JSON output formats
5. Verify exit codes match exception hierarchy
6. Tests pass when run with `pytest tests/integration/cli/test_auth_integration.py tests/integration/cli/test_tenants_integration.py -v`

---

## Insights from Previous Tasks

### Dependency Task Notes
| Task ID | Title | Key Insight |
|---------|-------|-------------|
| 35.1 | Create CLI integration test infrastructure | Fixtures created: `runner`, `mock_cli_services_auth`, `mock_cli_services_tenants`, model factories |

### Reusable Patterns from Dependencies
- **Composite fixture pattern**: Use `mock_cli_services_auth` and `mock_cli_services_tenants` for patching services
- **Model factories**: Use `make_auth_status()`, `make_cached_token()`, `make_tenant()` from conftest.py
- **Service patching**: Patches at `teams_phone.cli.auth.CacheManager` and `teams_phone.cli.auth.AuthService`

### Warnings and Gotchas
- **From Task 35.1**: AsyncMock `__aenter__` must return self for context manager pattern
- **From Task 35.1**: mypy errors about missing py.typed are project-wide, not fixture issues
- **Auth CLI**: Uses sync AuthService (not async) - no GraphClient context manager needed
- **Tenants CLI**: The `_create_tenant_service()` helper creates services internally

### Open Items Inherited
- None - 35.1 completed all fixture infrastructure

---

## Architecture Context

### Relevant Design Decisions
- CLI uses Typer with CliRunner for testing
- Services are patched at the module import level (e.g., `teams_phone.cli.auth.AuthService`)
- Exit codes are defined in `teams_phone.constants.ExitCode` and match exception hierarchy
- JSON output is controlled by `--json` global flag passed before subcommand

### Module Structure
```
src/teams_phone/cli/
├── auth.py        # auth_app: status, login, logout, refresh
├── tenants.py     # tenants_app: list, current, switch, test, remove, add
├── main.py        # Main app with global options
└── context.py     # CLIContext for capturing global options
```

### Conventions to Follow
- Test classes prefixed with `Test` and grouped by command
- Use `@pytest.mark.integration` marker for integration tests
- Factory functions are module-level helpers (not fixtures)
- Composite fixtures return dict of mocks for easy access

---

## Related Code References

### Primary References (Must Read)
| File | Lines | Purpose |
|------|-------|---------|
| `src/teams_phone/cli/auth.py` | 1-184 | Auth CLI implementation: status, login, logout, refresh |
| `src/teams_phone/cli/tenants.py` | 1-354 | Tenants CLI implementation: list, current, switch, test, remove, add |
| `tests/integration/cli/conftest.py` | 1-711 | CLI integration test fixtures |
| `tests/unit/test_cli_auth.py` | 1-640 | Unit test patterns for auth commands |
| `tests/unit/test_cli_tenants.py` | 1-794 | Unit test patterns for tenants commands |

### Secondary References (Helpful Context)
| File | Lines | Purpose |
|------|-------|---------|
| `src/teams_phone/models/auth.py` | 10-102 | AuthStatusType, CachedToken, AuthStatus, ConnectionTestResult models |
| `src/teams_phone/exceptions.py` | 1-138 | Exception hierarchy with exit codes |
| `src/teams_phone/constants.py` | 55-97 | ExitCode enum definition |

### Code Patterns to Follow

```python
# Integration test pattern from conftest.py (tests/integration/cli/conftest.py:693-710)
@pytest.fixture
def mock_cli_services_auth(
    mock_cache_manager: MagicMock,
    mock_auth_service: MagicMock,
) -> Generator[dict[str, MagicMock], None, None]:
    """Patch all services needed for auth CLI commands."""
    with (
        patch("teams_phone.cli.auth.CacheManager", return_value=mock_cache_manager),
        patch("teams_phone.cli.auth.AuthService", return_value=mock_auth_service),
    ):
        yield {
            "cache_manager": mock_cache_manager,
            "auth_service": mock_auth_service,
        }
```

```python
# Unit test pattern from test_cli_auth.py (tests/unit/test_cli_auth.py:53-77)
@pytest.mark.unit
def test_status_authenticated(self) -> None:
    """status shows authenticated when valid token exists."""
    expires = datetime(2025, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
    mock_auth_status = AuthStatus(
        status=AuthStatusType.AUTHENTICATED,
        tenant_name="test-tenant",
        tenant_id="test-tenant-id-abc",
        expires_at=expires,
    )
    # Patching pattern...
    result = runner.invoke(app, ["--no-color", "auth", "status"])
    assert result.exit_code == 0
    assert "authenticated" in result.stdout.lower()
```

```python
# Tenants test pattern from test_cli_tenants.py (tests/unit/test_cli_tenants.py:336-359)
@pytest.mark.unit
def test_test_uses_current_tenant(self) -> None:
    """test uses current tenant when no name provided."""
    current = _make_tenant()
    test_result = ConnectionTestResult(
        success=True,
        tenant_name="test-tenant",
        latency_ms=150,
        permissions=["https://graph.microsoft.com/.default"],
    )
    # Patching and assertion pattern...
```

---

## Dependencies

### Internal Modules
- `teams_phone.cli.main.app` - Main Typer application
- `teams_phone.cli.auth.auth_app` - Auth command group
- `teams_phone.cli.tenants.tenants_app` - Tenants command group
- `teams_phone.models` - AuthStatus, AuthStatusType, CachedToken, ConnectionTestResult, TenantProfile
- `teams_phone.exceptions` - AuthenticationError, ConfigurationError, NotFoundError, ValidationError

### External Packages
- `pytest` - Test framework
- `typer.testing.CliRunner` - CLI testing utility
- `unittest.mock` - MagicMock, patch

### New Dependencies Needed
- None required

---

## Testing Guidance

### Test File Locations
- `tests/integration/cli/test_auth_integration.py` (to be created)
- `tests/integration/cli/test_tenants_integration.py` (to be created)

### Test Patterns in Use
```python
# Integration test structure from conftest patterns
import pytest
from typer.testing import CliRunner
from teams_phone.cli.main import app
from tests.integration.cli.conftest import make_auth_status, make_tenant

class TestAuthWorkflows:
    """Integration tests for auth command workflows."""

    @pytest.mark.integration
    def test_login_then_status(self, runner, mock_cli_services_auth):
        """Login flow followed by status check."""
        mocks = mock_cli_services_auth
        # Configure mocks
        mocks["auth_service"].login.return_value = make_cached_token()
        mocks["auth_service"].get_status.return_value = make_auth_status()

        # Login
        result = runner.invoke(app, ["auth", "login"])
        assert result.exit_code == 0

        # Check status
        result = runner.invoke(app, ["auth", "status"])
        assert result.exit_code == 0
```

### Fixtures Available
- `runner` - CliRunner instance for invoking CLI commands
- `mock_cli_services_auth` - Patches CacheManager, AuthService for auth commands
- `mock_cli_services_tenants` - Patches CacheManager, TenantService for tenants commands
- `mock_auth_service` - Individual AuthService mock
- `mock_tenant_service` - Individual TenantService mock

### Test Utilities
- `make_auth_status()` - Factory for AuthStatus
- `make_cached_token()` - Factory for CachedToken
- `make_tenant()` - Factory for TenantProfile

---

## Risks and Blockers

### Technical Risks
- [ ] **Fixture scope**: Composite fixtures are function-scoped, each test gets fresh mocks - may need to reconfigure mocks within each test

### Dependency Risks
- [ ] None - all dependencies complete

### Open Questions
- [ ] Should workflows test multiple commands in sequence or just individual command behavior? (Task description suggests workflows like "login then status")
- [ ] How to handle typer.confirm() prompts in tenants remove command - use `input="y\n"` or `--force` flag

---

## Implementation Recommendations

### Suggested Approach
1. Create `test_auth_integration.py` with `TestAuthWorkflows` class
2. Implement auth workflow tests following task details (7 tests)
3. Create `test_tenants_integration.py` with `TestTenantsWorkflows` class
4. Implement tenants workflow tests following task details (8 tests)
5. Run tests and verify all pass

### Files to Create/Modify
| Action | File | Reason |
|--------|------|--------|
| Create | `tests/integration/cli/test_auth_integration.py` | Auth integration tests |
| Create | `tests/integration/cli/test_tenants_integration.py` | Tenants integration tests |

### Quick Wins
- Start with `test_status_table_output` and `test_status_json_output` - simplest auth tests
- Reuse unit test patterns - unit tests already cover most scenarios

---

## Cheat Sheet

### Key Imports
```python
from __future__ import annotations

import json
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch

import pytest
from typer.testing import CliRunner

from teams_phone.cli.main import app
from teams_phone.exceptions import (
    AuthenticationError,
    ConfigurationError,
    NotFoundError,
)
from teams_phone.models import (
    AuthStatus,
    AuthStatusType,
    CachedToken,
    ConnectionTestResult,
    TenantProfile,
)

# Import factory functions from conftest
from tests.integration.cli.conftest import (
    make_auth_status,
    make_cached_token,
    make_tenant,
)
```

### Key Functions to Use
- `runner.invoke(app, ["auth", "status"])` - Invoke CLI command
- `runner.invoke(app, ["--json", "auth", "status"])` - Invoke with JSON output
- `runner.invoke(app, ["tenants", "remove", "name"], input="y\n")` - Invoke with prompt input
- `json.loads(result.stdout)` - Parse JSON output
- `mocks["auth_service"].login.return_value = make_cached_token()` - Configure mock return

### Common Patterns
- **Table output assertion**: `assert "authenticated" in result.stdout.lower()`
- **JSON output assertion**: `data = json.loads(result.stdout); assert data["status"] == "authenticated"`
- **Exit code assertion**: `assert result.exit_code == 0` (success) or `assert result.exit_code == 2` (auth error)
- **Service call assertion**: `mocks["auth_service"].login.assert_called_once_with(None, force=False)`

### Exit Codes Reference
| Exit Code | Exception | Meaning |
|-----------|-----------|---------|
| 0 | - | Success |
| 1 | TeamsPhoneError | General error |
| 2 | AuthenticationError | Auth failure |
| 4 | NotFoundError | Resource not found |
| 5 | ValidationError | Validation failure |
| 7 | ConfigurationError | Config error |

### Tests to Implement (from task details)

**test_auth_integration.py - TestAuthWorkflows:**
1. `test_login_then_status` - Login flow followed by status check verifying AUTHENTICATED state
2. `test_logout_then_status` - Logout followed by status showing UNAUTHENTICATED
3. `test_status_table_output` - Verify table format includes tenant, status, expiry columns
4. `test_status_json_output` - Verify JSON structure with json.loads() validation
5. `test_refresh_expired_token` - Test token refresh scenario with force flag
6. `test_login_already_authenticated` - Test --force flag behavior when already logged in
7. `test_logout_all_tenants` - Test --all flag clears all tenant tokens

**test_tenants_integration.py - TestTenantsWorkflows:**
1. `test_add_then_list_tenant` - Add tenant and verify it appears in list
2. `test_add_then_show_tenant` - Add tenant and verify show displays all config
3. `test_switch_default_tenant` - Switch tenant and verify it becomes current
4. `test_remove_tenant` - Remove tenant and verify it's gone from list
5. `test_test_connection_success` - Mock successful Graph API connection
6. `test_test_connection_failure` - Mock connection failure with proper error
7. `test_list_marks_current_tenant` - Verify 'Current' indicator in list output
8. `test_tenants_json_output` - All commands produce valid JSON with --json flag

---

*Generated by task-recon (claude-opus-4-5-20251101) | Ready for implementation*
