Skip to content

Comprehensive upgrade of Kinde Flutter SDK test coverage#53

Open
KeeganBeuthin wants to merge 7 commits intokinde-oss:mainfrom
KeeganBeuthin:update/ovehaul-unit-testing
Open

Comprehensive upgrade of Kinde Flutter SDK test coverage#53
KeeganBeuthin wants to merge 7 commits intokinde-oss:mainfrom
KeeganBeuthin:update/ovehaul-unit-testing

Conversation

@KeeganBeuthin
Copy link
Contributor

Changes

New Test Infrastructure

JwtTestHelper - Factory for creating mock JWT tokens with customizable claims
AuthStateFixtures - Pre-configured AuthState instances for common test scenarios
Enhanced MockChannels - Full secure storage simulation (read/write/delete/deleteAll)

New Test Files

auth_state_test.dart - Constructor, JSON serialization, expiration, token retrieval
token_utils_test.dart - Claims, permissions, feature flags, organizations
kinde_error_test.dart - Error factory, Dio/Platform/Format exceptions
auth_config_test.dart - URL validation, scope handling
additional_params_test.dart - Web/auth request parameter conversion
helpers_test.dart - URL safety validation
refresh_token_interceptor_test.dart - Token refresh on 403, error handling
token_api_test.dart - OAuth token endpoint interactions
store_test.dart - Secure storage persistence
kinde_web_test.dart - Web OAuth flows, PKCE, error codes

@KeeganBeuthin KeeganBeuthin requested a review from a team as a code owner November 28, 2025 10:07
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

Walkthrough

Adds a large suite of unit tests and test helpers across the SDK covering parameter serialization, auth config, auth state, token API/utils, web flows, error mapping, refresh interceptor, store behavior, URL helpers, in-memory mocks, and JWT/fixture generators.

Changes

Cohort / File(s) Summary
Parameter tests
test/additional_params_test.dart
Tests Parameter enum strings; AdditionalParameters and InternalAdditionalParameters constructors, conversions to web/authorization param maps, list/string handling, and mutation behavior.
Auth config tests
test/auth_config_test.dart
Validates AuthConfig construction, redirect URL validation across schemes, default scopes, scope ordering/duplicates, and edge-case inputs.
Auth state tests & fixtures
test/auth_state_test.dart, test/test_helpers/auth_state_fixtures.dart
AuthState JSON round-trip, expiry/isExpired behavior, token retrieval, edge cases; fixture helpers for many auth state variations and JSON forms.
Token utils & API tests
test/token_utils_test.dart, test/token_api_test.dart
TokenUtils parsing, claims, flags, permissions, org extraction tests; TokenApi request formation, headers, form-encoding, response parsing, error propagation and timeouts.
Core SDK & web flow tests
test/kinde_flutter_sdk_core_test.dart, test/kinde_web_test.dart
SDK initialization and runtime behaviors with mocked endpoints; web OAuth flow tests (code verifier, redirect parsing, state/code extraction, exchange params).
Error handling tests
test/kinde_error_test.dart
KindeError constructors, equality/hashCode, toString, and mapping from many exception types (DioException variants, PlatformException, AuthorizationException, FormatException, etc.).
Refresh token interceptor tests
test/refresh_token_interceptor_test.dart
RefreshTokenInterceptor onError flows for 403 vs non-403, network errors, refresh callback success/failure, and handler resolve/reject/next behavior.
Store tests
test/store_test.dart
Store singleton, clear/loadFromStorage, JSON serialization/round-trip for AuthState and Keys, concurrent update edge cases and storage behavior.
Helpers & URL tests
test/helpers_test.dart
isSafeWebUrl validation across valid/invalid URLs, edge cases, and malformed inputs.
Mocks & test helpers
test/mock_channels.dart, test/test_helpers/jwt_test_helper.dart, test/test_helpers/mock_store.dart
In-memory flutter_secure_storage mock with CRUD operations; JwtTestHelper to create non-signed JWTs and helpers; MockStore in-memory AuthState/Keys test store.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus areas:
    • test/store_test.dart — state lifecycle, serialization, and interactions with mock channels.
    • test/mock_channels.dart — correctness of in-memory storage semantics and concurrent behavior.
    • test/test_helpers/jwt_test_helper.dart & test/test_helpers/auth_state_fixtures.dart — token generation, claim shapes, expiry handling consumed by many tests.
    • test/refresh_token_interceptor_test.dart & test/kinde_error_test.dart — interceptor control flow and comprehensive error mapping.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description provides a well-organized overview of the changes, including new test infrastructure components (JwtTestHelper, AuthStateFixtures, MockChannels) and a complete list of new test files with their coverage areas, all of which match the changeset content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title 'Comprehensive upgrade of Kinde Flutter SDK test coverage' accurately reflects the main change: adding extensive test coverage across multiple SDK components.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (6)
