mnemo_cards/mnemo_cards_backend/test/models/achievement_model_test.dart

591 lines
15 KiB
Dart
Raw Normal View History

import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
import 'package:test/test.dart';
void main() {
group('AchievementModel', () {
group('constructor', () {
test('creates model with default values', () {
final model = AchievementModel();
expect(model.id, '');
expect(model.title, '');
expect(model.description, '');
expect(model.iconUrl, isNull);
expect(model.unlockedAt, isNull);
expect(model.type, AchievementType.firstWordLearned);
expect(model.progress, 0.0);
});
test('creates model with all parameters', () {
final now = DateTime.now();
final model = AchievementModel(
id: 'test_achievement',
title: 'Test Achievement',
description: 'Test Description',
iconUrl: 'https://example.com/icon.png',
unlockedAt: now,
type: AchievementType.streak7Days,
progress: 0.75,
);
expect(model.id, 'test_achievement');
expect(model.title, 'Test Achievement');
expect(model.description, 'Test Description');
expect(model.iconUrl, 'https://example.com/icon.png');
expect(model.unlockedAt, now);
expect(model.type, AchievementType.streak7Days);
expect(model.progress, 0.75);
});
test('creates model with null iconUrl', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
expect(model.iconUrl, isNull);
});
test('creates model with null unlockedAt', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
expect(model.unlockedAt, isNull);
});
});
group('toDto', () {
test('converts model to DTO correctly', () {
final now = DateTime.now();
final model = AchievementModel(
id: 'test_achievement',
title: 'Test Achievement',
description: 'Test Description',
iconUrl: 'https://example.com/icon.png',
unlockedAt: now,
type: AchievementType.words100Learned,
progress: 0.85,
);
final dto = model.toDto();
expect(dto.id, 'test_achievement');
expect(dto.title, 'Test Achievement');
expect(dto.description, 'Test Description');
expect(dto.iconUrl, 'https://example.com/icon.png');
expect(dto.unlockedAt, now);
expect(dto.type, AchievementType.words100Learned);
expect(dto.progress, 0.85);
});
test('converts model with null values to DTO', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
final dto = model.toDto();
expect(dto.iconUrl, isNull);
expect(dto.unlockedAt, isNull);
});
test('converts all achievement types correctly', () {
for (final type in AchievementType.values) {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: type,
);
final dto = model.toDto();
expect(dto.type, type);
}
});
});
group('fromDto', () {
test('creates model from DTO correctly', () {
final now = DateTime.now();
final dto = AchievementDto(
id: 'test_achievement',
title: 'Test Achievement',
description: 'Test Description',
iconUrl: 'https://example.com/icon.png',
unlockedAt: now,
type: AchievementType.streak30Days,
progress: 0.9,
);
final model = AchievementModel.fromDto(dto);
expect(model.id, 'test_achievement');
expect(model.title, 'Test Achievement');
expect(model.description, 'Test Description');
expect(model.iconUrl, 'https://example.com/icon.png');
expect(model.unlockedAt, now);
expect(model.type, AchievementType.streak30Days);
expect(model.progress, 0.9);
});
test('creates model from DTO with null values', () {
final dto = AchievementDto(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.firstWordLearned,
);
final model = AchievementModel.fromDto(dto);
expect(model.iconUrl, isNull);
expect(model.unlockedAt, isNull);
});
test('creates model from DTO for all achievement types', () {
for (final type in AchievementType.values) {
final dto = AchievementDto(
id: 'test',
title: 'Test',
description: 'Test',
type: type,
);
final model = AchievementModel.fromDto(dto);
expect(model.type, type);
}
});
});
group('toDto and fromDto roundtrip', () {
test('preserves all data through conversion', () {
final now = DateTime.now();
final original = AchievementModel(
id: 'test_achievement',
title: 'Test Achievement',
description: 'Test Description',
iconUrl: 'https://example.com/icon.png',
unlockedAt: now,
type: AchievementType.perfectTestScore,
progress: 0.95,
);
final dto = original.toDto();
final restored = AchievementModel.fromDto(dto);
expect(restored.id, original.id);
expect(restored.title, original.title);
expect(restored.description, original.description);
expect(restored.iconUrl, original.iconUrl);
expect(restored.unlockedAt, original.unlockedAt);
expect(restored.type, original.type);
expect(restored.progress, original.progress);
});
});
group('isUnlocked', () {
test('returns false when unlockedAt is null', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
expect(model.isUnlocked, isFalse);
});
test('returns true when unlockedAt is set', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
unlockedAt: DateTime.now(),
);
expect(model.isUnlocked, isTrue);
});
});
group('isLocked', () {
test('returns true when unlockedAt is null', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
expect(model.isLocked, isTrue);
});
test('returns false when unlockedAt is set', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
unlockedAt: DateTime.now(),
);
expect(model.isLocked, isFalse);
});
});
group('unlock', () {
test('sets unlockedAt to current time', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
final beforeUnlock = DateTime.now();
final unlocked = model.unlock();
final afterUnlock = DateTime.now();
expect(unlocked.unlockedAt, isNotNull);
expect(
unlocked.unlockedAt!.isAfter(beforeUnlock.subtract(const Duration(seconds: 1))),
isTrue,
);
expect(
unlocked.unlockedAt!.isBefore(afterUnlock.add(const Duration(seconds: 1))),
isTrue,
);
});
test('sets progress to 1.0', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
progress: 0.5,
);
final unlocked = model.unlock();
expect(unlocked.progress, 1.0);
});
test('preserves other fields', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
iconUrl: 'https://example.com/icon.png',
type: AchievementType.streak7Days,
);
final unlocked = model.unlock();
expect(unlocked.id, model.id);
expect(unlocked.title, model.title);
expect(unlocked.description, model.description);
expect(unlocked.iconUrl, model.iconUrl);
expect(unlocked.type, model.type);
});
});
group('updateProgress', () {
test('updates progress correctly', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
progress: 0.3,
);
final updated = model.updateProgress(0.7);
expect(updated.progress, 0.7);
});
test('clamps progress to 0.0 minimum', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
final updated = model.updateProgress(-0.5);
expect(updated.progress, 0.0);
});
test('clamps progress to 1.0 maximum', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
);
final updated = model.updateProgress(1.5);
expect(updated.progress, 1.0);
});
test('preserves unlockedAt when updating progress', () {
final now = DateTime.now();
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
unlockedAt: now,
);
final updated = model.updateProgress(0.5);
expect(updated.unlockedAt, now);
});
test('preserves other fields', () {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
iconUrl: 'https://example.com/icon.png',
type: AchievementType.words50Learned,
);
final updated = model.updateProgress(0.6);
expect(updated.id, model.id);
expect(updated.title, model.title);
expect(updated.description, model.description);
expect(updated.iconUrl, model.iconUrl);
expect(updated.type, model.type);
});
});
group('category', () {
test('returns correct category for first_steps achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.firstWordLearned,
).category,
'first_steps',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.firstTestCompleted,
).category,
'first_steps',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.firstPackCompleted,
).category,
'first_steps',
);
});
test('returns correct category for streaks achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.streak3Days,
).category,
'streaks',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.streak100Days,
).category,
'streaks',
);
});
test('returns correct category for words_mastery achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.words10Learned,
).category,
'words_mastery',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.words1000Learned,
).category,
'words_mastery',
);
});
test('returns correct category for performance achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.perfectTestScore,
).category,
'performance',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.speedLearner,
).category,
'performance',
);
});
test('returns correct category for dedication achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.dedicatedLearner,
).category,
'dedication',
);
});
test('returns correct category for time_based achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.nightOwl,
).category,
'time_based',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.earlyBird,
).category,
'time_based',
);
});
test('returns correct category for special achievements', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.consistentLearner,
).category,
'special',
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: AchievementType.languageMaster,
).category,
'special',
);
});
});
group('edge cases', () {
test('handles empty strings', () {
final model = AchievementModel(
id: '',
title: '',
description: '',
);
expect(model.id, '');
expect(model.title, '');
expect(model.description, '');
});
test('handles very long strings', () {
final longString = 'a' * 1000;
final model = AchievementModel(
id: longString,
title: longString,
description: longString,
);
expect(model.id, longString);
expect(model.title, longString);
expect(model.description, longString);
});
test('handles progress at boundaries', () {
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
progress: 0.0,
).progress,
0.0,
);
expect(
AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
progress: 1.0,
).progress,
1.0,
);
});
test('handles all achievement types', () {
for (final type in AchievementType.values) {
final model = AchievementModel(
id: 'test',
title: 'Test',
description: 'Test',
type: type,
);
expect(model.type, type);
expect(model.category, isNotEmpty);
}
});
});
});
}