Skip to content

test: setup unit test for flutter_skin main class and CI check on PRs#8

Merged
koukibadr merged 1 commit into
mainfrom
test/setup-testing-and-ci-strategy
Jul 8, 2026
Merged

test: setup unit test for flutter_skin main class and CI check on PRs#8
koukibadr merged 1 commit into
mainfrom
test/setup-testing-and-ci-strategy

Conversation

@koukibadr

@koukibadr koukibadr commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added stricter key-format validation for initialization.
    • Improved theme handling so the app can resolve skin data from a configurable remote source.
    • Added broader test coverage for initialization, theme loading, and error cases.
  • Bug Fixes

    • Accessing theme data before initialization now fails clearly.
    • Invalid or empty API keys are now rejected consistently.
  • Tests

    • Added mocks and new test scenarios for configuration, fallback behavior, and initialization checks.
    • Updated CI to run formatting, analysis, unit tests, and publish dry-run checks.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@koukibadr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 40 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a04e59e-68ec-415f-b319-ef9bc977a89d

📥 Commits

Reviewing files that changed from the base of the PR and between 377b6b6 and 374feb4.

📒 Files selected for processing (10)
  • .github/workflows/pr_verification_pipeline.yml
  • lib/constants/fskin_constants.dart
  • lib/flutter_skin.dart
  • lib/remote/fskin_remote_config.dart
  • lib/services/skin_service.dart
  • pubspec.yaml
  • test/flutter_skin/flutter_skin_init_test.dart
  • test/flutter_skin/initialization_scenario_test.dart
  • test/flutter_skin_test.dart
  • test/mocks/skin_model_mocks.dart
📝 Walkthrough

Walkthrough

This PR adds an apiKey format validation constant, converts FskinRemoteConfig's onSkinChanged and projectConfig from static to instance getters, and introduces a mutable static remoteConfig on FlutterSkin with test hooks (resetInstance, setRemoteConfig). Init now validates apiKey; theme getter guards against uninitialized state. Tests, mocks, mockito dependency, and CI pipeline changes are included.

Changes

Testable remote config and key validation

Layer / File(s) Summary
Key validation constant and remote config instance accessors
lib/constants/fskin_constants.dart, lib/remote/fskin_remote_config.dart
Adds FskinConstants.keyRegex and converts onSkinChanged/projectConfig from static to instance getters.
FlutterSkin remoteConfig wiring, validation, and guards
lib/flutter_skin.dart
Adds mutable static remoteConfig, resetInstance(), setRemoteConfig(); validates apiKey in init; updates stream/lifecycle/theme call sites; guards theme getter when uninitialized.
Tests, mocks, and dependencies for init/theme behavior
pubspec.yaml, test/flutter_skin/flutter_skin_init_test.dart, test/flutter_skin/initialization_scenario_test.dart, test/mocks/skin_model_mocks.dart, test/flutter_skin_test.dart
Adds mockito dependency, mock classes, tests for init/theme behavior and error scenarios, and removes the old placeholder test.
CI pipeline format/analyze/test/publish jobs
.github/workflows/pr_verification_pipeline.yml
Replaces the analyze job with dart_format_and_analyze, and wires unit-test and dry-run-upload jobs to run afterward.
Minor whitespace change
lib/services/skin_service.dart
Inserts a blank line with no behavior change.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant FlutterSkin
  participant FskinConstants
  participant FskinRemoteConfig

  App->>FlutterSkin: init(apiKey)
  FlutterSkin->>FskinConstants: validate apiKey against keyRegex
  FskinConstants-->>FlutterSkin: valid/invalid
  FlutterSkin->>FskinRemoteConfig: remoteConfig = singleton
  App->>FlutterSkin: theme
  FlutterSkin->>FlutterSkin: check _instance != null
  FlutterSkin->>FskinRemoteConfig: remoteConfig.projectConfig
  FskinRemoteConfig-->>FlutterSkin: ProjectConfig
  FlutterSkin-->>App: ThemeData
Loading

Possibly related PRs

  • koukibadr/flutter_skin#2: Both PRs modify FlutterSkin/FskinRemoteConfig initialization, switching remote config access between static and instance patterns.
  • koukibadr/flutter_skin#5: Both PRs modify the skin-update streaming wiring, including how remoteConfig is referenced for onSkinChanged.

Poem

A regex key, a guarded start,
remoteConfig now plays its part,
tests hop in with mocks so neat,
CI checks each format beat,
this bunny thumps — the build's complete! 🐇✨

🚥 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 matches the main changes: new FlutterSkin unit tests and PR CI workflow updates.
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.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/remote/fskin_remote_config.dart (1)

20-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Instance getter projectConfig should use this._cachedConfig, not the static _instance.

Now that projectConfig is an instance getter, it should return the instance's own _cachedConfig. The guard if (_instance == null) and the return _instance!._cachedConfig are leftovers from when this was a static getter. In production this works only because remoteConfig is the singleton, but it's incorrect for any non-singleton instance and is a design inconsistency.

♻️ Proposed refactor
   ProjectConfig? get projectConfig {
-    if (_instance == null) {
-      throw Exception(
-        'FskinRemoteConfig must be initialized with FskinRemoteConfig.init()',
-      );
-    }
-    return _instance!._cachedConfig;
+    return _cachedConfig;
   }
🤖 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 `@lib/remote/fskin_remote_config.dart` around lines 20 - 27, The instance
getter projectConfig in FskinRemoteConfig still relies on the static _instance
singleton check and return, which is leftover static behavior. Update
projectConfig to validate and return the current instance’s own _cachedConfig
via this, so the getter works correctly for any FskinRemoteConfig instance
rather than only the singleton remoteConfig.
.github/workflows/pr_verification_pipeline.yml (1)

