diff --git a/lib/data/models/briefing_feed.dart b/lib/data/models/briefing_feed.dart new file mode 100644 index 0000000..6bfc71e --- /dev/null +++ b/lib/data/models/briefing_feed.dart @@ -0,0 +1,20 @@ +class BriefingFeed { + final int id; + final String title; + final String url; + final String? category; + + const BriefingFeed({ + required this.id, + required this.title, + required this.url, + this.category, + }); + + factory BriefingFeed.fromJson(Map json) => BriefingFeed( + id: json['id'] as int, + title: json['title'] as String? ?? '', + url: json['url'] as String? ?? '', + category: json['category'] as String?, + ); +} diff --git a/lib/data/models/news_item.dart b/lib/data/models/news_item.dart new file mode 100644 index 0000000..23ca547 --- /dev/null +++ b/lib/data/models/news_item.dart @@ -0,0 +1,37 @@ +class NewsItem { + final int id; + final String title; + final String url; + final String snippet; + final String source; + final DateTime? publishedAt; + final List topics; + final String? reaction; + + const NewsItem({ + required this.id, + required this.title, + required this.url, + required this.snippet, + required this.source, + this.publishedAt, + required this.topics, + this.reaction, + }); + + factory NewsItem.fromJson(Map json) => NewsItem( + id: json['id'] as int, + title: json['title'] as String? ?? '', + url: json['url'] as String? ?? '', + snippet: json['snippet'] as String? ?? '', + source: json['source'] as String? ?? '', + publishedAt: json['published_at'] != null + ? DateTime.tryParse(json['published_at'] as String) + : null, + topics: (json['topics'] as List?) + ?.cast() + .toList() ?? + [], + reaction: json['reaction'] as String?, + ); +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 667fdd2..3820264 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -2,6 +2,8 @@ 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:fabled_app/data/models/news_item.dart'; +import 'package:fabled_app/data/models/briefing_feed.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -146,4 +148,72 @@ void main() { equals('item one item two')); }); }); + + group('NewsItem.fromJson', () { + test('parses all fields', () { + final json = { + 'id': 42, + 'title': 'Big news', + 'url': 'https://example.com/article', + 'snippet': 'A short summary.', + 'source': 'Example News', + 'published_at': '2026-01-15T10:00:00', + 'topics': ['tech', 'ai'], + 'reaction': 'up', + }; + final item = NewsItem.fromJson(json); + expect(item.id, equals(42)); + expect(item.title, equals('Big news')); + expect(item.url, equals('https://example.com/article')); + expect(item.snippet, equals('A short summary.')); + expect(item.source, equals('Example News')); + expect(item.publishedAt, equals(DateTime.parse('2026-01-15T10:00:00'))); + expect(item.topics, equals(['tech', 'ai'])); + expect(item.reaction, equals('up')); + }); + + test('handles null published_at and reaction', () { + final json = { + 'id': 1, + 'title': '', + 'url': '', + 'snippet': '', + 'source': '', + 'published_at': null, + 'topics': [], + 'reaction': null, + }; + final item = NewsItem.fromJson(json); + expect(item.publishedAt, isNull); + expect(item.reaction, isNull); + expect(item.topics, isEmpty); + }); + }); + + group('BriefingFeed.fromJson', () { + test('parses all fields', () { + final json = { + 'id': 7, + 'title': 'Hacker News', + 'url': 'https://news.ycombinator.com/rss', + 'category': 'tech', + }; + final feed = BriefingFeed.fromJson(json); + expect(feed.id, equals(7)); + expect(feed.title, equals('Hacker News')); + expect(feed.url, equals('https://news.ycombinator.com/rss')); + expect(feed.category, equals('tech')); + }); + + test('handles null category', () { + final json = { + 'id': 8, + 'title': 'Feed', + 'url': 'https://example.com/rss', + 'category': null, + }; + final feed = BriefingFeed.fromJson(json); + expect(feed.category, isNull); + }); + }); }