test/kinde_flutter_sdk_core_test.dart (1)

96-106: Consider verifying the actual http→https conversion.

The test asserts that initialization completes successfully but doesn't verify that the domain was actually converted from http:// to https://. While this may be a smoke test, adding an assertion to check the final domain value would strengthen the test's value.

Example enhancement:

test('should convert http:// to https:// in auth domain', () async {
  final sdk = await initializeKindeFlutterSdkForTest(
    authDomain: 'http://test.kinde.com',
    authClientId: 'test_client_id',
    loginRedirectUri: 'myapp://callback',
    logoutRedirectUri: 'myapp://logout',
    dio: dio,
  );

  expect(sdk, isNotNull);
  // Add assertion to verify the domain was normalized
  // expect(sdk.authDomain, equals('test.kinde.com')); // or similar
});
test/token_utils_test.dart (2)

154-165: Duplicate test case can be removed.

This test (should handle user details with all fields populated) is functionally identical to should return user details from id token (lines 125-136) - both use AuthStateFixtures.createValid() and verify the same fields with identical assertions.

Consider removing this duplicate test:

-      test('should handle user details with all fields populated', () {
-        tokenUtils.authState = AuthStateFixtures.createValid();
-
-        final userDetails = tokenUtils.getUserDetails();
-
-        expect(userDetails, isNotNull);
-        expect(userDetails!.id, JwtTestHelper.testUserId);
-        expect(userDetails.email, JwtTestHelper.testEmail);
-        expect(userDetails.givenName, JwtTestHelper.testGivenName);
-        expect(userDetails.familyName, JwtTestHelper.testFamilyName);
-        expect(userDetails.picture, JwtTestHelper.testPicture);
-      });

253-259: Duplicate test case provides minimal additional value.

This test (should return org code from token) is essentially the same as should return organization code above (lines 245-251), just with a different org code value ('my_org' vs 'org_acme').

Consider consolidating into a single parameterized test or removing:

-      test('should return org code from token', () {
-        tokenUtils.authState = AuthStateFixtures.createWithOrganization('my_org');
-
-        final org = tokenUtils.getOrganization();
-
-        expect(org.orgCode, 'my_org');
-      });
test/kinde_web_test.dart (2)

118-126: Tautological test provides no value.

This test only verifies that constant values equal themselves (minLength >= 43, maxLength <= 128). It doesn't test any SDK code behavior.

Consider removing this test or replacing it with an actual test that validates the code verifier length from the SDK's implementation:

-    test('code verifier length requirements per RFC 7636', () {
-      // RFC 7636 specifies code_verifier length between 43-128 characters
-      const minLength = 43;
-      const maxLength = 128;
-
-      expect(minLength, lessThanOrEqualTo(maxLength));
-      expect(minLength, greaterThanOrEqualTo(43));
-      expect(maxLength, lessThanOrEqualTo(128));
-    });

129-140: Tautological test provides no value.

This test only verifies that a constant equals itself (expectedMinLength >= 16). It doesn't test any actual SDK functionality.

Consider removing this test group or replacing it with actual tests that verify the SDK's auth state generation behavior:

