249 lines
9.7 KiB
Dart
249 lines
9.7 KiB
Dart
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<Timestamp>());
|
|
expect(fsMap['updatedAt'], isA<Timestamp>());
|
|
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));
|
|
});
|
|
});
|
|
}
|