Skip to content

feat: 进度百分比 + 下载重试 + 降级链测试 + Cresc 测试 + globals.d.ts#583

Open
sunnylqm wants to merge 1 commit into
masterfrom
feat/progress-retry-strict-tests
Open

feat: 进度百分比 + 下载重试 + 降级链测试 + Cresc 测试 + globals.d.ts#583
sunnylqm wants to merge 1 commit into
masterfrom
feat/progress-retry-strict-tests

Conversation

@sunnylqm

@sunnylqm sunnylqm commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

变更概览

新功能

  • 进度百分比: ProgressData 新增 progress 字段 (0-100),通过 computeProgress(received, total) 自动计算,用户无需手动算
  • 下载自动重试: ClientOptions 新增 maxRetries 选项(默认 3 次),下载失败后自动重试整个降级链

测试补充

  • downloadUpdate 降级链测试 (7 个用例):
    • diff 成功 → 直接使用 diff
    • diff 失败 → 回退到 pdiff
    • diff + pdiff 失败 → 回退到 full
    • 全部失败 → 抛出错误
    • 重试成功(第 1 次失败,第 2 次成功)
    • 默认 3 次重试验证(1 initial + 3 retries = 4 calls)
    • 重试耗尽 → 抛出错误
  • Cresc 类测试 (4 个用例):
    • 使用正确的 Cresc endpoints
    • 默认 locale 为 en
    • instanceof Pushy 验证
    • 自定义 server 覆盖默认 endpoints
  • computeProgress 工具函数测试 (6 个用例)

开发者体验

  • 新增 src/globals.d.ts,为 DEV 提供类型声明
  • 进度回调中的 progressData 参数增加 ProgressData 类型标注

改动文件

  • src/type.ts — ProgressData 增加 progress 字段,ClientOptions 增加 maxRetries
  • src/utils.ts — 新增 computeProgress 工具函数
  • src/client.ts — 进度百分比注入 + 重试循环 + 类型标注
  • src/globals.d.ts — DEV 类型声明
  • src/tests/client.test.ts — 降级链测试 + Cresc 测试
  • src/tests/utils.test.ts — computeProgress 测试

验证

  • bun run lint ✅
  • bun test → 66 pass, 0 fail ✅

Summary by CodeRabbit

  • New Features

    • Added download progress percentage to update download callbacks.
    • Introduced configurable download retry behavior with a default maximum retry limit.
  • Bug Fixes

    • Improved fallback order for patch/full downloads when earlier methods fail.
    • Enhanced retry flow and error reporting for persistent download failures.
  • Tests

    • Added unit tests for update download fallback and retry behavior.
    • Added unit tests for progress percentage calculation edge cases.
  • Documentation/Types

    • Extended public types to include progress percentage and max retry option.
    • Added __DEV__ global type declaration for React Native development mode.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9248aacc-eb08-4acc-a568-efe7f506b820

📥 Commits

Reviewing files that changed from the base of the PR and between ebd47be and 7df1727.

📒 Files selected for processing (6)
  • src/__tests__/client.test.ts
  • src/__tests__/utils.test.ts
  • src/client.ts
  • src/globals.d.ts
  • src/type.ts
  • src/utils.ts
✅ Files skipped from review due to trivial changes (1)
  • src/globals.d.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/type.ts
  • src/tests/utils.test.ts
  • src/utils.ts
  • src/client.ts
  • src/tests/client.test.ts

📝 Walkthrough

Walkthrough

The PR adds computed download progress, retry-aware update downloads, fallback-chain tests, and an ambient boolean declaration for __DEV__.

Changes

Client download flow and support types

Layer / File(s) Summary
Progress helper and progress type
src/type.ts, src/utils.ts, src/__tests__/utils.test.ts
ProgressData gains progress, computeProgress returns a floored percentage with a zero-total fallback, and utils tests cover edge cases and large values.
Retry-aware download flow
src/type.ts, src/client.ts
ClientOptions gains maxRetries, downloadUpdate forwards computed progress, and update downloads retry through diff, pdiff, and full requests until success or exhaustion.
Fallback chain tests
src/__tests__/client.test.ts
downloadUpdate tests cover diff, pdiff, and full fallback ordering, all-failure errors, configured retries, default retries, and retry exhaustion.
Ambient dev flag typing
src/globals.d.ts
__DEV__ is declared as an ambient boolean global.

Sequence Diagram(s)

sequenceDiagram
  participant downloadUpdate
  participant downloadPatchFromPpk
  participant downloadPatchFromPackage
  participant downloadFullUpdate

  loop attempt 0..maxRetries
    downloadUpdate->>downloadPatchFromPpk: try diff download
    alt diff succeeds
      downloadPatchFromPpk-->>downloadUpdate: success
    else diff fails
      downloadUpdate->>downloadPatchFromPackage: try pdiff download
      alt pdiff succeeds
        downloadPatchFromPackage-->>downloadUpdate: success
      else pdiff fails
        downloadUpdate->>downloadFullUpdate: try full update
        downloadFullUpdate-->>downloadUpdate: success or error
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • reactnativecn/react-native-update#580: Both PRs modify src/client.ts’s downloadUpdate control flow; this one adds an early no-op guard, while the current PR adds progress computation and retry fallback behavior.

Poem

