feat: m-client-validation task and README updates

- Add m-client-validation task with 02_native_test plan (PLAN-cloud-G07.md)
- Update roadmap current.md and product-foundation phase
- Update client-validation milestone
- Add README for apps/flutter-test and packages/messaging_flutter
This commit is contained in:
toki 2026-05-29 09:26:51 +09:00
parent ecf65493b6
commit 81bd42756d
7 changed files with 281 additions and 5 deletions

View file

@ -7,7 +7,7 @@
## 활성 Milestone
- [계획] 클라이언트 검증 기준선
- [진행중] 클라이언트 검증 기준선
- Phase: `agent-roadmap/phase/product-foundation/PHASE.md`
- 경로: `agent-roadmap/phase/product-foundation/milestones/client-validation.md`

View file

@ -24,7 +24,7 @@ nexo를 Mattermost 기반 서버/front/push-proxy 런타임과 nexo-owned Flutte
- [완료] 이미지 기반 런타임 검증
- 경로: `agent-roadmap/archive/phase/product-foundation/milestones/runtime-image-validation.md`
- 요약: nexo-owned Mattermost/push-proxy image build, 배포/수신 방식, 원격 Mac runtime 전환, 현 repo와 소비 프로젝트 smoke/e2e 검증 기준선을 만들고 잔여 Detox app-readiness 실패를 후속으로 분리했다.
- [계획] 클라이언트 검증 기준선
- [진행중] 클라이언트 검증 기준선
- 경로: `agent-roadmap/phase/product-foundation/milestones/client-validation.md`
- 요약: `apps/flutter-test`가 플러그인 소비 앱이자 테스트 호스트로 반복 검증을 제공하도록 고정한다.

View file

@ -12,7 +12,7 @@
## 상태
[계획]
[진행중]
## 승격 조건
@ -37,10 +37,10 @@
`apps/flutter-test` 앱을 플러그인 통합 검증에 필요한 만큼만 유지한다.
- [ ] [thin-client] `apps/flutter-test`가 제품 UI가 아니라 plugin integration host 역할을 유지하도록 README와 테스트가 같은 기준을 따른다.
- [x] [thin-client] `apps/flutter-test`가 제품 UI가 아니라 plugin integration host 역할을 유지하도록 README와 테스트가 같은 기준을 따른다.
- [ ] [integration-path] plugin method channel, event channel, opened-routing 통합 테스트 경로를 유지한다. 검증: `cd apps/flutter-test && flutter test integration_test`가 지원 환경에서 성공한다.
- [ ] [native-test] Android native unit test 실행 절차를 유지한다. 검증: `cd apps/flutter-test/android && ./gradlew testDebugUnitTest`가 Android SDK 환경에서 성공한다.
- [ ] [sdk-boundary] Flutter SDK Android 구현은 Mattermost mobile code를 팔로잉하지 않고 server/push contract와 Android/FCM platform 변화만 추적한다는 기준을 문서화한다.
- [x] [sdk-boundary] Flutter SDK Android 구현은 Mattermost mobile code를 팔로잉하지 않고 server/push contract와 Android/FCM platform 변화만 추적한다는 기준을 문서화한다.
### Epic: [manual-smoke] 외부 인프라 smoke
@ -69,6 +69,8 @@
- 관련 경로: `apps/flutter-test/`, `packages/messaging_flutter/`, `packages/messaging_flutter/docs/android-test-environment.md`
- 표준선(선택): client는 실제 소비 앱 모양의 검증 호스트이며 별도 `/example`이나 `/sandbox`를 두지 않는다. Flutter SDK는 nexo-owned embedded SDK로 유지한다
- 진행 메모: `thin-client``apps/flutter-test/README.md``apps/flutter-test/test/widget_test.dart` 기준을 맞췄다. `sdk-boundary``packages/messaging_flutter/README.md`의 SDK ownership/test strategy 기준을 보강했다.
- 활성 계획: `agent-task/m-client-validation/01_integration_path/`, `agent-task/m-client-validation/02_native_test/`
- 선행 작업: client migration과 1차 테스트
- 후속 작업: 메시징 계약 표준화, 알림 파이프라인 고도화
- 확인 필요: 없음

View file

@ -0,0 +1,139 @@
<!-- task=m-client-validation/02_native_test plan=0 tag=TEST -->
# Code Review Reference - TEST
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a user-only decision, external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.
## 개요
date=2026-05-29
task=m-client-validation/02_native_test, plan=0, tag=TEST
## 이 파일을 읽는 리뷰 에이전트에게
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md``code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md``plan_cloud_G07_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-client-validation/02_native_test/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
---
## 구현 항목별 완료 여부
| 항목 | 완료 여부 |
|------|---------|
| [TEST-1] Android Gradle native unit test execution | [ ] |
## 구현 체크리스트
- [ ] Android SDK/Gradle preflight를 확인하고, 없으면 `사용자 리뷰 요청`에 실제 출력과 재개 조건을 기록한다.
- [ ] `cd apps/flutter-test/android && ./gradlew testDebugUnitTest`가 Android SDK 환경에서 성공하도록 확인한다. 실패가 제품 결함이면 이 계획의 파일 경계 안에서 최소 수정 후 같은 명령을 재실행한다.
- [ ] Native unit test coverage gaps are not expanded unless the command reveals a failing product behavior in the existing tests.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 코드리뷰 전용 체크리스트
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
- [ ] `코드리뷰 결과``PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [ ] active `CODE_REVIEW-*-G??.md``code_review_cloud_G07_N.log`로 아카이브한다.
- [ ] active `PLAN-*-G??.md``plan_cloud_G07_M.log`로 아카이브한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-client-validation/02_native_test/``agent-task/archive/YYYY/MM/m-client-validation/02_native_test/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-client-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md``CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
## 계획 대비 변경 사항
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
## 주요 설계 결정
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
## 사용자 리뷰 요청
_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. code-review가 이 내용을 검증해 `USER_REVIEW.md`를 작성한다._
- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 재개 조건: 없음
## 리뷰어를 위한 체크포인트
- Android SDK/Flutter SDK metadata 부재를 제품 실패로 고치지 않았는지 확인한다.
- `./gradlew testDebugUnitTest` output이 실제 Gradle 실행 결과인지 확인한다.
- native coverage expansion을 이 계획 범위 밖으로 키우지 않았는지 확인한다.
## 검증 결과
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
### TEST-1 중간 검증
```sh
$ command -v java
(output)
$ cd apps/flutter-test/android && ./gradlew --version
(output)
$ cd apps/flutter-test/android && ./gradlew testDebugUnitTest
(output)
```
### 최종 검증
```sh
$ command -v java
(output)
$ cd apps/flutter-test/android && ./gradlew --version
(output)
$ cd apps/flutter-test/android && ./gradlew testDebugUnitTest
(output)
```
---
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.
Sections and their ownership:
| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` -> `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` -> `[x]` only |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless user input is required |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 | Implementing agent | Fill command output only |

View file

@ -0,0 +1,119 @@
<!-- task=m-client-validation/02_native_test plan=0 tag=TEST -->
# Android Native Unit Test Plan
## 이 파일을 읽는 구현 에이전트에게
`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 단계다. 검증을 실제로 실행하고, 실제 stdout/stderr를 기록하고, active 파일을 그대로 둔 채 리뷰 준비를 보고한다. 사용자 결정, 외부 Android SDK 준비, 또는 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거와 재개 조건을 적고 멈춘다. `USER_REVIEW.md`, archive log, `complete.log` 작성은 code-review 전용이다.
## 배경
현재 Milestone `클라이언트 검증 기준선``test-host` Epic에는 `native-test` 검증이 남아 있다. 이 작업은 `apps/flutter-test/android`에서 플러그인의 Android native unit test가 실행되는지 확인한다. Android SDK와 Gradle 환경 의존성이 있어 작은 문서 정리와 분리해 계획 작업으로 둔다.
## 사용자 리뷰 요청 흐름
구현 중 차단 조건은 active `CODE_REVIEW-cloud-G07.md``사용자 리뷰 요청` 섹션에 기록한다. 형식은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`를 따른다. code-review가 정당성을 검증한 뒤 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
## 분석 결과
### 읽은 파일
- `agent-roadmap/current.md`
- `agent-roadmap/phase/product-foundation/PHASE.md`
- `agent-roadmap/phase/product-foundation/milestones/client-validation.md`
- `agent-ops/rules/project/domain/client-app/rules.md`
- `agent-ops/rules/project/domain/messaging-flutter/rules.md`
- `apps/flutter-test/README.md`
- `apps/flutter-test/android/settings.gradle.kts`
- `apps/flutter-test/android/build.gradle.kts`
- `apps/flutter-test/android/app/build.gradle.kts`
- `apps/flutter-test/android/app/src/main/AndroidManifest.xml`
- `apps/flutter-test/pubspec.yaml`
- `packages/messaging_flutter/README.md`
- `packages/messaging_flutter/docs/android-test-environment.md`
- `packages/messaging_flutter/android/build.gradle.kts`
- `packages/messaging_flutter/android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPlugin.kt`
- `packages/messaging_flutter/android/src/test/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPluginTest.kt`
### 테스트 커버리지 공백
- Gradle execution path: `apps/flutter-test/README.md:79` documents `./gradlew testDebugUnitTest`, but active task evidence is still needed.
- Native plugin method handling: `packages/messaging_flutter/android/src/test/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPluginTest.kt:28` covers invalid `debugSendNativeEvent`; `:61`, `:93`, `:125`, and `:157` cover argument validation for token/auth/signing/clear flows.
- Intent payload behavior: `NexoMessagingPluginTest.kt:201` through `:245` covers null, empty, default opened type, explicit type, and user interaction.
- Storage, ACK request behavior, notification rendering, inline reply, and dismiss receiver coverage are still broader future native-test gaps; this plan does not expand them because the current roadmap item asks for execution procedure and `testDebugUnitTest` success.
### 심볼 참조
none. 이 계획은 rename/remove 작업을 포함하지 않는다.
### 분할 판단
Split decision policy was evaluated before choosing plan files. Shared task group: `agent-task/m-client-validation/`. This subtask is `02_native_test` and has no runtime dependency. Sibling `01_integration_path` is independent because it validates Flutter device/emulator integration rather than Gradle unit test execution.
### 범위 결정 근거
이 계획은 `test-host` Epic의 `[native-test]`만 다룬다. Flutter host integration은 `01_integration_path`에서 분리한다. Native coverage expansion for storage/ACK/notification/reply/dismiss is noted as a gap but excluded because the active roadmap item only requires maintaining the Android native unit test execution procedure and command success.
### 빌드 등급
build=`cloud-G07`, review=`cloud-G07`. Android SDK, Flutter Gradle plugin wiring, and Gradle output classification are success criteria, so terminal/toolchain diagnosis is required.
## 구현 체크리스트
- [ ] Android SDK/Gradle preflight를 확인하고, 없으면 `사용자 리뷰 요청`에 실제 출력과 재개 조건을 기록한다.
- [ ] `cd apps/flutter-test/android && ./gradlew testDebugUnitTest`가 Android SDK 환경에서 성공하도록 확인한다. 실패가 제품 결함이면 이 계획의 파일 경계 안에서 최소 수정 후 같은 명령을 재실행한다.
- [ ] Native unit test coverage gaps are not expanded unless the command reveals a failing product behavior in the existing tests.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
### [TEST-1] Android Gradle native unit test execution
#### 문제
`apps/flutter-test/README.md:79` and `packages/messaging_flutter/README.md:215` document `./gradlew testDebugUnitTest`. The app Gradle wrapper reads Flutter SDK metadata through `apps/flutter-test/android/settings.gradle.kts:1`, while plugin unit tests are configured by `packages/messaging_flutter/android/build.gradle.kts:45` and `:58`. The native tests exist in `packages/messaging_flutter/android/src/test/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPluginTest.kt:14`, but this Milestone still needs execution evidence from an Android SDK environment.
#### 해결 방법
기본 구현은 Gradle native unit test 실행과 결과 기록이다. 코드 변경은 계획하지 않는다. `SDK location not found`, missing Flutter SDK metadata, or missing Android SDK는 제품 코드 수정으로 처리하지 않고 환경 blocker로 기록한다. 기존 테스트가 제품 결함을 노출하면 아래 파일 경계 안에서 최소 수정한다.
#### 수정 파일 및 체크리스트
- [ ] `packages/messaging_flutter/android/src/test/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPluginTest.kt`는 기존 unit coverage를 유지한다.
- [ ] `packages/messaging_flutter/android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPlugin.kt`는 기존 test failure가 plugin 결함을 가리킬 때만 수정한다.
- [ ] `packages/messaging_flutter/android/build.gradle.kts`는 test sourceSet/testOptions/dependencies 결함이 확인된 경우에만 수정한다.
- [ ] `apps/flutter-test/android/settings.gradle.kts``apps/flutter-test/android/app/build.gradle.kts`는 Flutter/Android Gradle wiring 결함이 확인된 경우에만 수정한다.
#### 테스트 작성
새 테스트 작성은 기본 계획에서 제외한다. 현재 unit tests가 roadmap의 native test 실행 경로를 검증하기 위한 대상이다. 명령 실패가 기존 tests로 표현되지 않는 regression을 드러내면 `NexoMessagingPluginTest.kt`에 해당 regression test를 추가하고 같은 Gradle command로 검증한다.
#### 중간 검증
```sh
command -v java
cd apps/flutter-test/android && ./gradlew --version
cd apps/flutter-test/android && ./gradlew testDebugUnitTest
```
예상 결과: `./gradlew --version``./gradlew testDebugUnitTest`가 성공한다. Android SDK 또는 Flutter SDK metadata가 없으면 제품 실패로 고치지 말고 `사용자 리뷰 요청`에 실제 출력과 필요한 환경을 기록한다.
## 수정 파일 요약
| 파일 | 항목 |
| --- | --- |
| `packages/messaging_flutter/android/src/test/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPluginTest.kt` | TEST-1 |
| `packages/messaging_flutter/android/src/main/kotlin/com/tokilabs/nexo/messaging/NexoMessagingPlugin.kt` | TEST-1 |
| `packages/messaging_flutter/android/build.gradle.kts` | TEST-1 |
| `apps/flutter-test/android/settings.gradle.kts` | TEST-1 |
| `apps/flutter-test/android/app/build.gradle.kts` | TEST-1 |
## 최종 검증
```sh
command -v java
cd apps/flutter-test/android && ./gradlew --version
cd apps/flutter-test/android && ./gradlew testDebugUnitTest
```
검색/생성 출력 검증은 없다. Gradle build cache 결과는 이 계획의 성공 판정에 사용하지 않는다.
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.