-  group('Auth State Generation', () {
-    test('auth state should be sufficiently random', () {
-      // Auth state is used to prevent CSRF attacks
-      // It should be unpredictable and unique per request
-
-      // Verify expected length based on typical implementations
-      // Most implementations use 16-32 bytes encoded as hex or base64
-      const expectedMinLength = 16; // 16 bytes minimum for security
-
-      expect(expectedMinLength, greaterThanOrEqualTo(16));
-    });
-  });
test/store_test.dart (1)

12-15: Consider adding Store.init() to setUp for consistency.

Currently Store.init() is called at the beginning of each test. Moving it to a setUp block would reduce code duplication and ensure consistent initialization.

  setUpAll(() {
    // Initialize mock platform channels for secure storage
    mockChannels.setupMockChannel();
  });

+  setUp(() async {
+    await Store.init();
+  });
+
  group('Store', () {
    group('Singleton Pattern', () {
      test('should return same instance', () {
        final instance1 = Store.instance;

Then remove individual await Store.init(); calls from each test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11ab82d and 3981e81.

📒 Files selected for processing (15)
  • test/additional_params_test.dart (1 hunks)
  • test/auth_config_test.dart (1 hunks)
  • test/auth_state_test.dart (1 hunks)
  • test/helpers_test.dart (1 hunks)
  • test/kinde_error_test.dart (1 hunks)
  • test/kinde_flutter_sdk_core_test.dart (1 hunks)
  • test/kinde_web_test.dart (1 hunks)
  • test/mock_channels.dart (1 hunks)
  • test/refresh_token_interceptor_test.dart (1 hunks)
  • test/store_test.dart (1 hunks)
  • test/test_helpers/auth_state_fixtures.dart (1 hunks)
  • test/test_helpers/jwt_test_helper.dart (1 hunks)
  • test/test_helpers/mock_store.dart (1 hunks)
  • test/token_api_test.dart (1 hunks)
  • test/token_utils_test.dart (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:110-124
Timestamp: 2025-06-20T10:43:27.820Z
Learning: The PR #27 for the Flutter starter kit example app is designed to work with Kinde Flutter SDK version 1.0.4, where the register() method will return a token if registration is successful. The current build errors are expected since the code targets a future SDK version.
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:79-94
Timestamp: 2025-06-20T10:44:49.809Z
Learning: In the Kinde Flutter SDK, version 1.0.4 will change the login() method to return a token when login is successful, rather than returning void. Code in this repository may be written to anticipate this future API change.
📚 Learning: 2025-11-27T10:12:42.833Z
Learnt from: KeeganBeuthin
Repo: kinde-oss/kinde-flutter-sdk PR: 52
File: lib/src/store/store.dart:54-57
Timestamp: 2025-11-27T10:12:42.833Z
Learning: In the Kinde Flutter SDK's Store class (lib/src/store/store.dart), the authState and keys setters intentionally use a fire-and-forget pattern when writing to flutter_secure_storage. This maintains API compatibility with the previous Hive-based implementation and avoids introducing breaking changes by keeping the setters synchronous rather than converting them to async methods.

Applied to files:

  • test/auth_state_test.dart
  • test/mock_channels.dart
  • test/store_test.dart
  • test/test_helpers/auth_state_fixtures.dart
  • test/test_helpers/mock_store.dart
📚 Learning: 2025-11-27T10:13:04.736Z
Learnt from: KeeganBeuthin
Repo: kinde-oss/kinde-flutter-sdk PR: 52
File: lib/src/store/store.dart:44-47
Timestamp: 2025-11-27T10:13:04.736Z
Learning: In the kinde-flutter-sdk Store class (lib/src/store/store.dart), the authState and keys setters use a fire-and-forget pattern for writing to flutter_secure_storage to maintain backward compatibility with the previous Hive-based implementation, which also had synchronous setters. This is an intentional design decision to avoid breaking changes.

Applied to files:

  • test/auth_state_test.dart
  • test/mock_channels.dart
  • test/store_test.dart
  • test/kinde_flutter_sdk_core_test.dart
  • test/test_helpers/auth_state_fixtures.dart
  • test/test_helpers/mock_store.dart
📚 Learning: 2025-06-20T10:43:27.820Z
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:110-124
Timestamp: 2025-06-20T10:43:27.820Z
Learning: The PR #27 for the Flutter starter kit example app is designed to work with Kinde Flutter SDK version 1.0.4, where the register() method will return a token if registration is successful. The current build errors are expected since the code targets a future SDK version.

Applied to files:

  • test/auth_state_test.dart
  • test/kinde_web_test.dart
  • test/kinde_flutter_sdk_core_test.dart
  • test/kinde_error_test.dart
  • test/token_api_test.dart
📚 Learning: 2025-06-20T10:44:49.809Z
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:79-94
Timestamp: 2025-06-20T10:44:49.809Z
Learning: In the Kinde Flutter SDK, version 1.0.4 will change the login() method to return a token when login is successful, rather than returning void. Code in this repository may be written to anticipate this future API change.

Applied to files:

  • test/kinde_flutter_sdk_core_test.dart
  • test/token_api_test.dart
🔇 Additional comments (18)
test/token_utils_test.dart (2)

23-28: LGTM!

Good use of setUp to initialize a fresh TestTokenUtils instance for each test, ensuring test isolation.


302-376: LGTM!

Excellent coverage of feature flag scenarios including type handling, default values, missing flags, and type mismatch errors. The tests properly verify both the flag metadata (code, type, isDefault) and values.

test/kinde_web_test.dart (2)

238-284: LGTM!

Solid test coverage for URL state extraction including URL-encoded parameters, missing required parameters, and error response parsing. These tests validate actual URL parsing behavior that the SDK would rely on.


8-59: LGTM!

Good coverage of KindeError codes for web flows. These tests validate that error code constants exist and match expected string values.

test/test_helpers/mock_store.dart (1)

6-24: LGTM!

Clean and minimal mock implementation that avoids Flutter secure storage dependencies for unit testing. The synchronous setters align with the real Store's API (which uses fire-and-forget pattern per learnings).

test/kinde_error_test.dart (3)

164-266: LGTM!

Excellent coverage of DioException handling including all timeout types, connection errors, cancellation, and nested KindeError extraction. The test at line 253-265 for nested errors is particularly valuable for ensuring proper error unwrapping.


268-309: LGTM!

Good coverage of FormatException handling including OAuth state mismatch detection, JSON error extraction from messages, and graceful fallback for non-JSON or malformed content.


46-74: LGTM!

Well-structured equality tests covering both positive and negative cases, plus hashCode consistency verification.

test/store_test.dart (3)

350-364: Verify empty token string handling is intentional.

This test validates that empty strings can be stored for accessToken, idToken, refreshToken, and scope. In practice, empty tokens would likely indicate an invalid state.

Consider whether this edge case reflects intended behavior or if the SDK should reject/handle empty tokens differently. If empty strings are valid (e.g., representing "no token"), this test is appropriate.


308-325: LGTM!

Good test for verifying the fire-and-forget pattern handles rapid consecutive updates correctly, with the final state reflecting the last update. Based on learnings, this aligns with the intentional design decision for backward compatibility.


253-306: LGTM!

Good JSON serialization round-trip tests for both AuthState and Keys. The comment on line 295 appropriately notes the nested serialization nuance for Keys.

test/test_helpers/jwt_test_helper.dart (6)

9-17: LGTM! Well-defined test constants.

The test constants provide clear, reusable values for common JWT claims and are appropriately defined as const.


20-52: LGTM! Flexible token creation with sensible defaults.

The method provides good flexibility through optional parameters while maintaining sensible defaults for all required claims.


108-143: LGTM! Convenient helper methods for common test scenarios.

These specialized factory methods make it easy to create tokens for specific test cases without manually specifying all parameters.


145-150: LGTM! Comprehensive feature flag examples.

The test feature flags cover multiple data types (boolean, string, integer) and follow the expected structure with type and value keys.


152-167: LGTM! Correct JWT construction for testing.

The JWT structure and base64URL encoding are correct. Using a placeholder signature is appropriate since signature validation is not required for these test scenarios.


170-174: LGTM! Nice convenience extension.

The extension method provides a fluent, readable way to create expiring tokens in tests (e.g., Duration(hours: 1).toExpiringToken()).

test/test_helpers/auth_state_fixtures.dart (1)

8-131: LGTM! Comprehensive and well-structured test fixtures.

The AuthStateFixtures class provides excellent coverage of common testing scenarios:

  • Various expiration states (valid, expired, expiring in duration)
  • Different token combinations (with/without ID token, empty state)
  • Domain-specific scenarios (feature flags, permissions, organizations)
  • JSON deserialization testing (expires_in vs. ISO date formats)

Each factory method is clear, concise, and appropriately delegates to JwtTestHelper for token generation. The use of const constructors (line 62) and sensible defaults throughout makes these fixtures easy to use in tests.

Copy link
Member

@victoreronmosele victoreronmosele left a comment

Choose a reason for hiding this comment

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

Requested some changes for clean up and to streamline the tests.

Copy link
Member

@victoreronmosele victoreronmosele left a comment

Choose a reason for hiding this comment

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

Requesting some changes with how we test time sensitive logic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
test/token_utils_test.dart (1)

154-165: Remove or differentiate the duplicate getUserDetails happy‑path test

test('should handle user details with all fields populated', ...) is effectively identical to test('should return user details from id token', ...) above (same fixture, same expectations). Consider removing the duplicate or varying the scenario (e.g., partially populated fields) to add extra coverage instead of repetition.

test/auth_state_test.dart (2)

36-110: fromJson tests give good coverage of JSON shapes and edge cases

Nice coverage of:

  • expires_in vs ISO timestamp inputs
  • missing grant_type defaulting
  • missing optional fields
  • zero and negative expires_in behavior

This should catch most regressions in AuthState.fromJson. The existing feedback about moving away from DateTime.now-based tolerances via a test clock still applies; beyond that, nothing additional from my side.


161-201: Consider making isExpired tests fully deterministic with a test clock

The isExpired group nicely covers the main branches (no token, no expiry, clearly expired, clearly not expired, near‑boundary). A couple of the “near now” cases (e.g., expiring in 1 second) still depend on real wall‑clock timing and could, in extreme slow CI environments, become flaky.

Once AuthState.isExpired is wired to a testable clock (as discussed in prior comments), it would be worth wrapping these in a fixed clock (withClock(...)) or letting the fixtures accept an explicit base time so the tests are entirely deterministic.

🧹 Nitpick comments (3)
test/token_utils_test.dart (2)

11-21: Consider removing or using setAuthState to avoid dead code

setAuthState is never used in this file; tests set tokenUtils.authState directly. Either switch tests to use the helper or drop it to keep the test helper minimal.


55-69: Tighten and/or de-couple parseToken error expectations from implementation details

The malformed-token test asserts a raw RangeError, while the invalid‑base64 test accepts throwsA(anything). That combination is a bit inconsistent and couples tests to the current internal implementation. You might want to either:

  • assert a more domain‑level error type (e.g., a KindeError) where appropriate, or
  • relax the first case to “any exception” if you only care that malformed tokens fail.

This is optional but will make future refactors of parseToken less brittle.

test/auth_state_test.dart (1)

112-159: Tighten the round‑trip toJson/fromJson test and clean up the comment

The round‑trip test is good, but you can make it clearer and a bit stronger:

  • The comment about converting expires_in no longer matches what the code does.
  • You’re not currently asserting that accessTokenExpirationDateTime is preserved through the round‑trip, even though fromJson can reconstruct it from the ISO string.

Consider this small refactor:

       test('should round-trip through JSON correctly', () {
         final original = AuthStateFixtures.createValid();
         final json = original.toJson();
-
-        // Convert expires_in format for fromJson compatibility
-        final jsonForParsing = Map<String, dynamic>.from(json);
-
-        final restored = AuthState.fromJson(jsonForParsing);
+        final restored = AuthState.fromJson(
+          Map<String, dynamic>.from(json),
+        );
 
         expect(restored.accessToken, original.accessToken);
         expect(restored.refreshToken, original.refreshToken);
         expect(restored.idToken, original.idToken);
         expect(restored.grantType, original.grantType);
         expect(restored.scope, original.scope);
+        expect(
+          restored.accessTokenExpirationDateTime,
+          original.accessTokenExpirationDateTime,
+        );
       });

This keeps the intent the same while verifying the expiry field and removing an outdated comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eaa5536 and 8dac547.

📒 Files selected for processing (3)
  • test/auth_state_test.dart (1 hunks)
  • test/kinde_flutter_sdk_core_test.dart (1 hunks)
  • test/token_utils_test.dart (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/kinde_flutter_sdk_core_test.dart
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:110-124
Timestamp: 2025-06-20T10:43:27.820Z
Learning: The PR #27 for the Flutter starter kit example app is designed to work with Kinde Flutter SDK version 1.0.4, where the register() method will return a token if registration is successful. The current build errors are expected since the code targets a future SDK version.
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:79-94
Timestamp: 2025-06-20T10:44:49.809Z
Learning: In the Kinde Flutter SDK, version 1.0.4 will change the login() method to return a token when login is successful, rather than returning void. Code in this repository may be written to anticipate this future API change.
📚 Learning: 2025-11-27T10:12:42.833Z
Learnt from: KeeganBeuthin
Repo: kinde-oss/kinde-flutter-sdk PR: 52
File: lib/src/store/store.dart:54-57
Timestamp: 2025-11-27T10:12:42.833Z
Learning: In the Kinde Flutter SDK's Store class (lib/src/store/store.dart), the authState and keys setters intentionally use a fire-and-forget pattern when writing to flutter_secure_storage. This maintains API compatibility with the previous Hive-based implementation and avoids introducing breaking changes by keeping the setters synchronous rather than converting them to async methods.

Applied to files:

  • test/auth_state_test.dart
📚 Learning: 2025-11-27T10:13:04.736Z
Learnt from: KeeganBeuthin
Repo: kinde-oss/kinde-flutter-sdk PR: 52
File: lib/src/store/store.dart:44-47
Timestamp: 2025-11-27T10:13:04.736Z
Learning: In the kinde-flutter-sdk Store class (lib/src/store/store.dart), the authState and keys setters use a fire-and-forget pattern for writing to flutter_secure_storage to maintain backward compatibility with the previous Hive-based implementation, which also had synchronous setters. This is an intentional design decision to avoid breaking changes.

Applied to files:

  • test/auth_state_test.dart
📚 Learning: 2025-06-20T10:43:27.820Z
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:110-124
Timestamp: 2025-06-20T10:43:27.820Z
Learning: The PR #27 for the Flutter starter kit example app is designed to work with Kinde Flutter SDK version 1.0.4, where the register() method will return a token if registration is successful. The current build errors are expected since the code targets a future SDK version.

Applied to files:

  • test/auth_state_test.dart
📚 Learning: 2025-06-20T10:44:49.809Z
Learnt from: kartuli98
Repo: kinde-oss/kinde-flutter-sdk PR: 27
File: example/lib/state/app_state_manager.dart:79-94
Timestamp: 2025-06-20T10:44:49.809Z
Learning: In the Kinde Flutter SDK, version 1.0.4 will change the login() method to return a token when login is successful, rather than returning void. Code in this repository may be written to anticipate this future API change.

Applied to files:

  • test/auth_state_test.dart
🔇 Additional comments (3)
test/auth_state_test.dart (3)

10-34: Constructor tests correctly cover full and default initialization

These tests validate both a fully-populated AuthState and the default constructor (including grantType default), which is exactly what you want here. No issues spotted.


203-229: getToken tests cover both token types and null cases well

These tests exercise both TokenType.accessToken and TokenType.idToken, plus the null branches, which is exactly the behavior surface of getToken. Looks solid.


231-306: createRequestTokenParam tests thoroughly cover inclusion/exclusion rules

Good coverage of:

  • happy‑path map creation
  • excluding null refreshToken / grantType
  • excluding empty or null scope
  • fully empty case

This should give high confidence that request params aren’t polluted with null/empty values. No further changes needed from my side.

@KeeganBeuthin
Copy link
Contributor Author

Hello @victoreronmosele thank you for the feedback. I have taken this into account and removed the accidental duplicate tests as well as added the requested assertions as well as improving the logout tests.

With regard to using withClock, your suggestions are certainly valid, however that would require modifying auth_state.dart which is outside of the scope of this particular pr. I do agree that it would make tests more deterministic and we can look at making that change in a follow up pr.

With regard to the positive getToken test, I attempted this, but getToken() validates that the session was established through the proper login flow - setting sdk.authState directly results in [session-expired-or-invalid]. A true positive test would require mocking the full auth.

Please let me know if you have any further feedback bro :)

@victoreronmosele
Copy link
Member

With regard to using withClock, your suggestions are certainly valid, however that would require modifying auth_state.dart which is outside of the scope of this particular pr.

You're right. We can improve this in a follow up PR.

@victoreronmosele
Copy link
Member

Reviewing the updates.

@victoreronmosele
Copy link
Member

With regard to the positive getToken test, I attempted this, but getToken() validates that the session was established through the proper login flow - setting sdk.authState directly results in [session-expired-or-invalid]. A true positive test would require mocking the full auth.

We can also improve this in a different PR. I'll also look into it.

@victoreronmosele
Copy link
Member

Approved!

@KeeganBeuthin KeeganBeuthin changed the title Comprehensive overhaul of the Kinde Flutter SDK test suite Comprehensive upgrade of Kinde Flutter SDK test coverage Dec 8, 2025
KeeganBeuthin added a commit to KeeganBeuthin/kinde-flutter-sdk that referenced this pull request Dec 17, 2025
Created comprehensive, maintainable test suite for UsersApi following
pragmatic, enterprise-grade principles with zero over-engineering.

## Key Achievements:

- Upgraded 5 API methods from placeholder to comprehensive tests
- Increased test count from 5 → 50 tests (+900%)
- Removed all TODO comments
- Implemented proper HTTP mocking with http_mock_adapter
- Added comprehensive error scenarios (400, 401, 403, 404, 500)
- Added request validation tests
- Used inline fixtures (no premature abstraction)
- Followed AAA pattern throughout
- Zero linter errors

## Test Coverage:

- createUser: 13 tests (success, errors, request validation)
- getUserData: 7 tests (success, errors, request validation)
- getUsers: 14 tests (success, pagination, filtering, sorting, errors)
- updateUser: 7 tests (success, errors, request validation)
- deleteUser: 9 tests (success, errors, request validation)

## Quality Standards Met:

✅ Proper HTTP mocking (not generic DioAdapterMock)
✅ Inline, readable test data (no massive fixtures file)
✅ AAA pattern (Arrange, Act, Assert)
✅ Multiple specific assertions (not just isNotNull)
✅ Clear, descriptive test names
✅ Request structure validation
✅ Response structure validation
✅ Edge case coverage (pagination, optional params)
✅ Aligned with PR kinde-oss#53 patterns
✅ Self-reviewed and reflected before implementation

## Documentation:

- TEST_MODERNIZATION_APPROACH.md: Pragmatic strategy & rationale
- PHASE_1_PROTOTYPE_COMPLETE.md: Comprehensive completion report

## Next Steps:

- User review & test execution
- Evaluate patterns & repetition
- Decide on helper extraction (if needed)
- Apply template to remaining 13 API test files

This prototype serves as the template for modernizing all 14 API test files
with enterprise-grade quality and maintainability.
Copy link

@dtoxvanilla1991 dtoxvanilla1991 left a comment

Choose a reason for hiding this comment

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

Good stuff. Well done. Left several 👣


/// Mock store for testing that doesn't require Flutter secure storage.
/// This provides direct access to auth state manipulation for unit tests.
class MockStore {
Copy link

@dtoxvanilla1991 dtoxvanilla1991 Feb 12, 2026

Choose a reason for hiding this comment

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

Dead code 💀 - file never imported.

MockStore is not referenced by any test file in this PR or the existing codebase. A grep across the entire workspace (including ignored files) returns zero usages of MockStore.

This was flagged by @victoreronmosele in a prior review thread and the thread was resolved, but, interestingly, the file still ships in this final commit. Please remove it entirely to avoid dead code accumulation.

Comment on lines +118 to +126
test('code verifier length requirements per RFC 7636', () {
// RFC 7636 specifies code_verifier length between 43-128 characters
const minLength = 43;
const maxLength = 128;

expect(minLength, lessThanOrEqualTo(maxLength));
expect(minLength, greaterThanOrEqualTo(43));
expect(maxLength, lessThanOrEqualTo(128));
});

Choose a reason for hiding this comment

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

Tautological test — asserts constants against themselves, tests no SDK behavior.

This test declares minLength = 43 then asserts expect(minLength, greaterThanOrEqualTo(43)). This will always pass regardless of the SDK's actual implementation. It doesn't invoke any code verifier generation logic.

Either remove this test or replace it with an assertion that calls into actual SDK code to verify the generated code verifier's length falls within the RFC 7636 range (43–128 chars). If web crypto isn't available in VM tests, document that as a known gap rather than shipping a no-op assertion.

Comment on lines +129 to +140
group('Auth State Generation', () {
test('auth state should be sufficiently random', () {
// Auth state is used to prevent CSRF attacks
// It should be unpredictable and unique per request

// Verify expected length based on typical implementations
// Most implementations use 16-32 bytes encoded as hex or base64
const expectedMinLength = 16; // 16 bytes minimum for security

expect(expectedMinLength, greaterThanOrEqualTo(16));
});
});

Choose a reason for hiding this comment

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

Same issue — tautological test.

expect(expectedMinLength, greaterThanOrEqualTo(16)) where expectedMinLength is a const 16 defined two lines above. This tests nothing about the SDK's auth state generation. Remove or replace with a real assertion against actual generated state values.

Comment on lines +18 to +20
void setAuthState(AuthState? state) {
authState = state;
}
Copy link

@dtoxvanilla1991 dtoxvanilla1991 Feb 12, 2026

Choose a reason for hiding this comment

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

Dead code ☠️ - setAuthState is never called.

All tests in this file set tokenUtils.authState directly via the public field. This helper method adds no value. Remove it to keep the test helper minimal - the public field is sufficient.


test('should throw session-expired-or-invalid when auth state invalid', () async {
// Setting authState without proper validation still triggers error
sdk.authState = AuthState(
Copy link

@dtoxvanilla1991 dtoxvanilla1991 Feb 12, 2026

Choose a reason for hiding this comment

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

Advisory: sdk.authState = writes to the TokenUtils mixin field, not to Store.

In prod, KindeFlutterSDK.authState getter reads from _store.authState, but this assignment writes to the mixin's @protected AuthState? authState field. This means the test is exercising a disconnected state path - the Store's cached state remains null while the mixin's field gets populated.

This doesn't break anything in this PR (since the subsequent getToken() call fails for other reasons), but it's worth noting for future test authors: positive-path tests that rely on sdk.authState = ... won't reflect the real runtime behavior where state flows through Store. Consider adding a comment here noting this limitation, or in a follow-up PR, add a setter override in KindeFlutterSDK that delegates to Store.

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.

3 participants