Hop hop, I carry progress in my paws,
From diff to pdiff, then full by download laws.
My whiskers twitch at __DEV__'s gentle glow,
And retries bloom like carrots in a row.
🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the PR and summarizes the main additions: progress percentage, retry logic, fallback tests, and the globals type declaration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/progress-retry-strict-tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/__tests__/client.test.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)
src/__tests__/utils.test.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)
src/client.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)
  • 3 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sunnylqm sunnylqm force-pushed the feat/progress-retry-strict-tests branch from 398f57f to ebd47be Compare June 26, 2026 06:30
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/__tests__/client.test.ts (1)

373-406: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

maxRetries destructured here is never used.

setupDownloadMocks accepts maxRetries but never forwards it; the actual retry count is controlled solely by new Pushy({ ..., maxRetries }) in each test. The parameter (passed at Lines 472, 495, 527) is dead and misleading—drop it or wire it into the constructed client to avoid implying it has an effect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/client.test.ts` around lines 373 - 406, The setupDownloadMocks
helper in client.test.ts destructures maxRetries but never uses it, so the
parameter is misleading and dead. Remove maxRetries from the helper signature
and its destructuring in setupDownloadMocks, or alternatively thread it through
to the Pushy client setup if the tests are meant to control retry behavior; use
the setupDownloadMocks and Pushy test setup as the places to update.
src/client.ts (1)

528-607: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Retries fire immediately with no backoff.

On persistent failures the loop re-runs the full diff→pdiff→full chain back-to-back (up to maxRetries + 1 times) with no delay. For server/network-induced failures this hammers the backend instantly and is unlikely to recover. Consider adding a short (ideally exponential) backoff between attempts.

♻️ Example backoff between attempts
     for (let attempt = 0; attempt <= maxRetries; attempt++) {
       if (attempt > 0) {
         log(`retry attempt ${attempt}/${maxRetries}`);
+        await new Promise(r => setTimeout(r, Math.min(1000 * 2 ** (attempt - 1), 10000)));
         errorMessages.length = 0;
         lastError = undefined;
         succeeded = '';
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.ts` around lines 528 - 607, The retry loop in client.ts for the
update download flow retries immediately with no delay, which can hammer the
backend on repeated failures. Add a short backoff between attempts inside the
for-loop around the diff/pdiff/full download chain, ideally exponential and only
between failed attempts, while keeping the existing success break behavior and
preserving the current logging/error handling in the retry path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/type.ts`:
- Around line 36-37: The `progress` field in `RCTPushyDownloadProgress` is being
treated as always present, but that is not true across all emission paths.
Update the `type.ts` definition and the related progress flow in
`downloadAndInstallApk`/`onDownloadProgress` so the runtime contract matches
reality: either make `progress` optional in the shared type or ensure every
producer, including the APK download path and any native
`RCTPushyDownloadProgress` payloads, computes and sets it consistently like
`wrapProgress` does in `downloadUpdate`.

---

Nitpick comments:
In `@src/__tests__/client.test.ts`:
- Around line 373-406: The setupDownloadMocks helper in client.test.ts
destructures maxRetries but never uses it, so the parameter is misleading and
dead. Remove maxRetries from the helper signature and its destructuring in
setupDownloadMocks, or alternatively thread it through to the Pushy client setup
if the tests are meant to control retry behavior; use the setupDownloadMocks and
Pushy test setup as the places to update.

In `@src/client.ts`:
- Around line 528-607: The retry loop in client.ts for the update download flow
retries immediately with no delay, which can hammer the backend on repeated
failures. Add a short backoff between attempts inside the for-loop around the
diff/pdiff/full download chain, ideally exponential and only between failed
attempts, while keeping the existing success break behavior and preserving the
current logging/error handling in the retry path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eb1e47da-d9d4-4940-8b63-bdbabb7aedd9

📥 Commits

Reviewing files that changed from the base of the PR and between 311cefd and ebd47be.

📒 Files selected for processing (6)
  • src/__tests__/client.test.ts
  • src/__tests__/utils.test.ts
  • src/client.ts
  • src/globals.d.ts
  • src/type.ts
  • src/utils.ts

Comment thread src/type.ts Outdated
…c tests, globals.d.ts

Features:
- Add progress percentage (0-100) to ProgressData via computeProgress utility
- Add maxRetries option for automatic download retry on failure
- Download progress callbacks now include computed progress field

Tests:
- Add comprehensive downloadUpdate fallback chain tests (diff→pdiff→full)
- Add retry mechanism tests (success on retry, exhaust retries)
- Add Cresc class tests (endpoints, locale, instanceof, custom server)
- Add computeProgress unit tests

Developer Experience:
- Add src/globals.d.ts for __DEV__ type declaration
- Type-hint progressData callbacks with ProgressData type
@sunnylqm sunnylqm force-pushed the feat/progress-retry-strict-tests branch from ebd47be to 7df1727 Compare June 26, 2026 06:57
@sunnylqm

Copy link
Copy Markdown
Contributor Author

All 3 CodeRabbit findings addressed:

  1. progress field → Changed to progress?: number (optional), since native RCTPushyDownloadProgress and downloadAndInstallApk path do not populate it.

  2. Dead maxRetries param → Removed from setupDownloadMocks helper signature and all call sites.

  3. No backoff on retry → Added exponential backoff (1s, 2s, 4s, ... capped at 10s) between retry attempts. Tests bypass delays via setTimeout override.

Lint ✅ | 66 tests pass ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant