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
122 lines
3.8 KiB
Dart
122 lines
3.8 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:worldhopper/models/entry.dart';
|
|
import 'package:worldhopper/models/stream_link.dart';
|
|
import 'package:worldhopper/services/server_software/server_software.dart';
|
|
|
|
// Test the fetchEntryProgress logic directly since it's a pure data extraction
|
|
// from StreamLink.lastRead — no HTTP or platform channels needed.
|
|
|
|
Map<String, EntryProgress> _extractProgress(List<Entry> entries) {
|
|
final result = <String, EntryProgress>{};
|
|
for (final entry in entries) {
|
|
if (entry.hasStreamLink &&
|
|
entry.streamLink!.lastRead != null &&
|
|
entry.streamLink!.lastRead! > 0) {
|
|
result[entry.id] = EntryProgress(
|
|
entryId: entry.id,
|
|
currentPage: entry.streamLink!.lastRead!,
|
|
totalPages: entry.streamLink!.pageCount,
|
|
);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
StreamLink _streamLink({int pageCount = 50, int? lastRead}) => StreamLink(
|
|
href: 'https://example.com/page/{pageNumber}',
|
|
type: 'image/jpeg',
|
|
pageCount: pageCount,
|
|
lastRead: lastRead,
|
|
);
|
|
|
|
Entry _entry(String id, {StreamLink? streamLink}) => Entry(
|
|
id: id,
|
|
title: 'Entry $id',
|
|
streamLink: streamLink,
|
|
);
|
|
|
|
void main() {
|
|
group('OPDS fetchEntryProgress logic', () {
|
|
test('extracts progress from StreamLink.lastRead', () {
|
|
final entries = [
|
|
_entry('e1', streamLink: _streamLink(pageCount: 100, lastRead: 49)),
|
|
_entry('e2', streamLink: _streamLink(pageCount: 50, lastRead: 24)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
|
|
expect(result, hasLength(2));
|
|
expect(result['e1']!.currentPage, 49);
|
|
expect(result['e1']!.totalPages, 100);
|
|
expect(result['e1']!.percentage, 0.5);
|
|
expect(result['e2']!.currentPage, 24);
|
|
expect(result['e2']!.totalPages, 50);
|
|
expect(result['e2']!.percentage, 0.5);
|
|
});
|
|
|
|
test('skips entries without StreamLink', () {
|
|
final entries = [
|
|
_entry('e1'),
|
|
_entry('e2', streamLink: _streamLink(pageCount: 10, lastRead: 5)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
|
|
expect(result, hasLength(1));
|
|
expect(result.containsKey('e1'), isFalse);
|
|
expect(result.containsKey('e2'), isTrue);
|
|
});
|
|
|
|
test('skips entries with lastRead = null', () {
|
|
final entries = [
|
|
_entry('e1', streamLink: _streamLink(pageCount: 10, lastRead: null)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
expect(result, isEmpty);
|
|
});
|
|
|
|
test('skips entries with lastRead = 0', () {
|
|
final entries = [
|
|
_entry('e1', streamLink: _streamLink(pageCount: 10, lastRead: 0)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
expect(result, isEmpty);
|
|
});
|
|
|
|
test('returns empty map for empty entry list', () {
|
|
final result = _extractProgress([]);
|
|
expect(result, isEmpty);
|
|
});
|
|
|
|
test('marks completed entry correctly', () {
|
|
final entries = [
|
|
_entry('e1', streamLink: _streamLink(pageCount: 10, lastRead: 9)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
|
|
expect(result['e1']!.isCompleted, isTrue);
|
|
expect(result['e1']!.percentage, 1.0);
|
|
});
|
|
|
|
test('handles mixed entries with and without progress', () {
|
|
final entries = [
|
|
_entry('e1', streamLink: _streamLink(pageCount: 20, lastRead: 10)),
|
|
_entry('e2'),
|
|
_entry('e3', streamLink: _streamLink(pageCount: 30, lastRead: null)),
|
|
_entry('e4', streamLink: _streamLink(pageCount: 40, lastRead: 0)),
|
|
_entry('e5', streamLink: _streamLink(pageCount: 50, lastRead: 25)),
|
|
];
|
|
|
|
final result = _extractProgress(entries);
|
|
|
|
expect(result, hasLength(2));
|
|
expect(result.containsKey('e1'), isTrue);
|
|
expect(result.containsKey('e5'), isTrue);
|
|
expect(result['e1']!.percentage, closeTo(0.55, 0.01));
|
|
expect(result['e5']!.percentage, closeTo(0.52, 0.01));
|
|
});
|
|
});
|
|
}
|