import 'package:fabled_app/data/api/voice_api.dart'; import 'package:fabled_app/providers/voice_provider.dart'; import 'package:fabled_app/data/models/knowledge_item.dart'; import 'package:fabled_app/data/models/note.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { group('Note.fromJson', () { test('parses noteType when present', () { final json = { 'id': 1, 'title': 'Alice', 'body': '', 'tags': [], 'note_type': 'person', 'created_at': '2024-01-01T00:00:00', 'updated_at': '2024-01-01T00:00:00', }; final note = Note.fromJson(json); expect(note.noteType, equals('person')); }); test('defaults noteType to note when absent', () { final json = { 'id': 2, 'title': 'My note', 'body': '', 'tags': [], 'created_at': '2024-01-01T00:00:00', 'updated_at': '2024-01-01T00:00:00', }; final note = Note.fromJson(json); expect(note.noteType, equals('note')); }); }); group('KnowledgeItem.fromJson', () { test('parses a task item with task fields', () { final json = { 'id': 10, 'note_type': 'task', 'title': 'Do the thing', 'body': '', 'tags': [], 'status': 'todo', 'priority': 'high', 'due_date': '2025-01-01', 'created_at': '2024-01-01T00:00:00', 'updated_at': '2024-01-01T00:00:00', }; final item = KnowledgeItem.fromJson(json); expect(item.noteType, equals('task')); expect(item.status, equals('todo')); expect(item.priority, equals('high')); }); test('defaults noteType to note when absent', () { final json = { 'id': 11, 'title': 'A note', 'body': '', 'tags': [], 'created_at': '2024-01-01T00:00:00', 'updated_at': '2024-01-01T00:00:00', }; final item = KnowledgeItem.fromJson(json); expect(item.noteType, equals('note')); expect(item.status, isNull); }); }); group('VoiceStatus.fromJson', () { test('parses enabled with stt and tts available', () { final status = VoiceStatus.fromJson({ 'enabled': true, 'stt': true, 'tts': true, }); expect(status.enabled, isTrue); expect(status.stt, isTrue); expect(status.tts, isTrue); }); test('parses disabled state', () { final status = VoiceStatus.fromJson({ 'enabled': false, 'stt': false, 'tts': false, }); expect(status.enabled, isFalse); }); test('fullyAvailable is false when enabled but stt is false', () { final status = VoiceStatus.fromJson({ 'enabled': true, 'stt': false, 'tts': true, }); expect(status.fullyAvailable, isFalse); }); }); group('VoiceNotifier sentence extraction', () { test('extracts complete sentences at full stops', () { final result = extractSentences('Hello world. How are you? I am fine!'); expect(result.sentences, equals(['Hello world.', 'How are you?', 'I am fine!'])); expect(result.remainder, equals('')); }); test('leaves incomplete fragment in remainder', () { final result = extractSentences('Hello world. Incomplete'); expect(result.sentences, equals(['Hello world.'])); expect(result.remainder, equals('Incomplete')); }); test('returns empty sentences and full text when no boundary', () { final result = extractSentences('No boundary here'); expect(result.sentences, isEmpty); expect(result.remainder, equals('No boundary here')); }); }); group('VoiceNotifier markdown stripping', () { test('strips code fences', () { expect(stripMarkdownForTts('Before\n```dart\ncode\n```\nAfter'), equals('Before After')); }); test('strips bold and italic markers', () { expect(stripMarkdownForTts('**bold** and *italic*'), equals('bold and italic')); }); test('strips headers', () { expect(stripMarkdownForTts('## Section title'), equals('Section title')); }); test('keeps link text, removes URL', () { expect(stripMarkdownForTts('[click here](https://example.com)'), equals('click here')); }); test('strips list markers', () { expect(stripMarkdownForTts('- item one\n- item two'), equals('item one item two')); }); }); }