Skip to content

Setup test environment#1

Merged
AliAkrem merged 13 commits intomasterfrom
add-tests/auth-test
Apr 6, 2025
Merged

Setup test environment#1
AliAkrem merged 13 commits intomasterfrom
add-tests/auth-test

Conversation

@AliAkrem
Copy link
Copy Markdown
Owner

@AliAkrem AliAkrem commented Apr 6, 2025

Description

This PR implements the initial test infrastructure for API integration testing. It includes:

  • Basic authentication test setup with login/logout scenarios
  • Mock implementations for secure storage
  • HTTP client overrides for testing
  • Environment configuration setup

Types of changes

  • Bug Fix (non-breaking change which fixes an issue)
  • New Feature (non-breaking change which adds functionality)
  • Others (any other types not listed above)

Checklist

Further comments

The test infrastructure is designed to be extensible for future API endpoint testing. Key design decisions:

  • Using mock secure storage to avoid real device storage during tests
  • Implementing HTTP overrides to handle SSL certificates in test environment

Related Issue

Closes: #

Summary by CodeRabbit

  • New Features
    • Enhanced environment configuration management to bolster application stability and security.
  • Refactor
    • Updated secure network handling for increased flexibility and robustness.
  • Tests
    • Expanded test coverage for authentication and network operations to ensure consistent quality.
  • Chores
    • Revamped build and deployment workflows with improved security and controlled execution.

These improvements contribute to a more stable and secure application experience for end-users.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2025

Walkthrough

This pull request reinstates the .env.production entry in the .gitignore file and updates the ApiClient class in the network module to support dependency injection via its constructor. In addition, the pubspec.yaml now includes a new development dependency (flutter_dotenv) while removing an unused asset. The PR also adds new test files covering authentication, mock implementations for secure storage and HTTP overrides, and detailed testing documentation. Finally, it introduces a CODEOWNERS file and enhances the CI workflow with manual trigger inputs and security checks.

Changes

File(s) Summary of Changes
.gitignore Reinstated the .env.production entry.
lib/core/network/api_client.dart Updated _secureStorage declaration and modified the constructor to accept an optional FlutterSecureStorage instance (dependency injection).
pubspec.yaml Added flutter_dotenv: ^5.2.1 as a dev dependency and removed the .env.test asset.
test/auth/auth_api_test.dart New test file for the authentication API, covering login (valid/invalid) and logout functionality.
test/helpers/{mock_secure_storage.dart, test_http_override.dart} Added MockSecureStorage and TestHttpOverrides classes to provide mock implementations for secure storage and HTTP client overrides for testing.
test/widget_test.dart Removed a basic widget test file.
test/README.md New documentation file outlining integration test setup, environment variables, and testing best practices.
.github/CODEOWNERS New file outlining file ownership rules for CI/CD configuration files within the repository.
.github/workflows/ci.yml Updated the CI workflow: added a force_run input, introduced check-changes and security-check jobs, and modified the build job to depend on these checks.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CI_Workflow as "GitHub CI Workflow"
  participant CheckChanges as "check-changes Job"
  participant SecurityCheck as "security-check Job"
  participant Build as "Build Job"

  User->>CI_Workflow: Trigger workflow (commit/force_run)
  CI_Workflow->>CheckChanges: Run check-changes job
  CheckChanges-->>CI_Workflow: Report file change status
  CI_Workflow->>SecurityCheck: Run security-check job if needed
  SecurityCheck-->>CI_Workflow: Return security approval
  CI_Workflow->>Build: Initiate build job based on checks
  Build-->>User: Deliver build status and test results
Loading

Poem

I'm just a bunny, hopping through the code,
Carrots of changes lined in every node.
Secrets tucked away, now hidden from the light,
With tests and workflows making every build just right.
In burrows deep of CI, I nibble on each new feature,
Hop along, dear coder, to a future even brighter!
🥕🐇


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Free

📥 Commits

Reviewing files that changed from the base of the PR and between b8d417a and fbc1510.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml (4 hunks)

Note

🎁 Summarized by CodeRabbit Free

Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting https://app.coderabbit.ai/login.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
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)
.env.example (1)

1-2: Consider adding descriptive comments

The example file correctly provides template variables without exposing actual credentials. This follows best practices for environment configuration. Consider adding comments to explain the purpose and expected format of these credentials to help other developers understand how to use them.

-TEST_USERNAME=
-TEST_PASSWORD=
+# Username for authentication testing (email format)
+TEST_USERNAME=
+# Password for authentication testing (min 8 characters)
+TEST_PASSWORD=
test/helpers/test_http_override.dart (1)

3-9: Add documentation about security implications

The implementation correctly overrides the HTTP client to bypass SSL certificate validation for testing purposes, which aligns with your PR objective. However, this approach has security implications that should be documented.

+/// Test-only HTTP overrides that bypass SSL certificate validation.
+/// WARNING: Only use for testing. Never use in production as it creates security vulnerabilities.
 class TestHttpOverrides extends HttpOverrides {
   @override
   HttpClient createHttpClient(SecurityContext? context) {
     return super.createHttpClient(context)
+      // Accepts any SSL certificate to facilitate testing with self-signed certificates
       ..badCertificateCallback = (cert, host, port) => true;
   }
 }
test/helpers/mock_secure_storage.dart (1)

1-126: Well-implemented mock class for FlutterSecureStorage

The MockSecureStorage class is a good implementation for testing purposes. It correctly implements the main storage operations with a simple in-memory Map, allowing for tests that don't rely on actual device storage.

However, I'd suggest a couple of improvements:

  1. When writing null values in the write method (line 18), you're converting them to empty strings. This might behave differently from the actual FlutterSecureStorage implementation, which could lead to unexpected test behavior.
- _storage[key] = value ?? '';
+ _storage[key] = value;
  1. Consider adding a comment at the top of the class explaining its purpose for future developers.
test/auth/auth_api_test.dart (3)

52-62: Improve error handling in failed login test

The test correctly verifies that invalid credentials throw an exception, but it doesn't verify the specific type or message of the exception.

Consider expecting a more specific exception type or message:

try {
  await authRepository.login(invalidUsername, invalidPassword);
  fail('Expected an exception to be thrown');
} catch (e) {
  expect(e, isA<Exception>());
+ expect(e.toString(), contains('Invalid credentials'));
}

65-81: Avoid duplication in test setup

The logout test duplicates the environment variable loading logic from the first test.

Consider extracting credentials retrieval to a helper method:

String getTestUsername() {
  final username = dotenv.env['TEST_USERNAME'];
  expect(username, isNotNull, reason: 'TEST_USERNAME environment variable is required');
  return username!;
}

String getTestPassword() {
  final password = dotenv.env['TEST_PASSWORD'];
  expect(password, isNotNull, reason: 'TEST_PASSWORD environment variable is required');
  return password!;
}

10-26: Add a test for failed environment setup

The current tests assume the .env.test file exists and contains valid credentials, but there's no test for handling missing or invalid environment configuration.

Consider adding a test that verifies proper error handling when environment variables are missing:

test('Should handle missing environment variables gracefully', () async {
  // Temporarily clear environment variables
  final originalUsername = dotenv.env['TEST_USERNAME'];
  final originalPassword = dotenv.env['TEST_PASSWORD'];
  dotenv.env.remove('TEST_USERNAME');
  dotenv.env.env.remove('TEST_PASSWORD');
  
  try {
    // Your test code that should handle missing variables
    // ...
  } finally {
    // Restore environment variables
    if (originalUsername != null) dotenv.env['TEST_USERNAME'] = originalUsername;
    if (originalPassword != null) dotenv.env['TEST_PASSWORD'] = originalPassword;
  }
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8b8916e and 669dd81.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • .env.example (1 hunks)
  • .gitignore (1 hunks)
  • lib/core/network/api_client.dart (1 hunks)
  • pubspec.yaml (1 hunks)
  • test/auth/auth_api_test.dart (1 hunks)
  • test/helpers/mock_secure_storage.dart (1 hunks)
  • test/helpers/test_http_override.dart (1 hunks)
  • test/widget_test.dart (0 hunks)
💤 Files with no reviewable changes (1)
  • test/widget_test.dart
🔇 Additional comments (5)
.gitignore (1)

45-47: Good practice for environment configuration security

Adding these environment files to .gitignore ensures that sensitive information like API keys and test credentials are kept out of version control, which is a best practice for security. This aligns with your PR objective of separating test credentials into a dedicated file to enhance security.

pubspec.yaml (2)

33-33: Appropriate dev dependency for environment configuration

Using flutter_dotenv as a development dependency is appropriate for managing environment variables in testing scenarios.


41-42: Verify env file usage is test-only

Including .env.test as an asset ensures it's available during testing. However, be cautious that this means the file will be bundled with the app during builds.

Ensure that .env.test is only loaded in test code and not accidentally used in production builds. Consider implementing environment-specific asset loading to prevent test credentials from being included in production builds.

lib/core/network/api_client.dart (1)

7-7: Good refactoring to support dependency injection

The changes to the ApiClient constructor allow for better testability through dependency injection of the FlutterSecureStorage. This is an excellent practice for enabling unit testing with mock implementations.

Also applies to: 9-10

test/auth/auth_api_test.dart (1)

13-16: Good test environment setup

Loading environment variables from a dedicated test file and setting up HTTP overrides are good practices for isolating tests from production environments.

Comment on lines +28 to +51
test('Login API should return valid AuthResponse', () async {
final username = dotenv.env['TEST_USERNAME'] ?? 'TEST_username_';
final password = dotenv.env['TEST_PASSWORD'] ?? 'TEST_password_';

final authResponse = await authRepository.login(username, password);

// Verify response structure
expect(authResponse, isA<AuthResponse>());
expect(authResponse.token, isNotEmpty);
expect(authResponse.uuid, isNotEmpty);
expect(authResponse.etablissementId, isNotNull);

// Verify data was saved
final isLoggedIn = await authRepository.isLoggedIn();
expect(isLoggedIn, true);

final etablissementId = await authRepository.getEtablissementId();
expect(etablissementId, isNotNull);

// Cleanup
await authRepository.logout();
final isLoggedOut = await authRepository.isLoggedIn();
expect(isLoggedOut, false);
});
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.

🛠️ Refactor suggestion

Comprehensive login test but consider improving environment variable handling

The test covers all necessary assertions for a successful login flow, including token validation and cleanup.

However, using fallback values for missing environment variables might hide configuration issues:

- final username = dotenv.env['TEST_USERNAME'] ?? 'TEST_username_';
- final password = dotenv.env['TEST_PASSWORD'] ?? 'TEST_password_';
+ final username = dotenv.env['TEST_USERNAME'];
+ final password = dotenv.env['TEST_PASSWORD'];
+ expect(username, isNotNull, reason: 'TEST_USERNAME environment variable is required');
+ expect(password, isNotNull, reason: 'TEST_PASSWORD environment variable is required');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('Login API should return valid AuthResponse', () async {
final username = dotenv.env['TEST_USERNAME'] ?? 'TEST_username_';
final password = dotenv.env['TEST_PASSWORD'] ?? 'TEST_password_';
final authResponse = await authRepository.login(username, password);
// Verify response structure
expect(authResponse, isA<AuthResponse>());
expect(authResponse.token, isNotEmpty);
expect(authResponse.uuid, isNotEmpty);
expect(authResponse.etablissementId, isNotNull);
// Verify data was saved
final isLoggedIn = await authRepository.isLoggedIn();
expect(isLoggedIn, true);
final etablissementId = await authRepository.getEtablissementId();
expect(etablissementId, isNotNull);
// Cleanup
await authRepository.logout();
final isLoggedOut = await authRepository.isLoggedIn();
expect(isLoggedOut, false);
});
test('Login API should return valid AuthResponse', () async {
final username = dotenv.env['TEST_USERNAME'];
final password = dotenv.env['TEST_PASSWORD'];
expect(username, isNotNull, reason: 'TEST_USERNAME environment variable is required');
expect(password, isNotNull, reason: 'TEST_PASSWORD environment variable is required');
final authResponse = await authRepository.login(username, password);
// Verify response structure
expect(authResponse, isA<AuthResponse>());
expect(authResponse.token, isNotEmpty);
expect(authResponse.uuid, isNotEmpty);
expect(authResponse.etablissementId, isNotNull);
// Verify data was saved
final isLoggedIn = await authRepository.isLoggedIn();
expect(isLoggedIn, true);
final etablissementId = await authRepository.getEtablissementId();
expect(etablissementId, isNotNull);
// Cleanup
await authRepository.logout();
final isLoggedOut = await authRepository.isLoggedIn();
expect(isLoggedOut, false);
});

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2025

✅ CI build completed successfully!
The APK is available as an artifact in this workflow run.
Download the APK from: https://github.com/AliAkrem/progres/actions/runs/14294937548

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2025

✅ CI build completed successfully!
The APK is available as an artifact in this workflow run.
Download the APK from: https://github.com/AliAkrem/progres/actions/runs/14295221819

@AliAkrem AliAkrem merged commit 32a6d32 into master Apr 6, 2025
4 checks passed
@AliAkrem AliAkrem deleted the add-tests/auth-test branch April 6, 2025 20:00
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