# Testing / TypeScript / Observability監査 ## 1. 現在の検証状態 | 項目 | 結果 | |---|---| | TypeScript | `npx tsc --noEmit` 成功 | | noUnused試行 | `--noUnusedLocals --noUnusedParameters`は未使用警告多数で終了コード2 | | Expo Doctor | 20 checks中19成功、1失敗(patch mismatch) | | Expo install check | `npx expo install --check`を実行。Doctorと同じ25件で終了1、package/lock変更なし | | Expo public config | 成功 | | Web export | 成功(`/tmp/jrshikoku-audit-export`へ出力) | | unit/hook/component test | test/spec/__tests__なし | | integration/API test | なし | | native test | 自動/実機smokeなし。新規buildは未実行だが、直近7.2 EAS Build成功は確認 | | E2E/smoke | なし | | lint script | なし | | CI | `.github/` / `.eas/workflows/`なし | | EAS live state | read-only CLIでbuild/channel/updateを確認。Expo MCPは内部エラー | `package.json`のscriptsはcompile-web-script、start、android、ios、eject、postinstall、pushWeb、checkDiagramのみで、`test`、`lint`、`typecheck`、`verify`、`doctor`、`build`はない。`eject`は現行CNG/SDK 55と整合しないstale script候補である。 ## 2. テスト可能性の評価 ### Unit(すぐ可能) - `timeUtils`、`trainTimeFiltering`、駅ID/路線変換、列車番号変換、運休判定。 - `getTrainType`、delay label、Voicepeak text/key、station ordering。 - parser/type guard、source priority、stale/TTL、dedupe。 ### Hook / repository - `useCurrentTrain`のR2→GAS fallback、最新responseのみ反映、mock abort。 - `useTrainDelayData`の初回一回、retry、HTTP/parse error。 - `useAllTrainDiagram`のcache、polling、AppState。 - 通知設定・storage migration。 ### Component / integration - station diagramがAPI取得後に再計算されること。 - deep link / notificationからnested routeへ到達すること。 - WebView message、recording import、download bridge。 - tab切替、Android back、modal dismiss、rotation。 ### Native / smoke / E2E - FeliCa scan、Widget同期、Live Activity/Foreground Service、permission拒否。 - Development Buildでの起動、OTA runtime compatibility、push token登録。 - 主要ユーザーフローの少数smoke(起動→station→train→追従、operation、setting、FeliCa)。 ## 3. 最低限自動化すべきテスト 優先順位は次の通り。 1. **時刻**: 24:xx、04:00、JST、日付跨ぎ、delay加算。 2. **駅識別**: 同名駅、`M12`、枝番、路線文脈必須。 3. **列車位置parser**: R2/GAS/mockのmalformed、duplicate、stale response。 4. **ダイヤparser**: `休発`/`休着`、臨時運転日、serviceDate。 5. **API client**: status、timeout、abort、retry、JSON/HTML、schema mismatch。 6. **deep link**: cold start / warm start / notificationとの競合。 7. **storage**: favorite、theme、source setting、cache TTL/migration。 8. **Native capability**: mockable JS contract tests + 少数の実機smoke。 ## 4. TypeScript所見 tscが成功することは、外部JSONの安全性を意味しない。現在位置など一部はtype guardがあるが、Unyohub/Elesite、遅延、GAS、WebView message、navigation stateに`any`/assertion/dynamic recordが残る。 改善方針: - callerのinterfaceではなく、response parserの戻り値を信頼する。 - `unknown`で受け、必須キー、文字列/数値、許容enum、配列要素を検証する。 - malformed itemを捨てる場合は件数とsourceを観測し、全体失敗との違いを明示する。 - `source`、`fetchedAt`、`schemaVersion`、`serviceDate`をデータ envelopeへ入れる。 - Zod等は、parser数・bundle cost・実データの変化頻度を見て導入判断する。今回インストールしない。 - `tsconfig.json`のpaths/plugins記述は、現状tscが通るものの、`baseUrl`を含む意図を明文化して別PRで整理する。 ## 5. Error handling ### 良い点 - Sentry初期化、navigation/lifecycle breadcrumb、network context、WebView watchdogがある。 - `observedFetch`はHTTP/timeout/parse形式を区別しようとしている。 - app lifecycle crash sentinelで前回異常終了の兆候を残す。 ### 欠落 - rootの明示的ErrorBoundary / user-safe fallbackがない。`Sentry.wrap`だけでは画面復旧UIを提供しない。 - `logger.error`は基本consoleで、Sentryへ送る保証がない。 - raw fetch、WebView内fetch、Native fetchが観測層を迂回する。 - 通知登録エラーが空実装に近く、失敗理由をUIへ返さない。 - `NotificationSettings`はPOSTの`response.ok`を確認せず成功alertへ進み得る。 - `UpdateAsync`の`fetchUpdateAsync`がouter catchへawait/returnされない。Expo Updates既定の起動時checkも有効なため、custom check/reload方針と重複する。 ## 6. Sentry / privacy [`lib/observability/sentry.ts`](../../lib/observability/sentry.ts)をApp.tsxから初期化してSentryを有効にし、production trace 5%、Replay、Feedback、`sendDefaultPii:true`を設定する。`beforeSend`はdata_fetch response headの短縮が中心で、token/URL query/response payloadの意味ベースredactionは十分ではない。一方、`observedFetch`はfailure kind、endpoint tag、contextをSentryへ送る既存基盤を持つため、新規観測基盤を作るより適用範囲とredactionを揃えるべきである。 要確認・対応: - privacy policyにSentry、PII、Replay、Feedback、保存期間、同意、削除依頼を追記または実装設定を変更する。 - WebView navigation breadcrumbのURL queryをredactする。 - Push Token、Voicepeak token、Sentry credentialをconsole/breadcrumb/exception messageへ出さない。Push Tokenを権限user IDへ直接流用しない。 - Clipboardへのtokenコピーは、隠れgestureではなく利用目的と警告を示す明示操作だけにする。 - response headはHTML/JSONに個人情報やtokenが混ざる可能性があるため、host/pathだけを原則とする。 - release、distribution、runtimeVersion、EAS Update IDをSentry eventへtag付けする。 - source map uploadとrelease healthの実態をEAS/Sentryで確認する。 ## 7. 「取得失敗」の原因特定可能性 | 経路 | endpoint | status | timeout | parser | source/age | Sentry | |---|---|---|---|---|---|---| | current R2 | あり | あり | あり | 部分的 | 部分的 | あり | | current GAS fallback | あり | caller依存 | caller依存 | 部分的 | 不十分 | 一部 | | delay raw fetch | あり | なし | なし | なし | なし | なし/限定 | | Unyohub/Elesite | あり | なし | なし | なし | cache ageなし | console中心 | | WebView page | URL | WebView errorのみ | page依存 | script依存 | page内不明 | breadcrumb/watchdog | | Android service/widget | 独自URL | native実装依存 | あり/独自 | permissive | UIへ不明 | RN外 | 結論として、主要経路では一部追跡できるが、ユーザー報告から一意に原因を特定できる状態ではない。 ## 8. 推奨テスト段階 ```text Stage 0a: tsc + Web export + doctor/install-checkのbaselineを固定(現状失敗はartifact化) Stage 0b: SDK 55 patch整合後にdoctor/install-checkをblocking化 Stage 1: pure domain/parser unit tests Stage 2: repository/hook tests (fake timer/Abort/fixture) Stage 3: navigation/deep-link/component integration Stage 4: Development Build native smoke Stage 5: release channel/OTA/store smoke ``` 各PRで全Native buildを必須にする必要はない。JS/domain/API変更はStage 0〜2、Navigation/WebView変更はStage 3、module/config/plugin変更はStage 4、release config変更はStage 5を要求する。