stuff
Some checks are pending
Web App CI / test (push) Waiting to run
Web App CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2025-12-19 00:37:22 +03:00
parent 339bbaae2d
commit fface20519
10 changed files with 65 additions and 85 deletions

View file

@ -5,15 +5,15 @@ void main() {
group('ApiConfigV2 voice endpoints', () {
test('builds pack card voices path', () {
expect(
ApiConfigV2.packCardVoices('123', 45),
ApiConfigV2.packCardVoices('123', '45'),
'/packs/123/cards/45/voices',
);
});
test('builds voice file path and url', () {
expect(ApiConfigV2.voiceFile(7), '/voice/7');
expect(ApiConfigV2.voiceFile('7'), '/voice/7');
final voiceUrl = ApiConfigV2.getVoiceFileUrl(7);
final voiceUrl = ApiConfigV2.getVoiceFileUrl('7');
expect(voiceUrl, contains('/api/v2/voice/7'));
expect(voiceUrl.startsWith('http'), isTrue);
});

View file

@ -93,21 +93,15 @@ void main() {
test('creates request correctly', () {
final request = CreateChatSessionRequest(
title: 'New Chat Session',
description: 'A new chat',
tags: ['ai', 'assistant'],
);
expect(request.title, 'New Chat Session');
expect(request.description, 'A new chat');
expect(request.tags, ['ai', 'assistant']);
});
test('supports optional fields', () {
final request = CreateChatSessionRequest(title: 'Simple Chat');
expect(request.title, 'Simple Chat');
expect(request.description, isNull);
expect(request.tags, isEmpty);
});
});
@ -164,8 +158,6 @@ void main() {
test('CreateChatSessionRequest serializes and deserializes correctly', () {
final request = CreateChatSessionRequest(
title: 'New Session',
description: 'Description',
tags: ['tag1', 'tag2'],
);
final json = request.toJson();
@ -230,20 +222,14 @@ void main() {
test('CreateChatSessionRequest equality works correctly', () {
final request1 = CreateChatSessionRequest(
title: 'Test',
description: 'Desc',
tags: ['tag'],
);
final request2 = CreateChatSessionRequest(
title: 'Test',
description: 'Desc',
tags: ['tag'],
);
final request3 = CreateChatSessionRequest(
title: 'Different',
description: 'Desc',
tags: ['tag'],
);
expect(request1, equals(request2));

View file

@ -19,7 +19,7 @@ void main() {
late AuthService authService;
const code = '123456';
final user = UserDto(id: 1, email: 'user@example.com', name: 'Test User');
final user = UserDto(id: '1', email: 'user@example.com', name: 'Test User');
final authResponse = AuthResponseV2(
user: user,
accessToken: 'access',

View file

@ -53,7 +53,7 @@ void main() {
// Test that the method exists and handles auth errors properly
// Actual submission would require backend connectivity and auth
final statistics = TestStatisticsDto(
testId: 123,
testId: 'test-123',
words: AllWordsStatisticsDto.empty(),
attempts: 1,
);

View file

@ -33,7 +33,7 @@ void main() {
await stateManager.loadFavorites();
// Check that loaded state has the favorites
expect(stateManager.favorites, {1, 2, 3});
expect(stateManager.favorites, {'1', '2', '3'});
});
test('loadFavorites handles empty favorites', () async {
@ -43,53 +43,49 @@ void main() {
expect(stateManager.favorites, isEmpty);
});
test('loadFavorites handles invalid IDs', () async {
// Set up favorites with invalid ID
test('loadFavorites keeps stored IDs as strings', () async {
await prefs.setStringList('favorites', ['1', 'invalid', '3']);
await stateManager.loadFavorites();
// Should only contain valid IDs
expect(stateManager.favorites, {1, 3});
expect(stateManager.favorites, {'1', 'invalid', '3'});
});
test('toggleFavorite adds card to favorites', () async {
await stateManager.loadFavorites();
// Toggle a favorite
await stateManager.toggleFavorite(5);
await stateManager.toggleFavorite('5');
// Check that card is now favorite
expect(stateManager.isFavorite(5), isTrue);
expect(stateManager.isFavorite('5'), isTrue);
});
test('toggleFavorite removes card from favorites', () async {
await stateManager.loadFavorites();
// Add a favorite
await stateManager.toggleFavorite(5);
expect(stateManager.isFavorite(5), isTrue);
await stateManager.toggleFavorite('5');
expect(stateManager.isFavorite('5'), isTrue);
// Remove it
await stateManager.toggleFavorite(5);
expect(stateManager.isFavorite(5), isFalse);
await stateManager.toggleFavorite('5');
expect(stateManager.isFavorite('5'), isFalse);
});
test('toggleFavorite persists to SharedPreferences', () async {
await stateManager.loadFavorites();
// Toggle a favorite
await stateManager.toggleFavorite(7);
await stateManager.toggleFavorite('7');
// Check persistence by reloading
await stateManager.loadFavorites();
expect(stateManager.favorites, {7});
expect(stateManager.favorites, {'7'});
});
test('isFavorite returns false for unknown card', () {
// Without loading
expect(stateManager.isFavorite(1), isFalse);
expect(stateManager.isFavorite('1'), isFalse);
});
test('favorites getter returns empty set initially', () {
@ -101,23 +97,23 @@ void main() {
await stateManager.loadFavorites();
expect(stateManager.favorites, {10, 20});
expect(stateManager.favorites, {'10', '20'});
});
test('multiple toggles work correctly', () async {
await stateManager.loadFavorites();
// Toggle multiple favorites
await stateManager.toggleFavorite(1);
await stateManager.toggleFavorite(2);
await stateManager.toggleFavorite(3);
await stateManager.toggleFavorite('1');
await stateManager.toggleFavorite('2');
await stateManager.toggleFavorite('3');
expect(stateManager.favorites, {1, 2, 3});
expect(stateManager.favorites, {'1', '2', '3'});
// Remove one
await stateManager.toggleFavorite(2);
await stateManager.toggleFavorite('2');
expect(stateManager.favorites, {1, 3});
expect(stateManager.favorites, {'1', '3'});
});
});
}

View file

@ -1,5 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
import 'package:mnemo_cards_web_v2/domain/models/purchase_models.dart';
import 'package:mnemo_cards_web_v2/domain/services/purchases_service.dart';
import 'package:mnemo_cards_web_v2/domain/state/purchase_state_manager.dart';
import 'package:mocktail/mocktail.dart';

View file

@ -46,7 +46,7 @@ void main() {
name: 'Test Game',
questions: [
SimpleTestQuestionBody(
id: 1,
id: 'q1',
text: 'What is 2+2?',
buttons: [
TestButtonDto.text('3', '3'),
@ -107,14 +107,14 @@ void main() {
name: 'Multi Question Test',
questions: [
SimpleTestQuestionBody(
id: 1,
id: 'q1',
text: 'Q1?',
buttons: [TestButtonDto.text('A', 'A')],
answer: 'A',
word: 'one',
),
SimpleTestQuestionBody(
id: 2,
id: 'q2',
text: 'Q2?',
buttons: [TestButtonDto.text('B', 'B')],
answer: 'B',

View file

@ -33,15 +33,15 @@ void main() {
late UserStateManager userStateManager;
late SharedPreferences sharedPreferences;
const testUser = UserDto(
final testUser = UserDto(
id: 'test-user-123',
name: 'Test User',
email: 'test@example.com',
);
const testPackId = 'pack-abc-123';
const testCardId1 = 1;
const testCardId2 = 2;
const testCardId1 = '1';
const testCardId2 = '2';
setUp(() async {
// Initialize SharedPreferences with mock
@ -75,21 +75,21 @@ void main() {
expect(userStateManager.state, equals(const UserState.guest()));
// === STEP 2: Auto-login attempt (no saved token) ===
when(mockHttpRepository.isAuthenticated())
.thenAnswer((_) async => false);
when(() => mockHttpRepository.isAuthenticated()).thenAnswer((_) async => false);
final autoLoginUser = await authService.autoLogin();
expect(autoLoginUser, isNull);
// === STEP 3: Manual login with Telegram ===
const telegramCode = 'telegram-auth-code-123';
final authResponse = AuthResponse(
final authResponse = AuthResponseV2(
user: testUser,
accessToken: 'test-access-token',
refreshToken: 'test-refresh-token',
expiresAt: null,
);
when(mockHttpRepository.authenticateWithTelegram(telegramCode))
when(() => mockHttpRepository.authenticateWithTelegram(telegramCode))
.thenAnswer((_) async => authResponse);
final loggedInUser = await authService.loginWithTelegram(telegramCode);
@ -98,8 +98,9 @@ void main() {
expect(loggedInUser?.name, equals(testUser.name));
// Update user state
userStateManager.setUser(loggedInUser!);
expect(userStateManager.state, equals(UserState.authenticated(testUser)));
await userStateManager.setUser(loggedInUser!);
expect(userStateManager.isAuthenticated, isTrue);
expect(userStateManager.user, equals(testUser));
// === STEP 4: User starts learning cards ===
// Check initial progress
@ -137,8 +138,8 @@ void main() {
expect(newPackProgressService.isCardLearned(testPackId, testCardId2), isTrue);
// === STEP 6: User logs out ===
when(mockHttpRepository.logout()).thenAnswer((_) async => {});
when(mockHttpRepository.clearTokens()).thenAnswer((_) async => {});
when(() => mockHttpRepository.logout()).thenAnswer((_) async {});
when(() => mockHttpRepository.clearTokens()).thenAnswer((_) async {});
await authService.logout();
userStateManager.logout();
@ -188,20 +189,20 @@ void main() {
test('User state transitions are correct', () async {
// Start as guest
expect(userStateManager.state.isGuest, isTrue);
expect(userStateManager.state.isAuthenticated, isFalse);
expect(userStateManager.isGuest, isTrue);
expect(userStateManager.isAuthenticated, isFalse);
// Login
userStateManager.setUser(testUser);
expect(userStateManager.state.isGuest, isFalse);
expect(userStateManager.state.isAuthenticated, isTrue);
expect(userStateManager.state.user, equals(testUser));
await userStateManager.setUser(testUser);
expect(userStateManager.isGuest, isFalse);
expect(userStateManager.isAuthenticated, isTrue);
expect(userStateManager.user, equals(testUser));
// Logout
userStateManager.logout();
expect(userStateManager.state.isGuest, isTrue);
expect(userStateManager.state.isAuthenticated, isFalse);
expect(userStateManager.state.user, isNull);
expect(userStateManager.isGuest, isTrue);
expect(userStateManager.isAuthenticated, isFalse);
expect(userStateManager.user, isNull);
});
});
}

View file

@ -31,15 +31,15 @@ void main() {
late SharedPreferences sharedPreferences;
final testUser = UserDto(
id: 123,
id: '123',
name: 'Test User',
email: 'test@example.com',
);
const testPackId = 'pack-abc-123';
const testCardId1 = 1;
const testCardId2 = 2;
const testCardId3 = 3;
const testCardId1 = '1';
const testCardId2 = '2';
const testCardId3 = '3';
setUp(() async {
// Initialize SharedPreferences with mock
@ -202,26 +202,26 @@ void main() {
expect(packProgressService.getPackProgress(packId), equals(0));
// Learn cards one by one
await packProgressService.markCardLearned(packId, 1);
await packProgressService.markCardLearned(packId, '1');
expect(packProgressService.getPackProgress(packId), equals(1));
await packProgressService.markCardLearned(packId, 2);
await packProgressService.markCardLearned(packId, '2');
expect(packProgressService.getPackProgress(packId), equals(2));
await packProgressService.markCardLearned(packId, 3);
await packProgressService.markCardLearned(packId, '3');
expect(packProgressService.getPackProgress(packId), equals(3));
await packProgressService.markCardLearned(packId, 4);
await packProgressService.markCardLearned(packId, '4');
expect(packProgressService.getPackProgress(packId), equals(4));
await packProgressService.markCardLearned(packId, 5);
await packProgressService.markCardLearned(packId, '5');
expect(packProgressService.getPackProgress(packId), equals(5));
// Verify all cards are learned
final learnedCards = packProgressService.getLearnedCards(packId);
expect(learnedCards.length, equals(5));
for (int i = 1; i <= 5; i++) {
expect(packProgressService.isCardLearned(packId, i), isTrue);
expect(packProgressService.isCardLearned(packId, '$i'), isTrue);
}
});

View file

@ -15,19 +15,11 @@ class MockHttpRepositoryV2 extends Mock implements HttpRepositoryV2 {}
class MockUserScopeHolder extends Mock implements UserScopeHolder {}
class _TestAppScopeHolder extends ScopeHolder<AppScopeContainer> {
_TestAppScopeHolder(this._scope);
final AppScopeContainer _scope;
@override
AppScopeContainer createContainer() => _scope;
}
void main() {
late MockAppScopeContainer mockAppScope;
late MockHttpRepositoryV2 mockHttpRepository;
late MockUserScopeHolder mockUserScopeHolder;
late ScopeStateHolder<AppScopeContainer?> appScopeHolder;
setUp(() {
mockAppScope = MockAppScopeContainer();
@ -37,12 +29,16 @@ void main() {
when(() => mockAppScope.httpRepository).thenReturn(mockHttpRepository);
when(() => mockAppScope.userScopeHolder).thenReturn(mockUserScopeHolder);
when(() => mockUserScopeHolder.scope).thenReturn(null);
appScopeHolder = ScopeStateHolder<AppScopeContainer?>(
ScopeState.available(scope: mockAppScope),
);
});
Future<void> pumpPackDetails(WidgetTester tester, {required String packId}) {
return tester.pumpWidget(
ScopeProvider<AppScopeContainer>(
holder: _TestAppScopeHolder(mockAppScope),
holder: appScopeHolder,
child: MaterialApp(
home: PackDetailsPage(packId: packId),
),