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', () { group('ApiConfigV2 voice endpoints', () {
test('builds pack card voices path', () { test('builds pack card voices path', () {
expect( expect(
ApiConfigV2.packCardVoices('123', 45), ApiConfigV2.packCardVoices('123', '45'),
'/packs/123/cards/45/voices', '/packs/123/cards/45/voices',
); );
}); });
test('builds voice file path and url', () { 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, contains('/api/v2/voice/7'));
expect(voiceUrl.startsWith('http'), isTrue); expect(voiceUrl.startsWith('http'), isTrue);
}); });

View file

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

View file

@ -19,7 +19,7 @@ void main() {
late AuthService authService; late AuthService authService;
const code = '123456'; 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( final authResponse = AuthResponseV2(
user: user, user: user,
accessToken: 'access', accessToken: 'access',

View file

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

View file

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

View file

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

View file

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

View file

@ -31,15 +31,15 @@ void main() {
late SharedPreferences sharedPreferences; late SharedPreferences sharedPreferences;
final testUser = UserDto( final testUser = UserDto(
id: 123, id: '123',
name: 'Test User', name: 'Test User',
email: 'test@example.com', email: 'test@example.com',
); );
const testPackId = 'pack-abc-123'; const testPackId = 'pack-abc-123';
const testCardId1 = 1; const testCardId1 = '1';
const testCardId2 = 2; const testCardId2 = '2';
const testCardId3 = 3; const testCardId3 = '3';
setUp(() async { setUp(() async {
// Initialize SharedPreferences with mock // Initialize SharedPreferences with mock
@ -202,26 +202,26 @@ void main() {
expect(packProgressService.getPackProgress(packId), equals(0)); expect(packProgressService.getPackProgress(packId), equals(0));
// Learn cards one by one // Learn cards one by one
await packProgressService.markCardLearned(packId, 1); await packProgressService.markCardLearned(packId, '1');
expect(packProgressService.getPackProgress(packId), equals(1)); expect(packProgressService.getPackProgress(packId), equals(1));
await packProgressService.markCardLearned(packId, 2); await packProgressService.markCardLearned(packId, '2');
expect(packProgressService.getPackProgress(packId), equals(2)); expect(packProgressService.getPackProgress(packId), equals(2));
await packProgressService.markCardLearned(packId, 3); await packProgressService.markCardLearned(packId, '3');
expect(packProgressService.getPackProgress(packId), equals(3)); expect(packProgressService.getPackProgress(packId), equals(3));
await packProgressService.markCardLearned(packId, 4); await packProgressService.markCardLearned(packId, '4');
expect(packProgressService.getPackProgress(packId), equals(4)); expect(packProgressService.getPackProgress(packId), equals(4));
await packProgressService.markCardLearned(packId, 5); await packProgressService.markCardLearned(packId, '5');
expect(packProgressService.getPackProgress(packId), equals(5)); expect(packProgressService.getPackProgress(packId), equals(5));
// Verify all cards are learned // Verify all cards are learned
final learnedCards = packProgressService.getLearnedCards(packId); final learnedCards = packProgressService.getLearnedCards(packId);
expect(learnedCards.length, equals(5)); expect(learnedCards.length, equals(5));
for (int i = 1; i <= 5; i++) { 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 MockUserScopeHolder extends Mock implements UserScopeHolder {}
class _TestAppScopeHolder extends ScopeHolder<AppScopeContainer> {
_TestAppScopeHolder(this._scope);
final AppScopeContainer _scope;
@override
AppScopeContainer createContainer() => _scope;
}
void main() { void main() {
late MockAppScopeContainer mockAppScope; late MockAppScopeContainer mockAppScope;
late MockHttpRepositoryV2 mockHttpRepository; late MockHttpRepositoryV2 mockHttpRepository;
late MockUserScopeHolder mockUserScopeHolder; late MockUserScopeHolder mockUserScopeHolder;
late ScopeStateHolder<AppScopeContainer?> appScopeHolder;
setUp(() { setUp(() {
mockAppScope = MockAppScopeContainer(); mockAppScope = MockAppScopeContainer();
@ -37,12 +29,16 @@ void main() {
when(() => mockAppScope.httpRepository).thenReturn(mockHttpRepository); when(() => mockAppScope.httpRepository).thenReturn(mockHttpRepository);
when(() => mockAppScope.userScopeHolder).thenReturn(mockUserScopeHolder); when(() => mockAppScope.userScopeHolder).thenReturn(mockUserScopeHolder);
when(() => mockUserScopeHolder.scope).thenReturn(null); when(() => mockUserScopeHolder.scope).thenReturn(null);
appScopeHolder = ScopeStateHolder<AppScopeContainer?>(
ScopeState.available(scope: mockAppScope),
);
}); });
Future<void> pumpPackDetails(WidgetTester tester, {required String packId}) { Future<void> pumpPackDetails(WidgetTester tester, {required String packId}) {
return tester.pumpWidget( return tester.pumpWidget(
ScopeProvider<AppScopeContainer>( ScopeProvider<AppScopeContainer>(
holder: _TestAppScopeHolder(mockAppScope), holder: appScopeHolder,
child: MaterialApp( child: MaterialApp(
home: PackDetailsPage(packId: packId), home: PackDetailsPage(packId: packId),
), ),