Skip to main content

derive-tests

Derive obligation tests using the test harness. First checks for harness availability, then generates compact tests using domain-specific test environments. Hard gate: no harness -> no tests (stops immediately). Replaces obligation-test for harness-enabled domains.

설치로 이동

소스 정보

저장소
prebid/salesagent
최근 소스 활동
2026년 3월 19일 19:01
감지된 SKILL.md 언어
영어
스타
37
포크
53

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
derive-tests
description
Derive obligation tests using the test harness. First checks for harness availability, then generates compact tests using domain-specific test environments. Hard gate: no harness -> no tests (stops immediately). Replaces obligation-test for harness-enabled domains.
args
<obligation-ids-or-prefix> [--count N]
# Harness-Based Obligation Test Derivation Write one behavioral test per obligation using the test harness (`tests/harness/`). Tests use domain-specific test environments instead of inline `@patch` decorators, producing 10-15 line tests instead of 40-50 line tests. ## Args ``` /derive-tests UC-004-MAIN-01 UC-004-MAIN-02 /derive-tests UC-004 --count 10 /derive-tests UC-001-MAIN --count 15 ``` **Direct IDs**: Space-separated obligation IDs. **Prefix mode**: Auto-selects uncovered obligations matching the prefix. ## Available Harness Environments | Domain | Env Class | Tests | Production Function | |--------|-----------|-------|---------------------| | Delivery poll | `DeliveryPollEnv` | `test_delivery_poll_behavioral.py` | `_get_media_buy_delivery_impl` | | Webhook delivery | `WebhookEnv` | `test_delivery_webhook_behavioral.py` | `deliver_webhook_with_retry` | | Circuit breaker | `CircuitBreakerEnv` | `test_delivery_service_behavioral.py` | `WebhookDeliveryService`, `CircuitBreaker` | | Creative sync | `CreativeSyncEnv` | `test_creative_sync_behavioral.py` | `_sync_creatives_impl` | | Creative list | `CreativeListEnv` | `test_creative_list_behavioral.py` | `_list_creatives_impl` | | Creative formats | `CreativeFormatsEnv` | `test_creative_formats_behavioral.py` | `_list_creative_formats_impl` | **Multi-transport support:** Creative envs support `call_via(transport)` dispatch across IMPL, A2A, and REST transports. See [Multi-Transport Pattern](#multi-transport-pattern). ## Protocol ### Step 0: Resolve obligation IDs If args look like a prefix (no trailing `-NN` sequence number): ```bash python3 -c " import json al = json.loads(open('tests/unit/obligation_coverage_allowlist.json').read()) matches = sorted(oid for oid in al if oid.startswith('{prefix}')) print(' '.join(matches[:N])) print(f'Total matching: {len(matches)}, selected: {min(N, len(matches))}') " ``` ### Step 1: Harness gate (HARD STOP) Check if a harness exists for the target domain: ```bash ls tests/harness/*.py ``` Map obligation prefix to required harness. For the complete, auto-generated list of all env classes with their methods, read `.agent-index/harness/envs.pyi`. **If harness missing: STOP IMMEDIATELY.** Print: ``` No harness found for {domain}. Create tests/harness/{domain}.py first. See tests/harness/_base.py for the IntegrationEnv pattern. ``` Do NOT fall back to inline mocking. Generating tests without a harness produces architecturally poor code that wastes tokens. **If harness exists: CONTINUE.** ### Step 2: Read harness API + gold standard 1. **Read the harness class** for available methods: ```bash # Example for delivery poll: head -60 tests/harness/delivery_poll.py ``` Extract: class docstring, fluent API methods, call_impl signature. 2. **Read the meta-test file** for usage examples: ```bash # Example: cat tests/harness/test_harness_delivery_poll.py ``` These are your gold standard — tests use the exact same pattern. 3. **Read 1-2 converted tests** in the target file for style reference: ```bash # Look for tests already using the harness: grep -A 15 "DeliveryPollEnv\|WebhookEnv\|CircuitBreakerEnv" tests/unit/test_delivery_*_behavioral.py ``` ### Step 3: Research obligation For each obligation: 1. **Read the scenario** from `docs/test-obligations/`: ```bash grep -A 10 "{OID}" docs/test-obligations/*.md ``` Extract: Given/When/Then, business rule, priority, layer. 2. **Translate Given/When/Then directly into the test**: - **Given** → test setup (fixtures, env configuration) - **When** → action (call production function) - **Then** → assertions (expected output/state) The BDD spec is the **sole source** of expected behavior. Do NOT derive assertions from what the production code currently does. If the spec says "Then a SyncCreativesSubmitted is returned," assert `isinstance(result, SyncCreativesSubmitted)` — even if the code currently returns something else or doesn't implement the behavior at all. 3. **Check if production code implements it**: Locate the `_impl` function. - **Implemented** → test should PASS. If it doesn't, the code has a bug. - **Not implemented** → test MUST still assert spec behavior, marked with `@pytest.mark.xfail(strict=True, reason="<what's missing>")`. Never write a test that asserts current (wrong) behavior just because the spec behavior doesn't exist yet. That legitimizes the gap. ### Step 4: Write test Write ONE test following all 7 hard rules: | # | Rule | Check | |---|------|-------| | 1 | Import from `src.` | `from src.` in test file | | 2 | Call production function | Test calls `env.call_impl()` or production function | | 3 | Assert production output | Assertion checks a value from the production call | | 4 | `Covers: {OID}` tag | Docstring contains exactly `Covers: {OID}` | | 5 | Use harness env | Test uses harness context manager, not inline `@patch` | | 6 | Not mock-echo only | Does more than verify `mock.called` | | 7 | **No inline @patch** | MUST NOT define `@patch` decorators or inline UoW setup | **Test template (delivery poll — integration, preferred):** ```python import pytest from tests.factories import TenantFactory, PrincipalFactory, MediaBuyFactory @pytest.mark.requires_db class TestObligationName: """Short description of what's being tested. Covers: {OID} """ def test_specific_behavior(self, integration_db): """What specific behavior is verified. Covers: {OID} """ from tests.harness import DeliveryPollEnv with DeliveryPollEnv() as env: tenant = TenantFactory(tenant_id="t1") principal = PrincipalFactory(tenant=tenant, principal_id="p1") buy = MediaBuyFactory(tenant=tenant, principal=principal) env.set_adapter_response(buy.media_buy_id, impressions=5000) response = env.call_impl(media_buy_ids=[buy.media_buy_id]) assert response.some_field == expected_value ``` **Test template (delivery poll — unit, backward compat):** ```python class TestObligationName: """Short description. Covers: {OID} """ def test_specific_behavior(self): """What is verified. Covers: {OID} """ from tests.harness.delivery_poll_unit import DeliveryPollEnv with DeliveryPollEnv() as env: env.add_buy(media_buy_id="mb_001", ...) env.set_adapter_response("mb_001", impressions=5000) response = env.call_impl(media_buy_ids=["mb_001"]) assert response.some_field == expected_value ``` **Test template (webhook):** ```python class TestObligationName: """Short description. Covers: {OID} """ def test_specific_behavior(self): """What is verified. Covers: {OID} """ from tests.harness import WebhookEnv with WebhookEnv() as env: env.set_http_status(503, "Unavailable") success, result = env.call_deliver(max_retries=3) assert success is False assert result["attempts"] == 3 ``` **Test template (circuit breaker):** ```python class TestObligationName: """Short description. Covers: {OID} """ def test_specific_behavior(self): """What is verified. Covers: {OID} """ from tests.harness import CircuitBreakerEnv with CircuitBreakerEnv() as env: breaker = env.get_breaker(failure_threshold=3) for _ in range(3): breaker.record_failure() assert breaker.state == CircuitState.OPEN ``` ### Step 5: Run and fix ```bash uv run pytest <test_file>::<TestClass>::<test_method> -x -v ``` - PASS or XFAIL = proceed - ERROR = fix (import/name errors, wrong harness method) - If the harness doesn't expose what you need, use `env.mock["name"]` to access the underlying mock directly ### Step 6: Verify Six mechanical checks: 1. `grep "from src\." <file>` > 0 2. `grep "Covers: {OID}" tests/` count == 1 3. Test runs without ERROR 4. `make quality` passes 5. No duplicate Covers tags 6. If test PASSES: remove OID from allowlist, add file to `_UNIT_ENTITY_FILES` if needed, run obligation guard ### Step 7: Commit ```bash git add <test_file> tests/unit/obligation_coverage_allowlist.json git add tests/unit/test_architecture_obligation_coverage.py # if changed git commit -m "test: add obligation test for {OID}" ``` ## Multi-Transport Pattern When an obligation has transport variants in upstream BDD (e.g., both `T-UC-006-main-rest` and `T-UC-006-main-mcp`), generate ONE parametrized test instead of two separate tests. **Decision rule:** 1. Check if upstream BDD duplicates the scenario across REST/MCP transports 2. If yes → parametrize with `ALL_TRANSPORTS` 3. If no → use single transport (`Transport.IMPL`) **Template (multi-transport — creative domains):** ```python import pytest from tests.factories import TenantFactory, PrincipalFactory from tests.harness import CreativeSyncEnv, Transport, assert_envelope ALL_TRANSPORTS = [Transport.IMPL, Transport.A2A, Transport.REST] @pytest.mark.requires_db class TestObligationName: """Short description. Covers: T-UC-006-xxx-rest, T-UC-006-xxx-mcp """ @pytest.mark.parametrize("transport", ALL_TRANSPORTS, ids=lambda t: t.value) def test_specific_behavior(self, integration_db, transport): """What is verified. Covers: T-UC-006-xxx-rest, T-UC-006-xxx-mcp """ with CreativeSyncEnv() as env: # SHARED FIXTURE TenantFactory(tenant_id="test_tenant") PrincipalFactory(tenant_id="test_tenant", principal_id="test_principal") # TRANSPORT DISPATCH result = env.call_via(transport, creatives=[...]) # ENVELOPE (transport-specific) if transport == Transport.REST: assert_envelope(result, Transport.REST) # PAYLOAD (shared — identical for all transports) assert result.is_success assert result.payload.creatives[0].action == expected_action ``` **Transport-specific behaviors** (e.g., REST returns 401, MCP raises ToolError) get separate non-parametrized tests — don't mix them into the shared test. ### Creative Harness API ```python from tests.harness import CreativeSyncEnv, CreativeListEnv, CreativeFormatsEnv, Transport # Sync creatives (multi-transport) with CreativeSyncEnv() as env: env.set_registry_formats([...]) result = env.call_via(Transport.REST, creatives=[...], dry_run=True) # result.is_success, result.payload, result.envelope # List creatives (multi-transport) with CreativeListEnv() as env: result = env.call_via(Transport.A2A, media_buy_id="mb_001") # List formats (multi-transport) with CreativeFormatsEnv() as env: env.set_registry_formats([...]) result = env.call_via(Transport.IMPL) ``` All three envs support: `call_via(transport)`, `call_impl()`, `call_a2a()`, `build_rest_body()`, `parse_rest_response()`. ## Test File Selection | Domain | File | |--------|------| | UC-004 delivery poll | `test_delivery_poll_behavioral.py` | | UC-004 webhook | `test_delivery_webhook_behavioral.py` | | UC-004 service/CB | `test_delivery_service_behavioral.py` | | UC-002 | `test_create_media_buy_behavioral.py` | | UC-003 | `test_update_media_buy_behavioral.py` | | UC-006 sync (multi-transport) | `test_creative_sync_transport.py` | | UC-006 sync (impl only) | `test_creative_sync_behavioral.py` | | UC-006 list (impl only) | `test_creative_list_behavioral.py` | | UC-006 formats (impl only) | `test_creative_formats_behavioral.py` | New files must be added to `_UNIT_ENTITY_FILES` in `tests/unit/test_architecture_obligation_coverage.py`. ## Harness API Quick Reference ### DeliveryPollEnv ```python from tests.harness import DeliveryPollEnv with DeliveryPollEnv(principal_id="p1", tenant_id="t1") as env: # Add mock media buys to the repository
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기