View file

@ -22,6 +22,13 @@ The app is a plugin API consumer and verification host. Native push handling,
ACK delivery, token/key storage, notification rendering, inline reply, and
dismiss handling remain owned by `packages/messaging_flutter`.
## Thin Host Contract
The host UI should expose only the plugin integration state needed by tests:
device token, raw notification event, opened notification event, and routing
callback state. `test/widget_test.dart` protects this contract by asserting the
four status lines remain present and product-style controls are not added.
## Structure
```text
@ -65,6 +72,10 @@ flutter test
flutter test integration_test
```
Use `flutter test` for the thin-host contract and `flutter test
integration_test` for real Flutter host registration, method-channel,
event-channel, and opened-routing coverage.
Run Android native unit tests from `apps/flutter-test/android`:
```sh

View file

@ -229,6 +229,11 @@ Use deterministic tests for repository-owned behavior. The example integration
test can inject a debug native event through the plugin method channel so the
EventChannel and opened-routing path can run without Firebase FCM.
The Android implementation remains a nexo-owned embedded SDK surface in these
tests. Test coverage should follow the server/push contract, Android and FCM
platform behavior, and Flutter plugin registration rather than the Mattermost
mobile app source tree.
Use the repository tests as the source of truth for plugin behavior:
1. Root `test/` covers Dart API behavior, event parsing, callback routing, and