Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/supabase_flutter/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ include: package:flutter_lints/flutter.yaml
linter:
rules:
avoid_print: false
trailing_commas: preserve
101 changes: 101 additions & 0 deletions packages/supabase_flutter/test/auth_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import 'widget_test_stubs.dart';

class _MockLocalStorage extends MockLocalStorage {
bool _initializeCalled = false;

bool get initializeCalled => _initializeCalled;

@override
Future<void> initialize() async {
_initializeCalled = true;
return super.initialize();
}
}

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

const supabaseUrl = '';
const supabaseKey = '';

group('Authentication', () {
setUp(() async {
try {
await Supabase.instance.dispose();
} catch (e) {
// Ignore dispose errors
}

mockAppLink();
});

tearDown(() async {
try {
await Supabase.instance.dispose();
} catch (e) {
// Ignore dispose errors
}
});

group('Session management', () {
test('initializes local storage on initialize', () async {
final mockStorage = _MockLocalStorage();

await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: false,
authOptions: FlutterAuthClientOptions(
localStorage: mockStorage,
pkceAsyncStorage: MockAsyncStorage(),
),
);

// Give time for initialization to complete
await Future.delayed(const Duration(milliseconds: 100));

expect(mockStorage.initializeCalled, isTrue);
});
});

group('Session recovery', () {
test('handles corrupted session data gracefully', () async {
final corruptedStorage = MockExpiredStorage();

await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: false,
authOptions: FlutterAuthClientOptions(
localStorage: corruptedStorage,
pkceAsyncStorage: MockAsyncStorage(),
),
);

// MockExpiredStorage returns an expired session, not null
expect(Supabase.instance.client.auth.currentSession, isNotNull);
expect(Supabase.instance.client.auth.currentSession?.isExpired, isTrue);
});

test('handles null session during initialization', () async {
final emptyStorage = MockEmptyLocalStorage();

await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: false,
authOptions: FlutterAuthClientOptions(
localStorage: emptyStorage,
pkceAsyncStorage: MockAsyncStorage(),
),
);

// Should handle empty storage gracefully
expect(Supabase.instance.client.auth.currentSession, isNull);
});
});
});
}
174 changes: 174 additions & 0 deletions packages/supabase_flutter/test/initialization_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import 'widget_test_stubs.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

const supabaseUrl = '';
const supabaseKey = '';

group('Supabase initialization', () {
setUp(() {
SharedPreferences.setMockInitialValues({});
mockAppLink();
});

tearDown(() async {
try {
await Supabase.instance.dispose();
} catch (e) {
// Ignore dispose errors
}
});

group('Basic initialization', () {
test('initialize successfully with default options', () async {
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
);

expect(Supabase.instance, isNotNull);
expect(Supabase.instance.client, isNotNull);
});
});

group('Custom storage initialization', () {
test('initialize successfully with custom localStorage', () async {
final localStorage = MockLocalStorage();
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
authOptions: FlutterAuthClientOptions(
localStorage: localStorage,
),
);

expect(Supabase.instance, isNotNull);
expect(Supabase.instance.client, isNotNull);
});

test('handles initialization with expired session in storage', () async {
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: true,
authOptions: FlutterAuthClientOptions(
localStorage: MockExpiredStorage(),
pkceAsyncStorage: MockAsyncStorage(),
),
);

// Should handle expired session gracefully
expect(Supabase.instance.client.auth.currentSession, isNotNull);
});
});

group('Auth options initialization', () {
test('initialize successfully with PKCE auth flow', () async {
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
authOptions: const FlutterAuthClientOptions(
authFlowType: AuthFlowType.pkce,
),
);

expect(Supabase.instance, isNotNull);
expect(Supabase.instance.client, isNotNull);
});
});

group('Custom client initialization', () {
test('initialize successfully with custom HTTP client', () async {
final httpClient = PkceHttpClient();
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
httpClient: httpClient,
);

expect(Supabase.instance, isNotNull);
expect(Supabase.instance.client, isNotNull);
});

test('initialize successfully with custom access token', () async {
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
accessToken: () async => 'custom-access-token',
);

expect(Supabase.instance, isNotNull);
expect(Supabase.instance.client, isNotNull);

// Should throw AuthException when trying to access auth
expect(
() => Supabase.instance.client.auth,
throwsA(isA<AuthException>()),
);
});
});

group('Multiple initialization and disposal', () {
test('dispose and reinitialize works', () async {
// First initialization
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
);

expect(Supabase.instance, isNotNull);

// Dispose
await Supabase.instance.dispose();

// Need to run the event loop to let the dispose complete
await Future.delayed(Duration.zero);

// Re-initialize should work without errors
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
);

expect(Supabase.instance, isNotNull);
});

test('handles multiple initializations correctly', () async {
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: false,
authOptions: FlutterAuthClientOptions(
localStorage: MockLocalStorage(),
pkceAsyncStorage: MockAsyncStorage(),
),
);

// Store first instance to verify it's different after re-initialization
final firstInstance = Supabase.instance.client;

// Dispose first instance before re-initializing
await Supabase.instance.dispose();

await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: true,
authOptions: FlutterAuthClientOptions(
localStorage: MockEmptyLocalStorage(),
pkceAsyncStorage: MockAsyncStorage(),
),
);

final secondInstance = Supabase.instance.client;
expect(secondInstance, isNotNull);
expect(identical(firstInstance, secondInstance), isFalse);
});
});
});
}
Loading