Refactored method, added tests for DTS
This commit is contained in:
@@ -68,7 +68,10 @@ class Match {
|
||||
'gameId': game.id,
|
||||
'groupId': group?.id,
|
||||
'playerIds': players.map((player) => player.id).toList(),
|
||||
'scores': scores,
|
||||
'scores': scores.map(
|
||||
(playerId, scoreList) =>
|
||||
MapEntry(playerId, scoreList.map((score) => score.toJson()).toList()),
|
||||
),
|
||||
'notes': notes,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,6 +70,20 @@ class DataTransferService {
|
||||
'gameId': m.game.id,
|
||||
'groupId': m.group?.id,
|
||||
'playerIds': m.players.map((p) => p.id).toList(),
|
||||
'scores': m.scores.map(
|
||||
(playerId, scores) => MapEntry(
|
||||
playerId,
|
||||
scores
|
||||
.map(
|
||||
(s) => {
|
||||
'roundNumber': s.roundNumber,
|
||||
'score': s.score,
|
||||
'change': s.change,
|
||||
},
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
'notes': m.notes,
|
||||
},
|
||||
)
|
||||
@@ -127,126 +141,9 @@ class DataTransferService {
|
||||
final isValid = await _validateJsonSchema(jsonString);
|
||||
if (!isValid) return ImportResult.invalidSchema;
|
||||
|
||||
final Map<String, dynamic> decoded =
|
||||
json.decode(jsonString) as Map<String, dynamic>;
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
|
||||
final List<dynamic> playersJson =
|
||||
(decoded['players'] as List<dynamic>?) ?? [];
|
||||
final List<dynamic> gamesJson =
|
||||
(decoded['games'] as List<dynamic>?) ?? [];
|
||||
final List<dynamic> groupsJson =
|
||||
(decoded['groups'] as List<dynamic>?) ?? [];
|
||||
final List<dynamic> teamsJson =
|
||||
(decoded['teams'] as List<dynamic>?) ?? [];
|
||||
final List<dynamic> matchesJson =
|
||||
(decoded['matches'] as List<dynamic>?) ?? [];
|
||||
|
||||
// Import Players
|
||||
final List<Player> importedPlayers = playersJson
|
||||
.map((p) => Player.fromJson(p as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
final Map<String, Player> playerById = {
|
||||
for (final p in importedPlayers) p.id: p,
|
||||
};
|
||||
|
||||
// Import Games
|
||||
final List<Game> importedGames = gamesJson
|
||||
.map((g) => Game.fromJson(g as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
final Map<String, Game> gameById = {
|
||||
for (final g in importedGames) g.id: g,
|
||||
};
|
||||
|
||||
// Import Groups
|
||||
final List<Group> importedGroups = groupsJson.map((g) {
|
||||
final map = g as Map<String, dynamic>;
|
||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||
.cast<String>();
|
||||
|
||||
final members = memberIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Group(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
description: map['description'] as String,
|
||||
members: members,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
final Map<String, Group> groupById = {
|
||||
for (final g in importedGroups) g.id: g,
|
||||
};
|
||||
|
||||
// Import Teams
|
||||
final List<Team> importedTeams = teamsJson.map((t) {
|
||||
final map = t as Map<String, dynamic>;
|
||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||
.cast<String>();
|
||||
|
||||
final members = memberIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Team(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
members: members,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// Import Matches
|
||||
final List<Match> importedMatches = matchesJson.map((m) {
|
||||
final map = m as Map<String, dynamic>;
|
||||
|
||||
final String gameId = map['gameId'] as String;
|
||||
final String? groupId = map['groupId'] as String?;
|
||||
final List<String> playerIds =
|
||||
(map['playerIds'] as List<dynamic>? ?? []).cast<String>();
|
||||
final DateTime? endedAt = map['endedAt'] != null
|
||||
? DateTime.parse(map['endedAt'] as String)
|
||||
: null;
|
||||
|
||||
final game = gameById[gameId];
|
||||
final group = (groupId == null) ? null : groupById[groupId];
|
||||
final players = playerIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Match(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
game:
|
||||
game ??
|
||||
Game(
|
||||
name: 'Unknown',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
description: '',
|
||||
color: GameColor.blue,
|
||||
icon: '',
|
||||
),
|
||||
group: group,
|
||||
players: players,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
endedAt: endedAt,
|
||||
notes: map['notes'] as String? ?? '',
|
||||
);
|
||||
}).toList();
|
||||
|
||||
// Import all data into the database
|
||||
await db.playerDao.addPlayersAsList(players: importedPlayers);
|
||||
await db.gameDao.addGamesAsList(games: importedGames);
|
||||
await db.groupDao.addGroupsAsList(groups: importedGroups);
|
||||
await db.teamDao.addTeamsAsList(teams: importedTeams);
|
||||
await db.matchDao.addMatchAsList(matches: importedMatches);
|
||||
await importDataToDatabase(db, decoded);
|
||||
|
||||
return ImportResult.success;
|
||||
} on FormatException catch (e, stack) {
|
||||
@@ -262,6 +159,160 @@ class DataTransferService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Imports parsed JSON data into the database.
|
||||
@visibleForTesting
|
||||
static Future<void> importDataToDatabase(
|
||||
AppDatabase db,
|
||||
Map<String, dynamic> decoded,
|
||||
) async {
|
||||
final importedPlayers = parsePlayersFromJson(decoded);
|
||||
final playerById = {for (final p in importedPlayers) p.id: p};
|
||||
|
||||
final importedGames = parseGamesFromJson(decoded);
|
||||
final gameById = {for (final g in importedGames) g.id: g};
|
||||
|
||||
final importedGroups = parseGroupsFromJson(decoded, playerById);
|
||||
final groupById = {for (final g in importedGroups) g.id: g};
|
||||
|
||||
final importedTeams = parseTeamsFromJson(decoded, playerById);
|
||||
|
||||
final importedMatches = parseMatchesFromJson(
|
||||
decoded,
|
||||
gameById,
|
||||
groupById,
|
||||
playerById,
|
||||
);
|
||||
|
||||
await db.playerDao.addPlayersAsList(players: importedPlayers);
|
||||
await db.gameDao.addGamesAsList(games: importedGames);
|
||||
await db.groupDao.addGroupsAsList(groups: importedGroups);
|
||||
await db.teamDao.addTeamsAsList(teams: importedTeams);
|
||||
await db.matchDao.addMatchAsList(matches: importedMatches);
|
||||
}
|
||||
|
||||
/// Parses players from JSON data.
|
||||
@visibleForTesting
|
||||
static List<Player> parsePlayersFromJson(Map<String, dynamic> decoded) {
|
||||
final playersJson = (decoded['players'] as List<dynamic>?) ?? [];
|
||||
return playersJson
|
||||
.map((p) => Player.fromJson(p as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Parses games from JSON data.
|
||||
@visibleForTesting
|
||||
static List<Game> parseGamesFromJson(Map<String, dynamic> decoded) {
|
||||
final gamesJson = (decoded['games'] as List<dynamic>?) ?? [];
|
||||
return gamesJson
|
||||
.map((g) => Game.fromJson(g as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Parses groups from JSON data.
|
||||
@visibleForTesting
|
||||
static List<Group> parseGroupsFromJson(
|
||||
Map<String, dynamic> decoded,
|
||||
Map<String, Player> playerById,
|
||||
) {
|
||||
final groupsJson = (decoded['groups'] as List<dynamic>?) ?? [];
|
||||
return groupsJson.map((g) {
|
||||
final map = g as Map<String, dynamic>;
|
||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||
.cast<String>();
|
||||
|
||||
final members = memberIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Group(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
description: map['description'] as String,
|
||||
members: members,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Parses teams from JSON data.
|
||||
@visibleForTesting
|
||||
static List<Team> parseTeamsFromJson(
|
||||
Map<String, dynamic> decoded,
|
||||
Map<String, Player> playerById,
|
||||
) {
|
||||
final teamsJson = (decoded['teams'] as List<dynamic>?) ?? [];
|
||||
return teamsJson.map((t) {
|
||||
final map = t as Map<String, dynamic>;
|
||||
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
|
||||
.cast<String>();
|
||||
|
||||
final members = memberIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Team(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
members: members,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Parses matches from JSON data.
|
||||
@visibleForTesting
|
||||
static List<Match> parseMatchesFromJson(
|
||||
Map<String, dynamic> decoded,
|
||||
Map<String, Game> gameById,
|
||||
Map<String, Group> groupById,
|
||||
Map<String, Player> playerById,
|
||||
) {
|
||||
final matchesJson = (decoded['matches'] as List<dynamic>?) ?? [];
|
||||
return matchesJson.map((m) {
|
||||
final map = m as Map<String, dynamic>;
|
||||
|
||||
final gameId = map['gameId'] as String;
|
||||
final groupId = map['groupId'] as String?;
|
||||
final playerIds = (map['playerIds'] as List<dynamic>? ?? [])
|
||||
.cast<String>();
|
||||
final endedAt = map['endedAt'] != null
|
||||
? DateTime.parse(map['endedAt'] as String)
|
||||
: null;
|
||||
|
||||
final game = gameById[gameId] ?? createUnknownGame();
|
||||
final group = groupId != null ? groupById[groupId] : null;
|
||||
final players = playerIds
|
||||
.map((id) => playerById[id])
|
||||
.whereType<Player>()
|
||||
.toList();
|
||||
|
||||
return Match(
|
||||
id: map['id'] as String,
|
||||
name: map['name'] as String,
|
||||
game: game,
|
||||
group: group,
|
||||
players: players,
|
||||
createdAt: DateTime.parse(map['createdAt'] as String),
|
||||
endedAt: endedAt,
|
||||
notes: map['notes'] as String? ?? '',
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Creates a fallback game when the referenced game is not found.
|
||||
@visibleForTesting
|
||||
static Game createUnknownGame() {
|
||||
return Game(
|
||||
name: 'Unknown',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
description: '',
|
||||
color: GameColor.blue,
|
||||
icon: '',
|
||||
);
|
||||
}
|
||||
|
||||
/// Helper method to read file content from either bytes or path
|
||||
static Future<String?> _readFileContent(PlatformFile file) async {
|
||||
if (file.bytes != null) return utf8.decode(file.bytes!);
|
||||
|
||||
802
test/services/data_transfer_service_test.dart
Normal file
802
test/services/data_transfer_service_test.dart
Normal file
@@ -0,0 +1,802 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:drift/drift.dart' hide isNull, isNotNull;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
import 'package:tallee/data/db/database.dart';
|
||||
import 'package:tallee/data/models/game.dart';
|
||||
import 'package:tallee/data/models/group.dart';
|
||||
import 'package:tallee/data/models/match.dart';
|
||||
import 'package:tallee/data/models/player.dart';
|
||||
import 'package:tallee/data/models/score.dart';
|
||||
import 'package:tallee/data/models/team.dart';
|
||||
import 'package:tallee/services/data_transfer_service.dart';
|
||||
|
||||
void main() {
|
||||
late AppDatabase database;
|
||||
late Player testPlayer1;
|
||||
late Player testPlayer2;
|
||||
late Player testPlayer3;
|
||||
late Game testGame;
|
||||
late Group testGroup;
|
||||
late Team testTeam;
|
||||
late Match testMatch;
|
||||
final fixedDate = DateTime(2025, 11, 19, 0, 11, 23);
|
||||
final fakeClock = Clock(() => fixedDate);
|
||||
|
||||
setUp(() {
|
||||
database = AppDatabase(
|
||||
DatabaseConnection(
|
||||
NativeDatabase.memory(),
|
||||
closeStreamsSynchronously: true,
|
||||
),
|
||||
);
|
||||
|
||||
withClock(fakeClock, () {
|
||||
testPlayer1 = Player(name: 'Alice', description: 'First test player');
|
||||
testPlayer2 = Player(name: 'Bob', description: 'Second test player');
|
||||
testPlayer3 = Player(name: 'Charlie', description: 'Third player');
|
||||
|
||||
testGame = Game(
|
||||
name: 'Chess',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
description: 'Strategic board game',
|
||||
color: GameColor.blue,
|
||||
icon: 'chess_icon',
|
||||
);
|
||||
|
||||
testGroup = Group(
|
||||
name: 'Test Group',
|
||||
description: 'Group for testing',
|
||||
members: [testPlayer1, testPlayer2],
|
||||
);
|
||||
|
||||
testTeam = Team(name: 'Test Team', members: [testPlayer1, testPlayer2]);
|
||||
|
||||
testMatch = Match(
|
||||
name: 'Test Match',
|
||||
game: testGame,
|
||||
group: testGroup,
|
||||
players: [testPlayer1, testPlayer2],
|
||||
notes: 'Test notes',
|
||||
scores: {
|
||||
testPlayer1.id: [
|
||||
Score(roundNumber: 1, score: 10, change: 10),
|
||||
Score(roundNumber: 2, score: 20, change: 10),
|
||||
],
|
||||
testPlayer2.id: [
|
||||
Score(roundNumber: 1, score: 15, change: 15),
|
||||
Score(roundNumber: 2, score: 25, change: 10),
|
||||
],
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await database.close();
|
||||
});
|
||||
|
||||
// Helper for getting BuildContext
|
||||
Future<BuildContext> getContext(WidgetTester tester) async {
|
||||
// Minimal widget with Provider
|
||||
await tester.pumpWidget(
|
||||
Provider<AppDatabase>.value(
|
||||
value: database,
|
||||
child: MaterialApp(
|
||||
home: Builder(
|
||||
builder: (context) {
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final BuildContext context = tester.element(find.byType(Container));
|
||||
return context;
|
||||
}
|
||||
|
||||
group('DataTransferService Tests', () {
|
||||
testWidgets('deleteAllData()', (tester) async {
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.groupDao.addGroup(group: testGroup);
|
||||
await database.teamDao.addTeam(team: testTeam);
|
||||
await database.matchDao.addMatch(match: testMatch);
|
||||
|
||||
var playerCount = await database.playerDao.getPlayerCount();
|
||||
var gameCount = await database.gameDao.getGameCount();
|
||||
var groupCount = await database.groupDao.getGroupCount();
|
||||
var teamCount = await database.teamDao.getTeamCount();
|
||||
var matchCount = await database.matchDao.getMatchCount();
|
||||
|
||||
expect(playerCount, greaterThan(0));
|
||||
expect(gameCount, greaterThan(0));
|
||||
expect(groupCount, greaterThan(0));
|
||||
expect(teamCount, greaterThan(0));
|
||||
expect(matchCount, greaterThan(0));
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
await DataTransferService.deleteAllData(ctx);
|
||||
|
||||
playerCount = await database.playerDao.getPlayerCount();
|
||||
gameCount = await database.gameDao.getGameCount();
|
||||
groupCount = await database.groupDao.getGroupCount();
|
||||
teamCount = await database.teamDao.getTeamCount();
|
||||
matchCount = await database.matchDao.getMatchCount();
|
||||
|
||||
expect(playerCount, 0);
|
||||
expect(gameCount, 0);
|
||||
expect(groupCount, 0);
|
||||
expect(teamCount, 0);
|
||||
expect(matchCount, 0);
|
||||
});
|
||||
|
||||
group('getAppDataAsJson()', () {
|
||||
group('Whole export', () {
|
||||
testWidgets('Exporting app data works correctly', (tester) async {
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.playerDao.addPlayer(player: testPlayer2);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.groupDao.addGroup(group: testGroup);
|
||||
await database.teamDao.addTeam(team: testTeam);
|
||||
await database.matchDao.addMatch(match: testMatch);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
|
||||
expect(jsonString, isNotEmpty);
|
||||
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
|
||||
expect(decoded.containsKey('players'), true);
|
||||
expect(decoded.containsKey('games'), true);
|
||||
expect(decoded.containsKey('groups'), true);
|
||||
expect(decoded.containsKey('teams'), true);
|
||||
expect(decoded.containsKey('matches'), true);
|
||||
|
||||
final players = decoded['players'] as List<dynamic>;
|
||||
final games = decoded['games'] as List<dynamic>;
|
||||
final groups = decoded['groups'] as List<dynamic>;
|
||||
final teams = decoded['teams'] as List<dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
|
||||
expect(players.length, 2);
|
||||
expect(games.length, 1);
|
||||
expect(groups.length, 1);
|
||||
expect(teams.length, 1);
|
||||
expect(matches.length, 1);
|
||||
});
|
||||
|
||||
testWidgets('Exporting empty data works correctly', (tester) async {
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
|
||||
final players = decoded['players'] as List<dynamic>;
|
||||
final games = decoded['games'] as List<dynamic>;
|
||||
final groups = decoded['groups'] as List<dynamic>;
|
||||
final teams = decoded['teams'] as List<dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
|
||||
expect(players, isEmpty);
|
||||
expect(games, isEmpty);
|
||||
expect(groups, isEmpty);
|
||||
expect(teams, isEmpty);
|
||||
expect(matches, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('Specific data', () {
|
||||
testWidgets('Player data is correct', (tester) async {
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final players = decoded['players'] as List<dynamic>;
|
||||
final playerData = players[0] as Map<String, dynamic>;
|
||||
|
||||
expect(playerData['id'], testPlayer1.id);
|
||||
expect(playerData['name'], testPlayer1.name);
|
||||
expect(playerData['description'], testPlayer1.description);
|
||||
expect(
|
||||
playerData['createdAt'],
|
||||
testPlayer1.createdAt.toIso8601String(),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Game data is correct', (tester) async {
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final games = decoded['games'] as List<dynamic>;
|
||||
final gameData = games[0] as Map<String, dynamic>;
|
||||
|
||||
expect(gameData['id'], testGame.id);
|
||||
expect(gameData['name'], testGame.name);
|
||||
expect(gameData['ruleset'], testGame.ruleset.name);
|
||||
expect(gameData['description'], testGame.description);
|
||||
expect(gameData['color'], testGame.color.name);
|
||||
expect(gameData['icon'], testGame.icon);
|
||||
});
|
||||
|
||||
testWidgets('Group data is correct', (tester) async {
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.playerDao.addPlayer(player: testPlayer2);
|
||||
await database.groupDao.addGroup(group: testGroup);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final groups = decoded['groups'] as List<dynamic>;
|
||||
final groupData = groups[0] as Map<String, dynamic>;
|
||||
|
||||
expect(groupData['id'], testGroup.id);
|
||||
expect(groupData['name'], testGroup.name);
|
||||
expect(groupData['description'], testGroup.description);
|
||||
expect(groupData['memberIds'], isA<List>());
|
||||
|
||||
final memberIds = groupData['memberIds'] as List<dynamic>;
|
||||
expect(memberIds.length, 2);
|
||||
expect(memberIds, containsAll([testPlayer1.id, testPlayer2.id]));
|
||||
});
|
||||
|
||||
testWidgets('Team data is correct', (tester) async {
|
||||
await database.teamDao.addTeam(team: testTeam);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final teams = decoded['teams'] as List<dynamic>;
|
||||
|
||||
expect(teams.length, 1);
|
||||
|
||||
final teamData = teams[0] as Map<String, dynamic>;
|
||||
|
||||
expect(teamData['id'], testTeam.id);
|
||||
expect(teamData['name'], testTeam.name);
|
||||
expect(teamData['memberIds'], isA<List>());
|
||||
|
||||
// Note: In this system, teams don't have independent members.
|
||||
// Team members are only tracked through matches via PlayerMatchTable.
|
||||
// Therefore, memberIds will be empty for standalone teams.
|
||||
final memberIds = teamData['memberIds'] as List<dynamic>;
|
||||
expect(memberIds, isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('Match data is correct', (tester) async {
|
||||
await database.playerDao.addPlayersAsList(
|
||||
players: [testPlayer1, testPlayer2],
|
||||
);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.groupDao.addGroup(group: testGroup);
|
||||
await database.matchDao.addMatch(match: testMatch);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
final matchData = matches[0] as Map<String, dynamic>;
|
||||
|
||||
expect(matchData['id'], testMatch.id);
|
||||
expect(matchData['name'], testMatch.name);
|
||||
expect(matchData['gameId'], testGame.id);
|
||||
expect(matchData['groupId'], testGroup.id);
|
||||
expect(matchData['playerIds'], isA<List>());
|
||||
expect(matchData['notes'], testMatch.notes);
|
||||
|
||||
// Check player ids
|
||||
final playerIds = matchData['playerIds'] as List<String>;
|
||||
expect(playerIds.length, 2);
|
||||
expect(playerIds, containsAll([testPlayer1.id, testPlayer2.id]));
|
||||
|
||||
// Check scores structure
|
||||
final scoresJson = matchData['scores'] as Map<String, dynamic>;
|
||||
expect(scoresJson, isA<Map<String, dynamic>>());
|
||||
|
||||
final scores = scoresJson.map(
|
||||
(playerId, scoreList) => MapEntry(
|
||||
playerId,
|
||||
(scoreList as List)
|
||||
.map((s) => Score.fromJson(s as Map<String, dynamic>))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(scores, isA<Map<String, List<Score>>>());
|
||||
|
||||
/* Player 1 scores */
|
||||
// General structure
|
||||
expect(scores[testPlayer1.id], isNotNull);
|
||||
expect(scores[testPlayer1.id]!.length, 2);
|
||||
|
||||
// Round 1
|
||||
expect(scores[testPlayer1.id]![0].roundNumber, 1);
|
||||
expect(scores[testPlayer1.id]![0].score, 10);
|
||||
expect(scores[testPlayer1.id]![0].change, 10);
|
||||
|
||||
// Round 2
|
||||
expect(scores[testPlayer1.id]![1].roundNumber, 2);
|
||||
expect(scores[testPlayer1.id]![1].score, 20);
|
||||
expect(scores[testPlayer1.id]![1].change, 10);
|
||||
|
||||
/* Player 2 scores */
|
||||
// General structure
|
||||
expect(scores[testPlayer2.id], isNotNull);
|
||||
expect(scores[testPlayer2.id]!.length, 2);
|
||||
|
||||
// Round 1
|
||||
expect(scores[testPlayer2.id]![0].roundNumber, 1);
|
||||
expect(scores[testPlayer2.id]![0].score, 15);
|
||||
expect(scores[testPlayer2.id]![0].change, 15);
|
||||
|
||||
// Round 2
|
||||
expect(scores[testPlayer2.id]![1].roundNumber, 2);
|
||||
expect(scores[testPlayer2.id]![1].score, 25);
|
||||
expect(scores[testPlayer2.id]![1].change, 10);
|
||||
});
|
||||
|
||||
testWidgets('Match without group is handled correctly', (tester) async {
|
||||
final matchWithoutGroup = Match(
|
||||
name: 'No Group Match',
|
||||
game: testGame,
|
||||
group: null,
|
||||
players: [testPlayer1],
|
||||
notes: 'No group',
|
||||
);
|
||||
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.matchDao.addMatch(match: matchWithoutGroup);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
final matchData = matches[0] as Map<String, dynamic>;
|
||||
|
||||
expect(matchData['groupId'], isNull);
|
||||
});
|
||||
|
||||
testWidgets('Match with endedAt is handled correctly', (tester) async {
|
||||
final endedDate = DateTime(2025, 12, 1, 10, 0, 0);
|
||||
final endedMatch = Match(
|
||||
name: 'Ended Match',
|
||||
game: testGame,
|
||||
players: [testPlayer1],
|
||||
endedAt: endedDate,
|
||||
notes: 'Finished',
|
||||
);
|
||||
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.matchDao.addMatch(match: endedMatch);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
final matchData = matches[0] as Map<String, dynamic>;
|
||||
|
||||
expect(matchData['endedAt'], endedDate.toIso8601String());
|
||||
});
|
||||
|
||||
testWidgets('Structure is consistent', (tester) async {
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString1 = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final jsonString2 = await DataTransferService.getAppDataAsJson(ctx);
|
||||
|
||||
expect(jsonString1, equals(jsonString2));
|
||||
});
|
||||
|
||||
testWidgets('Empty match notes is handled correctly', (tester) async {
|
||||
final matchWithEmptyNotes = Match(
|
||||
name: 'Empty Notes Match',
|
||||
game: testGame,
|
||||
players: [testPlayer1],
|
||||
notes: '',
|
||||
);
|
||||
|
||||
await database.playerDao.addPlayer(player: testPlayer1);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.matchDao.addMatch(match: matchWithEmptyNotes);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
final matchData = matches[0] as Map<String, dynamic>;
|
||||
|
||||
expect(matchData['notes'], '');
|
||||
});
|
||||
|
||||
testWidgets('Multiple players in match is handled correctly', (
|
||||
tester,
|
||||
) async {
|
||||
final multiPlayerMatch = Match(
|
||||
name: 'Multi Player Match',
|
||||
game: testGame,
|
||||
players: [testPlayer1, testPlayer2, testPlayer3],
|
||||
notes: 'Three players',
|
||||
);
|
||||
|
||||
await database.playerDao.addPlayersAsList(
|
||||
players: [testPlayer1, testPlayer2, testPlayer3],
|
||||
);
|
||||
await database.gameDao.addGame(game: testGame);
|
||||
await database.matchDao.addMatch(match: multiPlayerMatch);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final matches = decoded['matches'] as List<dynamic>;
|
||||
final matchData = matches[0] as Map<String, dynamic>;
|
||||
|
||||
final playerIds = matchData['playerIds'] as List<dynamic>;
|
||||
expect(playerIds.length, 3);
|
||||
expect(
|
||||
playerIds,
|
||||
containsAll([testPlayer1.id, testPlayer2.id, testPlayer3.id]),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('All game colors are handled correctly', (tester) async {
|
||||
final games = [
|
||||
Game(
|
||||
name: 'Red Game',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
color: GameColor.red,
|
||||
icon: 'icon',
|
||||
),
|
||||
Game(
|
||||
name: 'Blue Game',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
color: GameColor.blue,
|
||||
icon: 'icon',
|
||||
),
|
||||
Game(
|
||||
name: 'Green Game',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
color: GameColor.green,
|
||||
icon: 'icon',
|
||||
),
|
||||
];
|
||||
|
||||
await database.gameDao.addGamesAsList(games: games);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final gamesJson = decoded['games'] as List<dynamic>;
|
||||
|
||||
expect(gamesJson.length, 3);
|
||||
expect(
|
||||
gamesJson.map((g) => g['color']),
|
||||
containsAll(['red', 'blue', 'green']),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('All rulesets are handled correctly', (tester) async {
|
||||
final games = [
|
||||
Game(
|
||||
name: 'Highest Score Game',
|
||||
ruleset: Ruleset.highestScore,
|
||||
color: GameColor.blue,
|
||||
icon: 'icon',
|
||||
),
|
||||
Game(
|
||||
name: 'Lowest Score Game',
|
||||
ruleset: Ruleset.lowestScore,
|
||||
color: GameColor.blue,
|
||||
icon: 'icon',
|
||||
),
|
||||
Game(
|
||||
name: 'Single Winner',
|
||||
ruleset: Ruleset.singleWinner,
|
||||
color: GameColor.blue,
|
||||
icon: 'icon',
|
||||
),
|
||||
];
|
||||
|
||||
await database.gameDao.addGamesAsList(games: games);
|
||||
|
||||
final ctx = await getContext(tester);
|
||||
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
|
||||
final decoded = json.decode(jsonString) as Map<String, dynamic>;
|
||||
final gamesJson = decoded['games'] as List<dynamic>;
|
||||
|
||||
expect(gamesJson.length, 3);
|
||||
expect(
|
||||
gamesJson.map((g) => g['ruleset']),
|
||||
containsAll(['highestScore', 'lowestScore', 'singleWinner']),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('Parse Methods', () {
|
||||
test('parsePlayersFromJson()', () {
|
||||
final jsonMap = {
|
||||
'players': [
|
||||
{
|
||||
'id': testPlayer1.id,
|
||||
'name': testPlayer1.name,
|
||||
'description': testPlayer1.description,
|
||||
'createdAt': testPlayer1.createdAt.toIso8601String(),
|
||||
},
|
||||
{
|
||||
'id': testPlayer2.id,
|
||||
'name': testPlayer2.name,
|
||||
'description': testPlayer2.description,
|
||||
'createdAt': testPlayer2.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final players = DataTransferService.parsePlayersFromJson(jsonMap);
|
||||
|
||||
expect(players.length, 2);
|
||||
expect(players[0].id, testPlayer1.id);
|
||||
expect(players[0].name, testPlayer1.name);
|
||||
expect(players[1].id, testPlayer2.id);
|
||||
expect(players[1].name, testPlayer2.name);
|
||||
});
|
||||
|
||||
test('parsePlayersFromJson() empty list', () {
|
||||
final jsonMap = {'players': []};
|
||||
final players = DataTransferService.parsePlayersFromJson(jsonMap);
|
||||
expect(players, isEmpty);
|
||||
});
|
||||
|
||||
test('parsePlayersFromJson() missing key', () {
|
||||
final jsonMap = <String, dynamic>{};
|
||||
final players = DataTransferService.parsePlayersFromJson(jsonMap);
|
||||
expect(players, isEmpty);
|
||||
});
|
||||
|
||||
test('parseGamesFromJson()', () {
|
||||
final jsonMap = {
|
||||
'games': [
|
||||
{
|
||||
'id': testGame.id,
|
||||
'name': testGame.name,
|
||||
'ruleset': testGame.ruleset.name,
|
||||
'description': testGame.description,
|
||||
'color': testGame.color.name,
|
||||
'icon': testGame.icon,
|
||||
'createdAt': testGame.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final games = DataTransferService.parseGamesFromJson(jsonMap);
|
||||
|
||||
expect(games.length, 1);
|
||||
expect(games[0].id, testGame.id);
|
||||
expect(games[0].name, testGame.name);
|
||||
expect(games[0].ruleset, testGame.ruleset);
|
||||
});
|
||||
|
||||
test('parseGroupsFromJson()', () {
|
||||
final playerById = {
|
||||
testPlayer1.id: testPlayer1,
|
||||
testPlayer2.id: testPlayer2,
|
||||
};
|
||||
|
||||
final jsonMap = {
|
||||
'groups': [
|
||||
{
|
||||
'id': testGroup.id,
|
||||
'name': testGroup.name,
|
||||
'description': testGroup.description,
|
||||
'memberIds': [testPlayer1.id, testPlayer2.id],
|
||||
'createdAt': testGroup.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final groups = DataTransferService.parseGroupsFromJson(
|
||||
jsonMap,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(groups.length, 1);
|
||||
expect(groups[0].id, testGroup.id);
|
||||
expect(groups[0].name, testGroup.name);
|
||||
expect(groups[0].members.length, 2);
|
||||
expect(groups[0].members[0].id, testPlayer1.id);
|
||||
expect(groups[0].members[1].id, testPlayer2.id);
|
||||
});
|
||||
|
||||
test('parseGroupsFromJson() ignores invalid player ids', () {
|
||||
final playerById = {testPlayer1.id: testPlayer1};
|
||||
|
||||
final jsonMap = {
|
||||
'groups': [
|
||||
{
|
||||
'id': testGroup.id,
|
||||
'name': testGroup.name,
|
||||
'description': testGroup.description,
|
||||
'memberIds': [testPlayer1.id, 'invalid-id'],
|
||||
'createdAt': testGroup.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final groups = DataTransferService.parseGroupsFromJson(
|
||||
jsonMap,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(groups.length, 1);
|
||||
expect(groups[0].members.length, 1);
|
||||
expect(groups[0].members[0].id, testPlayer1.id);
|
||||
});
|
||||
|
||||
test('parseTeamsFromJson()', () {
|
||||
final playerById = {testPlayer1.id: testPlayer1};
|
||||
|
||||
final jsonMap = {
|
||||
'teams': [
|
||||
{
|
||||
'id': testTeam.id,
|
||||
'name': testTeam.name,
|
||||
'memberIds': [testPlayer1.id],
|
||||
'createdAt': testTeam.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final teams = DataTransferService.parseTeamsFromJson(
|
||||
jsonMap,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(teams.length, 1);
|
||||
expect(teams[0].id, testTeam.id);
|
||||
expect(teams[0].name, testTeam.name);
|
||||
expect(teams[0].members.length, 1);
|
||||
expect(teams[0].members[0].id, testPlayer1.id);
|
||||
});
|
||||
|
||||
test('parseMatchesFromJson()', () {
|
||||
final playerById = {
|
||||
testPlayer1.id: testPlayer1,
|
||||
testPlayer2.id: testPlayer2,
|
||||
};
|
||||
final gameById = {testGame.id: testGame};
|
||||
final groupById = {testGroup.id: testGroup};
|
||||
|
||||
final jsonMap = {
|
||||
'matches': [
|
||||
{
|
||||
'id': testMatch.id,
|
||||
'name': testMatch.name,
|
||||
'gameId': testGame.id,
|
||||
'groupId': testGroup.id,
|
||||
'playerIds': [testPlayer1.id, testPlayer2.id],
|
||||
'notes': testMatch.notes,
|
||||
'createdAt': testMatch.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final matches = DataTransferService.parseMatchesFromJson(
|
||||
jsonMap,
|
||||
gameById,
|
||||
groupById,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(matches.length, 1);
|
||||
expect(matches[0].id, testMatch.id);
|
||||
expect(matches[0].name, testMatch.name);
|
||||
expect(matches[0].game.id, testGame.id);
|
||||
expect(matches[0].group?.id, testGroup.id);
|
||||
expect(matches[0].players.length, 2);
|
||||
});
|
||||
|
||||
test('parseMatchesFromJson() creates unknown game for missing game', () {
|
||||
final playerById = {testPlayer1.id: testPlayer1};
|
||||
final gameById = <String, Game>{};
|
||||
final groupById = <String, Group>{};
|
||||
|
||||
final jsonMap = {
|
||||
'matches': [
|
||||
{
|
||||
'id': testMatch.id,
|
||||
'name': testMatch.name,
|
||||
'gameId': 'non-existent-game-id',
|
||||
'playerIds': [testPlayer1.id],
|
||||
'notes': '',
|
||||
'createdAt': testMatch.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final matches = DataTransferService.parseMatchesFromJson(
|
||||
jsonMap,
|
||||
gameById,
|
||||
groupById,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(matches.length, 1);
|
||||
expect(matches[0].game.name, 'Unknown');
|
||||
expect(matches[0].game.ruleset, Ruleset.singleWinner);
|
||||
});
|
||||
|
||||
test('parseMatchesFromJson() handles null group', () {
|
||||
final playerById = {testPlayer1.id: testPlayer1};
|
||||
final gameById = {testGame.id: testGame};
|
||||
final groupById = <String, Group>{};
|
||||
|
||||
final jsonMap = {
|
||||
'matches': [
|
||||
{
|
||||
'id': testMatch.id,
|
||||
'name': testMatch.name,
|
||||
'gameId': testGame.id,
|
||||
'groupId': null,
|
||||
'playerIds': [testPlayer1.id],
|
||||
'notes': '',
|
||||
'createdAt': testMatch.createdAt.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final matches = DataTransferService.parseMatchesFromJson(
|
||||
jsonMap,
|
||||
gameById,
|
||||
groupById,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(matches.length, 1);
|
||||
expect(matches[0].group, isNull);
|
||||
});
|
||||
|
||||
test('parseMatchesFromJson() handles endedAt', () {
|
||||
final playerById = {testPlayer1.id: testPlayer1};
|
||||
final gameById = {testGame.id: testGame};
|
||||
final groupById = <String, Group>{};
|
||||
final endedDate = DateTime(2025, 12, 1, 10, 0, 0);
|
||||
|
||||
final jsonMap = {
|
||||
'matches': [
|
||||
{
|
||||
'id': testMatch.id,
|
||||
'name': testMatch.name,
|
||||
'gameId': testGame.id,
|
||||
'playerIds': [testPlayer1.id],
|
||||
'notes': '',
|
||||
'createdAt': testMatch.createdAt.toIso8601String(),
|
||||
'endedAt': endedDate.toIso8601String(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
final matches = DataTransferService.parseMatchesFromJson(
|
||||
jsonMap,
|
||||
gameById,
|
||||
groupById,
|
||||
playerById,
|
||||
);
|
||||
|
||||
expect(matches.length, 1);
|
||||
expect(matches[0].endedAt, endedDate);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user