# Testing Patterns

**Analysis Date:** 2026-05-07

## Test Framework

**Runner:**
- Django's built-in `TestCase` from `django.test`
- NO pytest or pytest-django detected in `requirements.txt`
- Tests run via: `python manage.py test`

**Assertion Library:**
- Django's `TestCase` built-in assertions (inherited from `unittest.TestCase`)
- `self.assertEqual()`, `self.assertTrue()`, `self.assertRaises()`, etc.

**Run Commands:**
```bash
python manage.py test              # Run all tests
python manage.py test app_name     # Run tests for specific app
python manage.py test app_name.tests.TestClassName  # Run specific test class
```

**Coverage:**
- No `.coveragerc` or coverage configuration found
- No coverage requirement enforced

## Test File Organization

**Location:**
- Each Django app has a `tests.py` file in app root: `{app_name}/tests.py`
- Pattern: co-located with models, serializers, views (one file per app)

**Naming:**
- Single file: `tests.py`
- Test classes inherit from `django.test.TestCase`
- Test methods prefixed with `test_`: `test_audio()`, `test_model_creation()`, `test_view_response()`

**File Structure:**
```
app_name/
├── models.py
├── serializers.py
├── views.py
├── urls.py
└── tests.py          # All tests for this app
```

## Current Test Coverage Status

**Honest Assessment: MINIMAL**

Test files examined show very sparse coverage:
- `userapp/tests.py`: 4 lines (empty TestCase class)
- `aiapp/tests.py`: 24 lines (only 1 actual test: `test_audio()`)
- `card/tests.py`: 3 lines (empty)
- `device_interaction/tests.py`: 3 lines (empty)
- `achievement_app/tests.py`: 3 lines (empty)

**Total active test methods: 1** (audio synthesis in `aiapp/tests.py`)

This represents **critical test coverage gap** — almost all API endpoints, models, and serializers are untested.

## Test Structure

**Suite Organization from `aiapp/tests.py`:**
```python
from django.test import TestCase
from django.urls import reverse
from .audio.AudioService import get_audio_service

class YourModelTest(TestCase):
    
    def test_audio(self):
        # Setup (minimal)
        audio_ser = get_audio_service()
        audio_ser.synthesize_speech('你好，你是谁啊')
    
    # Commented-out template tests below (never implemented)
    # def test_model_creation(self):
    # def test_view_response(self):
```

**Patterns:**
- `setUp()` method not used; tests don't create fixtures
- No tearDown() cleanup observed
- No assertion calls in active test (test_audio just calls synthesize_speech without checking result)

## Mocking

**Framework:** No mocking library detected (no `unittest.mock`, `responses`, `pytest-mock` in requirements.txt)

**Current Practice:**
- Tests that DO exist (test_audio) call real external services (AudioService.synthesize_speech)
- No mocking observed for:
  - Aliyun API calls (SMS, OSS, NLS)
  - Volcengine RTC token generation
  - Tencent audio service
  - Kimi AI API
  - Redis cache operations
  - Database operations (use real test DB)

**Risk:** Tests that call production services (Aliyun, Kimi, Volcengine) require:
- Valid credentials in .env
- Network access to external services
- Potential cost (SMS sends, API calls)

## Database Testing

**Test Database:**
- Django uses a separate test database (by default: test_qy_lty or suffixed with `test_`)
- No custom settings for test DB observed
- No fixtures or data factories defined

**Patterns:**
- Models have no test data factories (no factory_boy or similar)
- No `setUp()` creating test objects
- Manual creation would be required: `Model.objects.create(...)`

## Fixtures and Test Data

**Test Data:**
- NO fixture files found (.json, .yaml, .fixture)
- NO factory definitions (factory_boy not in requirements)
- NO model-level test data builders

**How to Add:**
If implementing tests, would need:
```python
def setUp(self):
    self.user = ParadiseUser.objects.create(
        username='testuser',
        email='test@example.com'
    )
    self.device = Device.objects.create(
        device_type=...,
        batch=...,
        mac_address='AA:BB:CC:DD:EE:FF'
    )
```

## WebSocket Consumer Testing

**Current State:** NO WebSocket consumer tests

**Channels provides `channels.testing` package** for async testing, but:
- No `conftest.py` or pytest configuration
- No async test runner configured
- Would require adding pytest + pytest-asyncio + pytest-django

**If implementing WebSocket tests, would require:**
```python
from channels.testing import WebsocketCommunicator
from device_interaction.consumers import DeviceConsumer

async def test_device_consumer_connect():
    communicator = WebsocketCommunicator(DeviceConsumer.as_asgi(), '/ws/device/')
    connected, subprotocol = await communicator.connect()
    assert connected
```

