- Add permissions_test.dart (51 tests for Domain, Action, Role) - Add auth_state_test.dart (16 tests for AuthState) - Add user_preferences_test.dart (12 tests for UserPreferences) - Add permission_gate_test.dart (10 tests for permission logic) All 60 tests passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
281 lines
8.1 KiB
Dart
281 lines
8.1 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:tatlock_ui/core/auth/auth_state.dart';
|
|
import 'package:tatlock_ui/core/auth/permissions.dart';
|
|
import 'package:tatlock_ui/core/auth/user_preferences.dart';
|
|
|
|
void main() {
|
|
group('AuthState', () {
|
|
group('default constructor', () {
|
|
test('creates unauthenticated state by default', () {
|
|
const state = AuthState();
|
|
|
|
expect(state.isAuthenticated, isFalse);
|
|
expect(state.accessToken, isNull);
|
|
expect(state.refreshToken, isNull);
|
|
expect(state.expiresAt, isNull);
|
|
expect(state.userId, isNull);
|
|
expect(state.userName, isNull);
|
|
expect(state.userEmail, isNull);
|
|
expect(state.roles, isEmpty);
|
|
expect(state.preferences, isNull);
|
|
});
|
|
});
|
|
|
|
group('authenticated state', () {
|
|
test('stores all user data', () {
|
|
final expiresAt = DateTime.now().add(const Duration(hours: 1));
|
|
const preferences = UserPreferences(
|
|
theme: 'dark',
|
|
defaultRoom: 'kitchen',
|
|
);
|
|
|
|
final state = AuthState(
|
|
isAuthenticated: true,
|
|
accessToken: 'access_token',
|
|
refreshToken: 'refresh_token',
|
|
expiresAt: expiresAt,
|
|
userId: 'user-123',
|
|
authentikId: 'authentik-456',
|
|
userName: 'Test User',
|
|
userEmail: 'test@example.com',
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
roles: const [
|
|
Role(
|
|
id: '1',
|
|
name: 'admin.general:admin',
|
|
domain: Domain.admin,
|
|
category: 'general',
|
|
action: Action.admin,
|
|
),
|
|
],
|
|
preferences: preferences,
|
|
);
|
|
|
|
expect(state.isAuthenticated, isTrue);
|
|
expect(state.accessToken, equals('access_token'));
|
|
expect(state.refreshToken, equals('refresh_token'));
|
|
expect(state.expiresAt, equals(expiresAt));
|
|
expect(state.userId, equals('user-123'));
|
|
expect(state.authentikId, equals('authentik-456'));
|
|
expect(state.userName, equals('Test User'));
|
|
expect(state.userEmail, equals('test@example.com'));
|
|
expect(state.avatarUrl, equals('https://example.com/avatar.jpg'));
|
|
expect(state.roles.length, equals(1));
|
|
expect(state.preferences?.theme, equals('dark'));
|
|
});
|
|
});
|
|
|
|
group('isTokenExpired', () {
|
|
test('returns true when expiresAt is null', () {
|
|
const state = AuthState(isAuthenticated: true);
|
|
|
|
expect(state.isTokenExpired, isTrue);
|
|
});
|
|
|
|
test('returns true when token is expired', () {
|
|
final expiredTime = DateTime.now().subtract(const Duration(hours: 1));
|
|
final state = AuthState(
|
|
isAuthenticated: true,
|
|
expiresAt: expiredTime,
|
|
);
|
|
|
|
expect(state.isTokenExpired, isTrue);
|
|
});
|
|
|
|
test('returns true when token expires within 1 minute', () {
|
|
final soonExpires = DateTime.now().add(const Duration(seconds: 30));
|
|
final state = AuthState(
|
|
isAuthenticated: true,
|
|
expiresAt: soonExpires,
|
|
);
|
|
|
|
expect(state.isTokenExpired, isTrue);
|
|
});
|
|
|
|
test('returns false when token is valid', () {
|
|
final futureExpires = DateTime.now().add(const Duration(hours: 1));
|
|
final state = AuthState(
|
|
isAuthenticated: true,
|
|
expiresAt: futureExpires,
|
|
);
|
|
|
|
expect(state.isTokenExpired, isFalse);
|
|
});
|
|
});
|
|
|
|
group('hasPermission', () {
|
|
test('returns true when role grants permission', () {
|
|
const state = AuthState(
|
|
isAuthenticated: true,
|
|
roles: [
|
|
Role(
|
|
id: '1',
|
|
name: 'control-room.general:admin',
|
|
domain: Domain.controlRoom,
|
|
category: 'general',
|
|
action: Action.admin,
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(state.hasPermission(Domain.controlRoom, Action.admin), isTrue);
|
|
expect(state.hasPermission(Domain.controlRoom, Action.viewer), isTrue);
|
|
});
|
|
|
|
test('returns false when no role grants permission', () {
|
|
const state = AuthState(
|
|
isAuthenticated: true,
|
|
roles: [
|
|
Role(
|
|
id: '1',
|
|
name: 'media.general:viewer',
|
|
domain: Domain.media,
|
|
category: 'general',
|
|
action: Action.viewer,
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(state.hasPermission(Domain.controlRoom, Action.viewer), isFalse);
|
|
expect(state.hasPermission(Domain.media, Action.admin), isFalse);
|
|
});
|
|
|
|
test('respects category parameter', () {
|
|
const state = AuthState(
|
|
isAuthenticated: true,
|
|
roles: [
|
|
Role(
|
|
id: '1',
|
|
name: 'control-room.servers:admin',
|
|
domain: Domain.controlRoom,
|
|
category: 'servers',
|
|
action: Action.admin,
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(
|
|
state.hasPermission(
|
|
Domain.controlRoom,
|
|
Action.admin,
|
|
category: 'servers',
|
|
),
|
|
isTrue,
|
|
);
|
|
expect(
|
|
state.hasPermission(
|
|
Domain.controlRoom,
|
|
Action.admin,
|
|
category: 'general',
|
|
),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('returns false for empty roles list', () {
|
|
const state = AuthState(isAuthenticated: true, roles: []);
|
|
|
|
expect(state.hasPermission(Domain.controlRoom, Action.viewer), isFalse);
|
|
});
|
|
});
|
|
|
|
group('isGlobalAdmin', () {
|
|
test('returns true when user has admin.general:admin role', () {
|
|
const state = AuthState(
|
|
isAuthenticated: true,
|
|
roles: [
|
|
Role(
|
|
id: '1',
|
|
name: 'admin.general:admin',
|
|
domain: Domain.admin,
|
|
category: 'general',
|
|
action: Action.admin,
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(state.isGlobalAdmin, isTrue);
|
|
});
|
|
|
|
test('returns false when user lacks admin role', () {
|
|
const state = AuthState(
|
|
isAuthenticated: true,
|
|
roles: [
|
|
Role(
|
|
id: '1',
|
|
name: 'control-room.general:admin',
|
|
domain: Domain.controlRoom,
|
|
category: 'general',
|
|
action: Action.admin,
|
|
),
|
|
],
|
|
);
|
|
|
|
expect(state.isGlobalAdmin, isFalse);
|
|
});
|
|
|
|
test('returns false for empty roles list', () {
|
|
const state = AuthState(isAuthenticated: true, roles: []);
|
|
|
|
expect(state.isGlobalAdmin, isFalse);
|
|
});
|
|
});
|
|
|
|
group('copyWith', () {
|
|
test('creates a copy with modified values', () {
|
|
const original = AuthState(
|
|
isAuthenticated: true,
|
|
accessToken: 'old_token',
|
|
userName: 'Original User',
|
|
);
|
|
|
|
final updated = original.copyWith(
|
|
accessToken: 'new_token',
|
|
userName: 'Updated User',
|
|
);
|
|
|
|
// Original unchanged
|
|
expect(original.accessToken, equals('old_token'));
|
|
expect(original.userName, equals('Original User'));
|
|
|
|
// Updated has new values
|
|
expect(updated.accessToken, equals('new_token'));
|
|
expect(updated.userName, equals('Updated User'));
|
|
|
|
// Preserved unchanged values
|
|
expect(updated.isAuthenticated, equals(original.isAuthenticated));
|
|
});
|
|
});
|
|
|
|
group('equality', () {
|
|
test('two identical states are equal', () {
|
|
const state1 = AuthState(
|
|
isAuthenticated: true,
|
|
userId: 'user-123',
|
|
userName: 'Test User',
|
|
);
|
|
const state2 = AuthState(
|
|
isAuthenticated: true,
|
|
userId: 'user-123',
|
|
userName: 'Test User',
|
|
);
|
|
|
|
expect(state1, equals(state2));
|
|
});
|
|
|
|
test('different states are not equal', () {
|
|
const state1 = AuthState(
|
|
isAuthenticated: true,
|
|
userId: 'user-123',
|
|
);
|
|
const state2 = AuthState(
|
|
isAuthenticated: true,
|
|
userId: 'user-456',
|
|
);
|
|
|
|
expect(state1, isNot(equals(state2)));
|
|
});
|
|
});
|
|
});
|
|
}
|