2026-05-07 10:37:16 +08:00

293 lines
9.2 KiB
Markdown

# 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.