MotorSignal-App/test/club_membership_presentatio...

313 lines
11 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:motorcycle_dash/features/social/clubs/club_membership_presentation.dart';
import 'package:motorcycle_dash/services/clubs/club_models.dart';
void main() {
group('ClubMembershipPresentation Unit Tests', () {
final baseClub = Club(
id: 'test_club_1',
name: 'NorCal Adventurers',
slug: 'norcal-adv',
description: 'Adventure riding club',
ownerUid: 'owner_uid',
memberCount: 10,
visibility: ClubVisibility.public,
admissionPolicy: ClubAdmissionPolicy.open,
createdAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
test('resolves unavailable when club is null', () {
final p = ClubMembershipPresentation.resolve(
club: null,
member: null,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.unavailable);
expect(p.statusBadge, 'UNAVAILABLE');
expect(p.isActionable, isFalse);
expect(p.primaryAction, isNull);
});
test('root owner cannot join even with a missing or stale member record', () {
for (final status in [null, ...ClubMemberStatus.values]) {
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.request),
member: status == null ? null : ClubMember(
uid: 'owner_uid',
clubId: baseClub.id,
role: ClubRole.member,
status: status,
updatedAt: DateTime(2026, 1, 1),
),
currentUserId: 'owner_uid',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.activeOwner);
expect(p.primaryAction, isNull);
expect(p.secondaryAction, isNull);
expect(p.canLeave, isFalse);
}
});
test('request admission actions respect active, pending and outsider states', () {
for (final role in ClubRole.values) {
for (final status in [null, ClubMemberStatus.active, ClubMemberStatus.requested]) {
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.request),
member: status == null ? null : ClubMember(
uid: 'rider_1',
clubId: baseClub.id,
role: role,
status: status,
updatedAt: DateTime(2026, 1, 1),
),
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.primaryAction, status == null
? ClubMembershipAction.requestToJoin
: status == ClubMemberStatus.requested
? ClubMembershipAction.withdrawRequest
: role == ClubRole.owner ? null : ClubMembershipAction.leaveClub);
}
}
});
test('resolves archived state when club.isArchived is true', () {
final archivedClub = baseClub.copyWith(archivedAt: DateTime(2026, 2, 1));
final p = ClubMembershipPresentation.resolve(
club: archivedClub,
member: null,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.archived);
expect(p.statusBadge, 'ARCHIVED');
expect(p.badgeType, PresentationBadgeType.amber);
expect(p.canLeave, isFalse);
expect(p.isActionable, isFalse);
expect(p.primaryAction, isNull);
});
test('resolves unauthenticated state when signed out or anonymous', () {
final p1 = ClubMembershipPresentation.resolve(
club: baseClub,
member: null,
currentUserId: null,
isAuthenticated: false,
);
expect(p1.state, ClubMembershipPresentationState.unauthenticated);
expect(p1.statusBadge, 'GUEST');
expect(p1.primaryAction, ClubMembershipAction.signIn);
expect(p1.isActionable, isTrue);
final p2 = ClubMembershipPresentation.resolve(
club: baseClub,
member: null,
currentUserId: '',
isAuthenticated: true,
);
expect(p2.state, ClubMembershipPresentationState.unauthenticated);
});
test('resolves outsiderOpen for open admission when unjoined', () {
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.open),
member: null,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.outsiderOpen);
expect(p.statusBadge, 'OPEN ADMISSION');
expect(p.badgeType, PresentationBadgeType.cyan);
expect(p.primaryAction, ClubMembershipAction.joinOpen);
expect(p.primaryActionLabel, 'Join Club');
expect(p.canLeave, isFalse);
});
test('resolves outsiderRequest for request admission when unjoined', () {
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.request),
member: null,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.outsiderRequest);
expect(p.statusBadge, 'REQUEST REQUIRED');
expect(p.primaryAction, ClubMembershipAction.requestToJoin);
expect(p.primaryActionLabel, 'Request to Join');
});
test('resolves outsiderInviteOnly for invite-only admission when unjoined', () {
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.inviteOnly),
member: null,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.outsiderInviteOnly);
expect(p.statusBadge, 'INVITE ONLY');
expect(p.primaryAction, isNull);
expect(p.isActionable, isFalse);
});
test('resolves historical states (left, declined, removed) as re-joinable outsiders', () {
for (final status in [ClubMemberStatus.left, ClubMemberStatus.declined, ClubMemberStatus.removed]) {
final member = ClubMember(
uid: 'rider_1',
clubId: baseClub.id,
status: status,
role: ClubRole.member,
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub.copyWith(admissionPolicy: ClubAdmissionPolicy.open),
member: member,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.outsiderOpen);
expect(p.primaryAction, ClubMembershipAction.joinOpen);
}
});
test('resolves invited state with accept and decline actions', () {
final invitedMember = ClubMember(
uid: 'rider_1',
clubId: baseClub.id,
status: ClubMemberStatus.invited,
role: ClubRole.member,
invitedByUid: 'owner_uid',
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub,
member: invitedMember,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.invited);
expect(p.statusBadge, 'INVITED');
expect(p.primaryAction, ClubMembershipAction.acceptInvite);
expect(p.primaryActionLabel, 'Accept Invitation');
expect(p.secondaryAction, ClubMembershipAction.declineInvite);
expect(p.secondaryActionLabel, 'Decline');
expect(p.isSecondaryDestructive, isTrue);
expect(p.canLeave, isFalse);
});
test('resolves requested state with withdraw request action', () {
final requestedMember = ClubMember(
uid: 'rider_1',
clubId: baseClub.id,
status: ClubMemberStatus.requested,
role: ClubRole.member,
requestedAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub,
member: requestedMember,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.requested);
expect(p.statusBadge, 'REQUEST PENDING');
expect(p.badgeType, PresentationBadgeType.amber);
expect(p.primaryAction, ClubMembershipAction.withdrawRequest);
expect(p.primaryActionLabel, 'Withdraw Request');
expect(p.isPrimaryDestructive, isTrue);
expect(p.canLeave, isFalse);
});
test('resolves activeMember state with leave club action', () {
final activeMember = ClubMember(
uid: 'rider_1',
clubId: baseClub.id,
status: ClubMemberStatus.active,
role: ClubRole.member,
joinedAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub,
member: activeMember,
currentUserId: 'rider_1',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.activeMember);
expect(p.statusBadge, 'MEMBER');
expect(p.primaryAction, ClubMembershipAction.leaveClub);
expect(p.primaryActionLabel, 'Leave Club');
expect(p.isPrimaryDestructive, isTrue);
expect(p.canLeave, isTrue);
});
test('resolves activeAdmin state with leave club action', () {
final activeAdmin = ClubMember(
uid: 'rider_admin',
clubId: baseClub.id,
status: ClubMemberStatus.active,
role: ClubRole.admin,
joinedAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub,
member: activeAdmin,
currentUserId: 'rider_admin',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.activeAdmin);
expect(p.statusBadge, 'ADMIN');
expect(p.primaryAction, ClubMembershipAction.leaveClub);
expect(p.canLeave, isTrue);
});
test('resolves activeOwner state where owner cannot leave', () {
final activeOwner = ClubMember(
uid: 'owner_uid',
clubId: baseClub.id,
status: ClubMemberStatus.active,
role: ClubRole.owner,
joinedAt: DateTime(2026, 1, 1),
updatedAt: DateTime(2026, 1, 1),
);
final p = ClubMembershipPresentation.resolve(
club: baseClub,
member: activeOwner,
currentUserId: 'owner_uid',
isAuthenticated: true,
);
expect(p.state, ClubMembershipPresentationState.activeOwner);
expect(p.statusBadge, 'OWNER');
expect(p.badgeType, PresentationBadgeType.green);
expect(p.primaryAction, isNull);
expect(p.canLeave, isFalse);
expect(p.isActionable, isFalse);
expect(p.statusDescription, contains('Ownership must be transferred'));
});
});
}