Skip to content

test: パッケージ本体のテストが 0 件で、CI の flutter test が常にスキップされている #12

Description

@LibraryLibrarian

概要

パッケージ本体(リポジトリルートの test/)にライブラリの振る舞いを検証するテストが 1 件も存在せず、リグレッションを検知する手段がない。

現状

ルートに test/ ディレクトリ自体が存在しない。

$ find . -name '*_test.dart' -not -path './.git/*'
./example/test/widget_test.dart

ヒットするのは example 側の flutter create テンプレート由来のカウンターアプリ用テストのみで、ライブラリの動作は一切検証していない。

また .github/workflows/ci.yaml の Test ステップは以下の条件付きになっている。

- name: Test
  if: ${{ hashFiles('test/**/*_test.dart') != '' }}
  run: flutter test

test/ が存在しない現在のツリーでは、この条件は常に false となる。そのため、CI がグリーンでもライブラリの振る舞いを検証する flutter test は実行されていない。

なお、履歴上は当初ルートに flutter create テンプレート由来の test/widget_test.dart があり、旧 package-ciflutter test が実行されていた。478c405(2025-08-16)でこのテンプレートテストを削除して以降、ルートのテストはスキップされている。したがって、正確には次の状態である。

  • 過去にテンプレートのカウンターテストが CI で実行されたことはある
  • ライブラリ固有のテストは過去にも存在しない
  • 478c405 以降、パッケージ本体の flutter test は常にスキップされている

CI には format / analyze / publish dry-run に加えて example の Android / iOS ビルドもあるが、これらは以下のライブラリ内部ロジックの振る舞いを保証するものではない。

影響

  • lib/ 配下 約 1,400 行がノーガード。PKCE の生成ロジック、リトライの判定、トークンの JSON 直列化、マルチアカウントのインデックス管理など、壊れても検知できない。
  • 0.1.3-beta で「保存責務をクライアントから TokenStore へ移す」という破壊的変更を入れているが、移行後の挙動を保証するテストが存在しない。

修正方針

プラットフォームチャネル(flutter_web_auth_2 / flutter_secure_storage の実機呼び出し)に依存しない部分から着手する。

1. 純粋ロジック(プラットフォームチャネル依存なし)

対象 検証内容
MisskeyOAuthClient.generateCodeVerifier() 長さが 128 / RFC 7636 の unreserved 文字のみで構成される / 複数回の生成結果が同一にならない
MisskeyOAuthClient.generateCodeChallenge() 既知の verifier に対する S256 の期待値と一致する / = パディングが除去されている / base64url であること
MisskeyOAuthClient.generateState() 空でない / 複数回の生成結果が同一にならない
MisskeyMiAuthClient.generateSessionId() 既定長 32 / length 指定が反映される / 英数字のみ
MisskeyMiAuthConfig.callbackUrl callbackScheme から <scheme>:// を正しく組み立てる

2. RetryPolicy / retry()lib/src/net/retry.dart

  • shouldRetry()
    • retryOnTypes に含まれる DioExceptionType(connectionTimeout / sendTimeout / receiveTimeout / connectionError / unknown)で true
    • retryOnStatusCodes(429, 500, 502, 503, 504)で true
    • 400 / 401 / 403 など、リトライすべきでないステータスで false
  • nextDelay()
    • 試行回数に応じて指数的に増加する
    • maxDelay を超えない(ジッター加算後も上限を超えないこと)
  • retry()
    • リトライ対象外の例外は即 rethrow され、action の呼び出し回数が 1 回で終わる
    • maxAttempts に達したら最後の例外を投げる(呼び出し回数が maxAttempts と一致する)
    • 途中で成功したらそこで打ち切る
    • 待機を発生させないテストでは、initialDelay: Duration.zeromaxDelay: Duration.zero を指定する。initialDelay だけを小さくしても 0〜99ms のジッターが残るため、実時間待機はなくならない

3. モデルの JSON 往復

  • StoredToken.toJson() / fromJson()
    • scope / user / createdAt が null のとき、出力 JSON にキー自体が含まれないこと
    • createdAt の ISO8601 往復で値が保たれること
    • createdAt が不正な文字列のとき DateTime.tryParse により null になり、例外にならないこと
  • AccountKey.toJson() / fromJson() / == / hashCode / storageKey()
    • 同じ host + accountId の 2 インスタンスが等価で、Set / List.contains で重複判定できること(SecureTokenStore のインデックス重複排除がこれに依存している)
  • OAuthServerInfo.fromJson() / OAuthTokenResponse.fromJson() / MiAuthCheckResponse.fromJson()
    • optional フィールドが欠落したレスポンスで例外にならないこと
    • required フィールド(例: authorization_endpoint / token_endpoint / access_token / token_type)の欠落や型不一致は、正常値として受理されないこと

4. HTTP 層(Dio をモックして)

MisskeyOAuthClient / MisskeyMiAuthClient / MisskeyAuthManager はいずれもコンストラクタで Dio を受け取れるため、http_mock_adapter 等でスタブ化してテストできる(dev_dependencies への追加が必要)。

  • MisskeyOAuthClient.getOAuthServerInfo()
    • 200 → OAuthServerInfo を返す
    • 404 / 501 → null(OAuth 非対応と判定)を返す
    • 404 / 501 以外の非 2xx → 現在の Dio 既定動作では DioException を経由して NetworkException になること
    • 通信不能 → NetworkException
    • 404 / 501 以外を ServerInfoException に統一する仕様を希望する場合は、テスト追加に先立って本体の例外マッピングを変更する必要がある
  • MisskeyOAuthClient.exchangeCodeForToken()
    • 200 → OAuthTokenResponse を返す
    • エラーレスポンスの error / error_descriptionTokenExchangeException のメッセージに含まれる
  • MisskeyAuthManager/api/i 呼び出し
    • リクエストボディが {"i": "<token>"} の形式で送られていること
    • user.id が存在しないレスポンスで ResponseParseException になること
    • loginWithOAuth() / loginWithMiAuth() 自体は flutter_web_auth_2 を踏むため、まずは /api/i 部分をテスト可能な単位に切り出すか、クライアントをモックする形を検討する

5. SecureTokenStore

FlutterSecureStorage をコンストラクタで注入できるため、mockito でモックするか、Map を backing store にしたフェイク実装を用意して以下を検証する。

  • upsert()read() の往復で StoredToken が復元できること
  • list()
    • インデックスに登録したアカウントが返ること
    • トークン本体が消えているキーが混ざっていても例外にならないこと
    • 表示名のフォールバック順(user.nameuser.usernameuser.userName)が期待どおりであること
  • delete()
    • トークン本体とインデックスの両方から削除されること
    • 削除対象がアクティブアカウントだった場合にアクティブ設定が解除されること
  • clearAll()
    • 全トークン + misskey_accounts_index + misskey_active_account がすべて削除されること
  • _addToIndex() 経由で同一 AccountKey を重複登録しないこと(upsert() を 2 回呼んでも list() が 1 件であること)

完了条件

  • リポジトリルートに test/ を作成し、少なくとも上記セクション 1〜3(プラットフォーム非依存の範囲)をカバーする
  • flutter test がローカルおよび CI で実際に実行され、パスする
    • test/*_test.dart が置かれれば ci.yamlhashFiles 条件は自動的に true になるため、ワークフロー側の変更は不要

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions