Files
sure/mobile/test/services/api_config_headers_test.dart
Michal Tajchert 96c893ec18 Mobile: custom proxy headers + small login UX fixes (#1748)
* Add mobile custom proxy headers

* Clear login placeholders on focus

Email/password fields ship with example values pre-filled. Tapping the
field now clears the placeholder so users don't have to delete it
manually. Skips clearing if the user has already edited the value.

* Push Configuration as a route from Sign in

Opening Configuration from the Sign in screen now uses Navigator.push
instead of toggling a state flag, so Android back returns to Sign in
instead of quitting the app. Saving the URL auto-pops the route.

* Address PR review on custom proxy headers

- Test Connection no longer leaves global ApiConfig headers mutated;
  unsaved edits are restored in a finally block after the probe.
- _loadSavedUrl / _loadCustomHeaders wrap storage reads in try/catch and
  always finish initialization with sensible defaults.
- Sanitization is now a single CustomProxyHeader.sanitize() reused by
  ApiConfig.setCustomProxyHeaders and CustomProxyHeadersService.
- Brief comment on redactedValue explaining the length-obscuring design.

* Harden custom proxy header validation and load path

- validateValue now rejects ASCII control characters (CR/LF/tab/etc.)
  to prevent header-injection via crafted values.
- loadHeaders moves the secure-storage read inside the try block so
  platform exceptions are caught the same way JSON parse errors are.
2026-05-11 23:09:21 +02:00

79 lines
2.5 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sure_mobile/models/custom_proxy_header.dart';
import 'package:sure_mobile/services/api_config.dart';
void main() {
setUp(() async {
SharedPreferences.setMockInitialValues({});
ApiConfig.clearApiKeyAuth();
ApiConfig.setBaseUrl(ApiConfig.defaultBaseUrl);
ApiConfig.setCustomProxyHeaders([]);
});
test('adds custom proxy headers to token auth headers', () {
ApiConfig.setCustomProxyHeaders([
CustomProxyHeader(name: 'X-Auth-Id', value: 'id'),
CustomProxyHeader(name: 'X-Auth-Secret', value: 'secret'),
]);
expect(ApiConfig.getAuthHeaders('token'), {
'X-Auth-Id': 'id',
'X-Auth-Secret': 'secret',
'Authorization': 'Bearer token',
'Accept': 'application/json',
});
});
test('adds custom proxy headers to unauthenticated json headers', () {
ApiConfig.setCustomProxyHeaders([
CustomProxyHeader(name: 'X-Mobile-Bypass', value: 'pass'),
]);
expect(ApiConfig.jsonHeaders(), {
'X-Mobile-Bypass': 'pass',
'Content-Type': 'application/json',
'Accept': 'application/json',
});
});
test('drops headers with reserved names', () {
ApiConfig.setCustomProxyHeaders([
CustomProxyHeader(name: 'Accept', value: 'text/plain'),
CustomProxyHeader(name: 'Authorization', value: 'should-be-dropped'),
CustomProxyHeader(name: 'X-Api-Key', value: 'should-be-dropped-too'),
CustomProxyHeader(name: 'Content-Type', value: 'application/xml'),
CustomProxyHeader(name: 'X-Auth-Id', value: 'id'),
]);
final result = ApiConfig.customProxyHeaders;
expect(result.length, 1);
expect(result.first.name, 'X-Auth-Id');
});
test('deduplicates headers by normalized name keeping the last value', () {
ApiConfig.setCustomProxyHeaders([
CustomProxyHeader(name: 'X-Auth-Id', value: 'first'),
CustomProxyHeader(name: 'x-auth-id', value: 'second'),
CustomProxyHeader(name: 'X-Auth-Id', value: 'third'),
]);
final result = ApiConfig.customProxyHeaders;
expect(result.length, 1);
expect(result.first.name, 'X-Auth-Id');
expect(result.first.value, 'third');
});
test('app managed headers win over custom headers', () {
ApiConfig.setCustomProxyHeaders([
CustomProxyHeader(name: 'Accept', value: 'text/plain'),
CustomProxyHeader(name: 'X-Auth-Id', value: 'id'),
]);
expect(ApiConfig.htmlHeaders(), {
'X-Auth-Id': 'id',
'Accept': 'text/html',
});
});
}