## External Service Testing

**Services integrated WITHOUT mocking in current codebase:**
- `aiapp.audio.AudioService` — calls real Aliyun/Tencent/Volcengine
- Aliyun SMS (send_sms in userapp/utils.py)
- Aliyun OSS (file uploads)
- Kimi API (AI chat)
- Volcengine RTC token generation
- WeChat OAuth (django-allauth)

**No test doubles, stubs, or mocks exist** — tests would hit production services.

## API Endpoint Testing

**Current State:** NO API tests

**Endpoints that should be tested but are NOT:**
- User authentication: `/api/user/register/`, `/api/user/mac-login/`, `/api/user/phone-login/`
- AI chat: `/api/ai/chat/{bot_id}/`, `/api/ai/multi-chat/`
- Device binding: `/api/device/bind/`, `/api/device/bind-status/`
- Card operations: `/api/card/scan/`, `/api/card/use/`, `/api/card/batches/generate/`
- Achievement tracking: `/api/achievement/user-achievements/`
- WebSocket: `/ws/device/` connection and message handling

**If implementing, would use DRF test client:**
```python
from rest_framework.test import APIClient

class UserAuthTest(TestCase):
    def setUp(self):
        self.client = APIClient()
    
    def test_mac_login(self):
        response = self.client.post('/api/user/mac-login/', {
            'mac_address': 'AA:BB:CC:DD:EE:FF'
        })
        self.assertEqual(response.status_code, 200)
        self.assertIn('token', response.json()['data'])
```

## Test Coverage Gaps

**CRITICAL UNTESTED AREAS:**

1. **Authentication layer** — RedisTokenAuthentication never tested
   - Token generation in userapp/utils.py:generate_token()
   - Token validation in userapp/authentication.py
   - Device MAC login flow
   - Phone verification login

2. **Device control semantics** — "last-bind-wins" ordering never verified
   - UserDevice.Meta.ordering = ['-bound_at'] not tested
   - Multiple users binding same device — control resolution untested
   - Hardcoded test MAC (AA:BB:CC:DD:EE:FF) skip logic untested

3. **WebSocket messaging** — All consumer methods untested
   - DeviceConsumer.connect() authentication
   - Message routing in receive()
   - Group messaging (device_{user_id})
   - Disconnect and heartbeat (device:last_seen:{mac})

4. **Serializer validation** — No validation tests
   - All custom validators in serializers.py files untested
   - Foreign key constraints untested
   - Unique field constraints untested

5. **External service integration** — No mocking
   - Aliyun SMS send (send_sms)
   - Aliyun OSS upload
   - Kimi AI API calls
   - Volcengine RTC token generation
   - Tencent audio service

6. **Async task processing** — No tests
   - Affinity rule evaluation (coming in P2)
   - Device heartbeat refresh (5-min TTL)
   - Daily affinity counter aggregation

7. **Model business logic** — No tests
   - AffinityRule.get_solo() singleton pattern
   - Device code generation (generate_device_code)
   - User achievement unlock logic
   - Card batch generation logic

## Recommendations for Test Implementation

**Phase 1 - Setup (High Priority):**
- Add pytest + pytest-django + pytest-asyncio to requirements.txt
- Create conftest.py for shared fixtures
- Create factory definitions (factory_boy) for common models
- Mock external services (responses, unittest.mock)

**Phase 2 - Unit Tests (High Priority):**
- Test authentication (token generation, validation, expiry)
- Test device binding semantics (last-bind-wins)
- Test serializer validation
- Test model methods (AffinitySetting.get_solo, generate_device_code)

**Phase 3 - Integration Tests (Medium Priority):**
- Test full auth flows (MAC login, phone login, registration)
- Test API endpoints with mocked external services
- Test WebSocket consumer connection and messaging
- Test card batch generation workflow

**Phase 4 - E2E Tests (Lower Priority):**
- Test device binding → WebSocket connection → message exchange flow
- Test achievement unlock on user action flow
- Test affinity rule trigger and log creation

**Minimum Coverage Target for Production:**
- Authentication/authorization: 80%+
- Device control logic: 100% (critical for device binding semantics)
- Serializer validation: 70%+
- API endpoints: 60%+ (focus on public-facing, critical flows)

---

*Testing analysis: 2026-05-07*

## Test Execution Example

Current working test (from `aiapp/tests.py`):
```bash
$ python manage.py test aiapp.tests.YourModelTest.test_audio
```

This test:
1. Gets AudioService instance
2. Calls synthesize_speech with Chinese text
3. Returns (no assertions — test just checks for uncaught exceptions)

This represents the ONLY active test in the codebase and does not verify behavior, only that the call completes.
