import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:motorcycle_dash/services/clubs/club_models.dart'; void main() { group('Club Enums', () { test('ClubVisibility labels and parsing', () { expect(ClubVisibility.public.label, equals('Public')); expect(ClubVisibility.unlisted.label, equals('Unlisted')); expect(ClubVisibility.private.label, equals('Private')); expect(ClubVisibility.fromString('public'), equals(ClubVisibility.public)); expect(ClubVisibility.fromString('unlisted'), equals(ClubVisibility.unlisted)); expect(ClubVisibility.fromString('private'), equals(ClubVisibility.private)); expect(ClubVisibility.fromString('unknown'), equals(ClubVisibility.public)); expect(ClubVisibility.fromString(null), equals(ClubVisibility.public)); }); test('ClubAdmissionPolicy labels and parsing', () { expect(ClubAdmissionPolicy.open.label, equals('Open to Join')); expect(ClubAdmissionPolicy.request.label, equals('Request to Join')); expect(ClubAdmissionPolicy.inviteOnly.label, equals('Invite Only')); expect(ClubAdmissionPolicy.fromString('open'), equals(ClubAdmissionPolicy.open)); expect(ClubAdmissionPolicy.fromString('request'), equals(ClubAdmissionPolicy.request)); expect(ClubAdmissionPolicy.fromString('inviteOnly'), equals(ClubAdmissionPolicy.inviteOnly)); expect(ClubAdmissionPolicy.fromString('other'), equals(ClubAdmissionPolicy.request)); expect(ClubAdmissionPolicy.fromString(null), equals(ClubAdmissionPolicy.request)); }); test('ClubRole labels, rank hierarchy, and parsing', () { expect(ClubRole.owner.label, equals('Owner')); expect(ClubRole.admin.label, equals('Admin')); expect(ClubRole.member.label, equals('Member')); // Hierarchy expect(ClubRole.owner.isAtLeast(ClubRole.owner), isTrue); expect(ClubRole.owner.isAtLeast(ClubRole.admin), isTrue); expect(ClubRole.owner.isAtLeast(ClubRole.member), isTrue); expect(ClubRole.admin.isAtLeast(ClubRole.owner), isFalse); expect(ClubRole.admin.isAtLeast(ClubRole.admin), isTrue); expect(ClubRole.admin.isAtLeast(ClubRole.member), isTrue); expect(ClubRole.member.isAtLeast(ClubRole.owner), isFalse); expect(ClubRole.member.isAtLeast(ClubRole.admin), isFalse); expect(ClubRole.member.isAtLeast(ClubRole.member), isTrue); expect(ClubRole.fromString('owner'), equals(ClubRole.owner)); expect(ClubRole.fromString('admin'), equals(ClubRole.admin)); expect(ClubRole.fromString('member'), equals(ClubRole.member)); expect(ClubRole.fromString(null), equals(ClubRole.member)); }); test('ClubMemberStatus classification and parsing', () { expect(ClubMemberStatus.active.isActive, isTrue); expect(ClubMemberStatus.invited.isActive, isFalse); expect(ClubMemberStatus.requested.isActive, isFalse); expect(ClubMemberStatus.invited.isPending, isTrue); expect(ClubMemberStatus.requested.isPending, isTrue); expect(ClubMemberStatus.active.isPending, isFalse); expect(ClubMemberStatus.declined.isHistorical, isTrue); expect(ClubMemberStatus.left.isHistorical, isTrue); expect(ClubMemberStatus.removed.isHistorical, isTrue); expect(ClubMemberStatus.active.isHistorical, isFalse); expect(ClubMemberStatus.fromString('active'), equals(ClubMemberStatus.active)); expect(ClubMemberStatus.fromString('invited'), equals(ClubMemberStatus.invited)); expect(ClubMemberStatus.fromString('requested'), equals(ClubMemberStatus.requested)); expect(ClubMemberStatus.fromString('left'), equals(ClubMemberStatus.left)); expect(ClubMemberStatus.fromString('removed'), equals(ClubMemberStatus.removed)); expect(ClubMemberStatus.fromString('declined'), equals(ClubMemberStatus.declined)); expect(ClubMemberStatus.fromString(null), equals(ClubMemberStatus.declined)); }); }); group('Club Model', () { final now = DateTime(2026, 9, 6, 12, 0, 0); test('full serialization roundtrip (JSON and Firestore)', () { final club = Club( id: 'club_1', name: 'Bay Area Sport Riders', slug: 'bay-area-sport-riders', description: 'Twisties and canyon carving', visibility: ClubVisibility.public, admissionPolicy: ClubAdmissionPolicy.request, ownerUid: 'user_alice', memberCount: 24, createdAt: now, updatedAt: now, archivedAt: null, logoUrl: 'https://example.com/logo.png', bannerUrl: 'https://example.com/banner.png', homeRegion: 'San Francisco Bay Area', motorcycleFocus: 'Sport / Canyon', ); final json = club.toJson(); final fromJson = Club.fromJson(json); expect(fromJson.id, equals(club.id)); expect(fromJson.name, equals(club.name)); expect(fromJson.slug, equals(club.slug)); expect(fromJson.description, equals(club.description)); expect(fromJson.visibility, equals(club.visibility)); expect(fromJson.admissionPolicy, equals(club.admissionPolicy)); expect(fromJson.ownerUid, equals(club.ownerUid)); expect(fromJson.memberCount, equals(club.memberCount)); expect(fromJson.createdAt, equals(club.createdAt)); expect(fromJson.updatedAt, equals(club.updatedAt)); expect(fromJson.archivedAt, isNull); expect(fromJson.isArchived, isFalse); expect(fromJson.logoUrl, equals(club.logoUrl)); expect(fromJson.bannerUrl, equals(club.bannerUrl)); expect(fromJson.homeRegion, equals(club.homeRegion)); expect(fromJson.motorcycleFocus, equals(club.motorcycleFocus)); // Firestore map serialization final fsMap = club.toFirestore(); expect(fsMap['createdAt'], isA()); expect(fsMap['updatedAt'], isA()); expect(fsMap['archivedAt'], isNull); // Deserializing from map with Timestamps final fromFs = Club.fromJson(fsMap); expect(fromFs.name, equals(club.name)); expect(fromFs.createdAt, equals(club.createdAt)); }); test('copyWith handles updates and null clearing', () { final club = Club( id: 'club_1', name: 'Alpha Club', slug: 'alpha-club', ownerUid: 'alice', createdAt: now, updatedAt: now, archivedAt: now, ); expect(club.isArchived, isTrue); final unarchived = club.copyWith(clearArchivedAt: true); expect(unarchived.archivedAt, isNull); expect(unarchived.isArchived, isFalse); final updatedName = club.copyWith(name: 'Beta Club'); expect(updatedName.name, equals('Beta Club')); expect(updatedName.slug, equals('alpha-club')); }); }); group('ClubMember Model', () { final now = DateTime(2026, 9, 6, 12, 0, 0); test('lean serialization roundtrip without duplicated rider profile identity', () { final member = ClubMember( uid: 'user_bob', clubId: 'club_1', status: ClubMemberStatus.active, role: ClubRole.admin, displayRank: 'Road Captain', joinedAt: now, requestedAt: now.subtract(const Duration(days: 1)), invitedByUid: 'user_alice', updatedAt: now, ); expect(member.isActive, isTrue); expect(member.isAdmin, isTrue); expect(member.isOwner, isFalse); final json = member.toJson(); // Ensure no identity duplication expect(json.containsKey('riderHandleSnapshot'), isFalse); expect(json.containsKey('riderDisplayNameSnapshot'), isFalse); expect(json.containsKey('riderAvatarPreset'), isFalse); expect(json.containsKey('riderAvatarUrl'), isFalse); final fromJson = ClubMember.fromJson(json); expect(fromJson.uid, equals(member.uid)); expect(fromJson.clubId, equals(member.clubId)); expect(fromJson.status, equals(member.status)); expect(fromJson.role, equals(member.role)); expect(fromJson.displayRank, equals('Road Captain')); expect(fromJson.invitedByUid, equals('user_alice')); expect(fromJson.joinedAt, equals(member.joinedAt)); }); test('copyWith handles displayRank clearing', () { final member = ClubMember( uid: 'user_bob', clubId: 'club_1', status: ClubMemberStatus.active, role: ClubRole.member, displayRank: 'Tail Gunner', updatedAt: now, ); final cleared = member.copyWith(clearDisplayRank: true); expect(cleared.displayRank, isNull); }); }); group('ClubBan Model', () { final now = DateTime(2026, 9, 6, 12, 0, 0); test('ban document serialization preserves reason', () { final ban = ClubBan( uid: 'troublemaker', clubId: 'club_1', bannedByUid: 'alice', bannedAt: now, reason: 'Violating group ride safety rules', ); final json = ban.toJson(); final fromJson = ClubBan.fromJson(json); expect(fromJson.uid, equals('troublemaker')); expect(fromJson.bannedByUid, equals('alice')); expect(fromJson.bannedAt, equals(now)); expect(fromJson.reason, equals('Violating group ride safety rules')); }); }); group('ClubHandle Model', () { final now = DateTime(2026, 9, 6, 12, 0, 0); test('handle reservation document serialization', () { final handle = ClubHandle( slug: 'norcal-adv', clubId: 'club_norcal', ownerUid: 'alice', createdAt: now, ); final json = handle.toJson(); final fromJson = ClubHandle.fromJson(json); expect(fromJson.slug, equals('norcal-adv')); expect(fromJson.clubId, equals('club_norcal')); expect(fromJson.ownerUid, equals('alice')); expect(fromJson.createdAt, equals(now)); }); }); }