# Sprint Contract — engine-bridge v2 (MEF plugin masquerade) **Owner:** Generator **Depends on:** engine-bridge v1 (완료) **Issue:** #10 ## Goal `engine-bridge` v1 probe design 문서가 권고한 **MEF plugin masquerade** 경로를 구현한다. recordingtest 전용 플러그인을 빌드해 `EG-BIM Modeler/Plugins/`에 drop-in하면, SUT가 로드할 때 플러그인이 로컬 HTTP 서버를 띄워 HmEG 상태(선택/카메라/씬/렌더)를 JSON으로 노출한다. recordingtest-side 클라이언트는 HTTP로 상태를 조회한다. PoC 범위: 플러그인 + 클라이언트 빌드 + 엔드투엔드 HTTP 테스트 (가짜 엔진 상태 주입). **SUT 실제 실행은 수동 smoke test 단계에서.** ## Definition of Done - [ ] `src/Recordingtest.EgPlugin/` — MEF plugin dll - Target: `net8.0-windows` - `Editor03.PluginInterface.dll`을 `HintPath`로 참조 (Private=false, SUT의 정본 복사 금지) - `Editor03.PluginInterface.dll`의 **실제 계약을 MetadataLoadContext로 분석**해서 구현할 최소 interface/attribute를 확인 (Generator가 discovery 수행) - 플러그인 로드 시점에 `HttpListener`로 `http://localhost:{port}/` 시작 (기본 포트 `38080`, 환경변수 `RECORDINGTEST_BRIDGE_PORT`로 override) - 엔드포인트: `/selection`, `/camera`, `/scene`, `/render`, `/health` - 각 엔드포인트는 `IEngineStateProvider` 인터페이스로부터 JSON을 반환 - 기본 `ReflectionEngineStateProvider`는 HmEG 내부 타입을 리플렉션으로 찾아 상태를 구성 (실패해도 예외 삼킴 — `{error: "..."}` 반환) - 플러그인 unload 시 HttpListener 정리 - [ ] `src/Recordingtest.EngineBridge.Client/` — HTTP 클라이언트 라이브러리 - `HmEgHttpSnapshot : IEngineSnapshot` 구현 (v1 인터페이스 재사용) - `HttpClient`로 엔드포인트 호출, 타임아웃 기본 2초 - 각 속성은 on-demand HTTP GET (caching 없음 v2) - `IsRenderComplete`는 `/render` 응답의 `complete` 필드 - [ ] `tests/Recordingtest.EngineBridge.IntegrationTests/` — xUnit - **fake HTTP 서버**: `HttpListener`를 테스트 안에서 띄우고 고정 JSON 응답 - 테스트 ≥ 6: 1. `Client_SelectionEndpoint_ReturnsIds` 2. `Client_CameraEndpoint_ReturnsCameraState` 3. `Client_SceneEndpoint_ReturnsSceneSummary` 4. `Client_RenderEndpoint_ReturnsIsComplete` 5. `Client_HealthEndpoint_ReturnsOk` 6. `Client_Timeout_ThrowsOrReturnsError` (2초 이내 미응답 시) - [ ] `src/Recordingtest.EgPlugin` 단위 테스트 ≥ 3 (플러그인 로직) - 테스트에서 **실제 plugin dll을 SUT에 주입하지 않음**. 순수 로직만. - `StateRouter_SerializesSelection_ToJson` - `StateRouter_WithFaultyProvider_ReturnsErrorPayload` - `PortResolver_PrefersEnvVar` (`RECORDINGTEST_BRIDGE_PORT`) - [ ] `docs/guides/engine-bridge-deploy.md` — 수동 배포 가이드 (SUT Plugins/ 폴더에 dll 복사, 환경변수 설정, 검증 절차) - [ ] `dotnet build` + `dotnet test` 전부 green - [ ] 플러그인이 SUT 자체 파일을 건드리지 않음 (guard hook 준수) ## Out of scope - SUT 실제 실행 (수동 smoke test) - 인증/암호화 (PoC는 localhost only) - 멀티클라이언트 / 동시성 (단일 HttpListener) - HmEG 리플렉션 매핑 완성도 — v2는 skeleton + error fallback 중심, 진짜 매핑은 smoke test 후 조정 ## Interfaces - **Inputs:** SUT가 plugin을 로드할 때의 MEF 계약 - **Outputs:** `http://localhost:/{selection|camera|scene|render|health}` JSON - **Side effects:** HttpListener 포트 점유 (플러그인 생명주기 내) ## Evaluation plan 1. `dotnet build recordingtest.sln` green 2. `dotnet test tests/Recordingtest.EngineBridge.IntegrationTests` — 6 pass 3. `dotnet test tests/Recordingtest.EgPlugin.Tests` — 3 pass 4. `Recordingtest.EgPlugin.dll` 출력 확인, `Editor03.PluginInterface`에 대한 HintPath/Private=false 확인 (csproj 리뷰) 5. Generator가 Editor03.PluginInterface.dll 메타데이터에서 발견한 실제 인터페이스 이름을 history에 기록했는지 확인 6. `docs/guides/engine-bridge-deploy.md` 존재 + 배포 단계(복사·환경변수·검증) 기술 7. SUT 폴더에 쓰기 흔적 없음 (grep) 8. Plugin 코드가 `HttpListener` 예외 상황(port 충돌, stop) 처리 ## Risks - `Editor03.PluginInterface`가 WPF dependency를 가지면 net8.0-windows로 충분하지 않을 수 있음 — 필요 시 TFM 조정 - 플러그인 dll이 SUT와 같은 디렉터리의 다른 dll 버전과 충돌 가능 — Private=false 필수 - HmEG 리플렉션 실제 매핑은 SUT 런타임에서만 검증 가능 → v2는 error fallback 철저