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
127 lines
3.3 KiB
Dart
127 lines
3.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:worldhopper/models/entry.dart';
|
|
import 'package:worldhopper/models/feed.dart';
|
|
import 'package:worldhopper/providers/series_navigation_provider.dart';
|
|
|
|
Entry _entry(String id, String title) => Entry(id: id, title: title);
|
|
|
|
Feed _feedWith(List<Entry> entries) =>
|
|
Feed(id: 'feed-1', title: 'Series', entries: entries);
|
|
|
|
/// Extracts the next-in-series logic from the provider for unit testing.
|
|
/// This mirrors the logic in nextInSeriesProvider without needing Riverpod.
|
|
Entry? _findNextInSeries(Feed feed, String currentEntryId) {
|
|
final entries = feed.entries;
|
|
for (var i = 0; i < entries.length; i++) {
|
|
if (entries[i].id == currentEntryId) {
|
|
if (i + 1 < entries.length) {
|
|
return entries[i + 1];
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void main() {
|
|
group('nextInSeriesProvider', () {
|
|
test('returns next entry when current is in the middle', () {
|
|
final feed = _feedWith([
|
|
_entry('ch1', 'Chapter 1'),
|
|
_entry('ch2', 'Chapter 2'),
|
|
_entry('ch3', 'Chapter 3'),
|
|
]);
|
|
|
|
final result = _findNextInSeries(feed, 'ch1');
|
|
|
|
expect(result, isNotNull);
|
|
expect(result!.id, 'ch2');
|
|
expect(result.title, 'Chapter 2');
|
|
});
|
|
|
|
test('returns next entry when current is second-to-last', () {
|
|
final feed = _feedWith([
|
|
_entry('ch1', 'Chapter 1'),
|
|
_entry('ch2', 'Chapter 2'),
|
|
_entry('ch3', 'Chapter 3'),
|
|
]);
|
|
|
|
final result = _findNextInSeries(feed, 'ch2');
|
|
|
|
expect(result, isNotNull);
|
|
expect(result!.id, 'ch3');
|
|
});
|
|
|
|
test('returns null when current entry is the last', () {
|
|
final feed = _feedWith([
|
|
_entry('ch1', 'Chapter 1'),
|
|
_entry('ch2', 'Chapter 2'),
|
|
_entry('ch3', 'Chapter 3'),
|
|
]);
|
|
|
|
final result = _findNextInSeries(feed, 'ch3');
|
|
|
|
expect(result, isNull);
|
|
});
|
|
|
|
test('returns null when current entry is not found in feed', () {
|
|
final feed = _feedWith([
|
|
_entry('ch1', 'Chapter 1'),
|
|
_entry('ch2', 'Chapter 2'),
|
|
]);
|
|
|
|
final result = _findNextInSeries(feed, 'unknown-id');
|
|
|
|
expect(result, isNull);
|
|
});
|
|
|
|
test('returns null for an empty feed', () {
|
|
final feed = _feedWith([]);
|
|
|
|
final result = _findNextInSeries(feed, 'ch1');
|
|
|
|
expect(result, isNull);
|
|
});
|
|
|
|
test('returns null for single-entry feed', () {
|
|
final feed = _feedWith([_entry('ch1', 'Only Chapter')]);
|
|
|
|
final result = _findNextInSeries(feed, 'ch1');
|
|
|
|
expect(result, isNull);
|
|
});
|
|
});
|
|
|
|
group('NextInSeriesRequest equality', () {
|
|
test('equal requests have same hashCode', () {
|
|
const a = NextInSeriesRequest(
|
|
serverId: 's1',
|
|
feedUrl: '/feed',
|
|
currentEntryId: 'ch1',
|
|
);
|
|
const b = NextInSeriesRequest(
|
|
serverId: 's1',
|
|
feedUrl: '/feed',
|
|
currentEntryId: 'ch1',
|
|
);
|
|
|
|
expect(a, equals(b));
|
|
expect(a.hashCode, equals(b.hashCode));
|
|
});
|
|
|
|
test('different requests are not equal', () {
|
|
const a = NextInSeriesRequest(
|
|
serverId: 's1',
|
|
feedUrl: '/feed',
|
|
currentEntryId: 'ch1',
|
|
);
|
|
const b = NextInSeriesRequest(
|
|
serverId: 's1',
|
|
feedUrl: '/feed',
|
|
currentEntryId: 'ch2',
|
|
);
|
|
|
|
expect(a, isNot(equals(b)));
|
|
});
|
|
});
|
|
}
|