5-67: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Add a top-level permissions: block and persist-credentials: false.

The workflow has no permissions: block, so jobs run with the repo's default (broad) token permissions. These jobs only need read access to source code — none require write scopes. Additionally, actions/checkout persists a GITHUB_TOKEN credential in .git/config by default; since no subsequent step pushes or authenticates, set persist-credentials: false on all checkout steps.

🔒️ Proposed fix
+permissions:
+  contents: read
+
 jobs:
   dart_format_and_analyze:

And on every checkout step:

-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
🤖 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 @.github/workflows/pr_verification_pipeline.yml around lines 5 - 67, Add a
top-level permissions block to the workflow so the jobs use least-privilege
read-only access instead of the default broad token scope. Also update every
actions/checkout step in the workflow to set persist-credentials to false, since
none of the jobs need to keep a GitHub token in .git/config. Locate the workflow
jobs dart_format_and_analyze, unit-test, and dry-run-upload and apply the
checkout change consistently across each one.

Source: Linters/SAST tools

🤖 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 @.github/workflows/pr_verification_pipeline.yml:
- Line 40: The downstream jobs in the GitHub Actions workflow still use
actions/checkout@v2, which is too old for current runners. Update the checkout
step in the unit-test and dry-run-upload jobs in pr_verification_pipeline.yml to
match the existing dart_format_and_analyze job by using actions/checkout@v4,
keeping the same checkout step structure but with the newer action version.
- Around line 8-15: The workflow defines a matrix value flutter-version in the
pr_verification_pipeline job, but the subosito/flutter-action step never
consumes it. Update the flutter-action configuration to use the matrix
flutter-version value in the with block for the flutter-version input (instead
of relying only on channel: stable) so the job runs the pinned version
consistently.

In `@lib/flutter_skin.dart`:
- Around line 22-29: resetInstance() only clears _instance and leaves
remoteConfig pointing at a prior test double, which can leak state across tests.
Update FlutterSkin.resetInstance() so it also resets remoteConfig to the default
singleton behavior, and make sure it does so safely even when
FskinRemoteConfig._instance has not been initialized yet. Use resetInstance(),
setRemoteConfig(), and remoteConfig in FlutterSkin to locate the change, and
ensure init(), onSkinChanged, fetchConfig, and projectConfig will observe the
real config after a reset.
- Line 35: The key validation in FlutterSkin.init is recompiling the same
pattern on every call by creating RegExp(FskinConstants.keyRegex) inline. Move
that regex into a static final field on FlutterSkin (or a nearby shared
constant) and reuse it in init() so the validation uses the precompiled RegExp
instead of rebuilding it each time.

In `@test/flutter_skin/flutter_skin_init_test.dart`:
- Around line 14-71: The FlutterSkin initialization tests are sharing static
state and still triggering real network access. Add test isolation around
FlutterSkin._instance and FlutterSkin.remoteConfig by resetting them in
setUp/tearDown using FlutterSkin.resetInstance() and restoring the default
remote config between tests. Also update FlutterSkin.init (and any direct
FskinRemoteConfig.init path it uses) so initialization goes through the
injectable remoteConfig or a mocked HTTP layer, allowing FlutterSkin.init,
FlutterSkin.singleton, and setRemoteConfig(MockFskinRemoteConfig()) to run
hermetically without live requests.

---

Outside diff comments:
In @.github/workflows/pr_verification_pipeline.yml:
- Around line 5-67: Add a top-level permissions block to the workflow so the
jobs use least-privilege read-only access instead of the default broad token
scope. Also update every actions/checkout step in the workflow to set
persist-credentials to false, since none of the jobs need to keep a GitHub token
in .git/config. Locate the workflow jobs dart_format_and_analyze, unit-test, and
dry-run-upload and apply the checkout change consistently across each one.

In `@lib/remote/fskin_remote_config.dart`:
- Around line 20-27: The instance getter projectConfig in FskinRemoteConfig
still relies on the static _instance singleton check and return, which is
leftover static behavior. Update projectConfig to validate and return the
current instance’s own _cachedConfig via this, so the getter works correctly for
any FskinRemoteConfig instance rather than only the singleton remoteConfig.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 23690c9d-6952-4285-bb44-3f8e0fbed819

📥 Commits

Reviewing files that changed from the base of the PR and between 194f4e3 and 377b6b6.

📒 Files selected for processing (10)
  • .github/workflows/pr_verification_pipeline.yml
  • lib/constants/fskin_constants.dart
  • lib/flutter_skin.dart
  • lib/remote/fskin_remote_config.dart
  • lib/services/skin_service.dart
  • pubspec.yaml
  • test/flutter_skin/flutter_skin_init_test.dart
  • test/flutter_skin/initialization_scenario_test.dart
  • test/flutter_skin_test.dart
  • test/mocks/skin_model_mocks.dart
💤 Files with no reviewable changes (1)
  • test/flutter_skin_test.dart

Comment thread .github/workflows/pr_verification_pipeline.yml Outdated
Comment thread .github/workflows/pr_verification_pipeline.yml Outdated
Comment thread lib/flutter_skin.dart Outdated
Comment thread lib/flutter_skin.dart
Comment thread test/flutter_skin/flutter_skin_init_test.dart
@koukibadr koukibadr force-pushed the test/setup-testing-and-ci-strategy branch from 377b6b6 to 46a4aaf Compare July 8, 2026 06:40
@koukibadr koukibadr force-pushed the test/setup-testing-and-ci-strategy branch from 46a4aaf to 374feb4 Compare July 8, 2026 06:49
@koukibadr koukibadr merged commit c399fac into main Jul 8, 2026
4 checks passed
@koukibadr koukibadr deleted the test/setup-testing-and-ci-strategy branch July 8, 2026 06:55
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