worldhopper/test/models/publication_test.dart
Felipe M. 58a562abe8
feat: refactor server software abstraction with Kavita REST API integration
Rename all OPDS-prefixed models to generic names (Server, Feed, Entry,
Link, StreamLink), expand the ServerSoftware interface to cover all
server interactions, and implement a full Kavita REST API client that
replaces the OPDS delegation.

- Rename OPDS* models to generic names across ~60 files
- Add database migrations 13 (opds_id → entry_id) and 14 (unify credentials)
- Create OPDSServerSoftware wrapping existing OPDS services
- Create KavitaApiClient for direct Kavita REST API calls
- Create KavitaFeedMapper to convert Kavita JSON to Feed/Entry models
- Rewrite KavitaServerSoftware to use native API (no OPDS delegation)
- Unify server credentials (remove softwareUsername/softwarePassword)
- Simplify add/edit server UI to single auth section
- Add test connection button to server add/edit screens
- Add progress indicator to PublicationCard using local and server data
- Eliminate softwareType branching in reader screens
- Add EntryProgress and fetchEntryProgress to ServerSoftware interface
- Fix Entry.acquisitionLink crash on empty links
- Add 59 new tests covering models, services, and providers
2026-04-06 17:46:48 +02:00

92 lines
2.6 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:worldhopper/models/enhanced_metadata.dart';
import 'package:worldhopper/models/entry.dart';
import 'package:worldhopper/models/publication.dart';
void main() {
group('Publication', () {
test('fromEntry uses entry.id as entryId', () {
const entry = Entry(
id: 'opds-entry-123',
title: 'Test Book',
authors: ['Author'],
);
final pub = Publication.fromEntry(
id: 'pub-1',
serverId: 's1',
entry: entry,
metadata: const EnhancedMetadata.empty(),
);
expect(pub.entryId, 'opds-entry-123');
expect(pub.title, 'Test Book');
expect(pub.authors, ['Author']);
});
test('toEntry converts back with entryId as id', () {
final now = DateTime(2024);
final pub = Publication(
id: 'pub-1',
serverId: 's1',
entryId: 'entry-456',
title: 'My Book',
authors: const ['Writer'],
firstCachedAt: now,
lastCachedAt: now,
lastAccessedAt: now,
);
final entry = pub.toEntry();
expect(entry.id, 'entry-456');
expect(entry.title, 'My Book');
expect(entry.authors, ['Writer']);
});
test('toDatabase uses entry_id column name', () {
final now = DateTime(2024);
final pub = Publication(
id: 'pub-1',
serverId: 's1',
entryId: 'my-entry-id',
title: 'Book',
firstCachedAt: now,
lastCachedAt: now,
lastAccessedAt: now,
);
final row = pub.toDatabase();
expect(row['entry_id'], 'my-entry-id');
expect(row.containsKey('opds_id'), isFalse);
});
test('fromDatabase reads entry_id column', () {
final now = DateTime(2024).millisecondsSinceEpoch;
final row = {
'id': 'pub-1',
'server_id': 's1',
'entry_id': 'restored-entry-id',
'title': 'Book',
'authors': '["Author"]',
'summary': null,
'content': null,
'cover_path': null,
'thumbnail_path': null,
'cover_url': null,
'thumbnail_url': null,
'series': null,
'series_position': null,
'publisher': null,
'language': null,
'isbn': null,
'categories': '[]',
'links': '[]',
'stream_link': null,
'published': null,
'updated': null,
'first_cached_at': now,
'last_cached_at': now,
'last_accessed_at': now,
};
final pub = Publication.fromDatabase(row);
expect(pub.entryId, 'restored-entry-id');
expect(pub.title, 'Book');
});
});
}