test(04-01): add failing tests for NotificationSettingsNotifier and NotificationService

- Tests for default state (enabled=false, time=07:00)
- Tests for setEnabled persistence to SharedPreferences
- Tests for setTime persistence to SharedPreferences
- Tests for loading persisted values via _load()
- Tests for NotificationService singleton pattern
- Tests for nextInstanceOf timezone logic (future/past time)
This commit is contained in:
2026-03-16 14:54:39 +01:00
parent 878767138c
commit 0f6789becd
2 changed files with 225 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:household_keeper/core/notifications/notification_service.dart';
void main() {
setUpAll(() {
tz.initializeTimeZones();
tz.setLocalLocation(tz.getLocation('Europe/Berlin'));
});
group('NotificationService', () {
test('singleton pattern: two instances are identical', () {
final a = NotificationService();
final b = NotificationService();
expect(identical(a, b), isTrue);
});
group('nextInstanceOf', () {
test('returns today when time is in the future', () {
final service = NotificationService();
final now = tz.TZDateTime.now(tz.local);
// Use a time 2 hours in the future, wrapping midnight if needed
final futureHour = (now.hour + 2) % 24;
final futureMinute = now.minute;
final futureTime = TimeOfDay(hour: futureHour, minute: futureMinute);
// Only test this case if futureHour > now.hour (no midnight wrap)
if (futureHour > now.hour) {
final result = service.nextInstanceOf(futureTime);
expect(result.day, now.day);
expect(result.hour, futureHour);
expect(result.minute, futureMinute);
}
});
test('returns tomorrow when time has passed', () {
final service = NotificationService();
final now = tz.TZDateTime.now(tz.local);
// Use a time in the past (1 hour ago), wrapping to previous day if needed
final pastHour = now.hour - 1;
// Only test if we are not at the beginning of the day
if (pastHour >= 0) {
final pastTime = TimeOfDay(hour: pastHour, minute: 0);
final result = service.nextInstanceOf(pastTime);
expect(result.day, now.day + 1);
expect(result.hour, pastHour);
expect(result.minute, 0);
}
});
test('scheduled time is in the future (always)', () {
final service = NotificationService();
final now = tz.TZDateTime.now(tz.local);
// Test with midnight (00:00) — always results in a time in future (tomorrow)
final result = service.nextInstanceOf(const TimeOfDay(hour: 0, minute: 0));
expect(result.isAfter(now) || result.isAtSameMomentAs(now), isTrue);
});
test('nextInstanceOf respects hours and minutes exactly', () {
final service = NotificationService();
final now = tz.TZDateTime.now(tz.local);
// Use a far future time today: 23:59, which should be today if we are before it
const targetTime = TimeOfDay(hour: 23, minute: 59);
final todayTarget = tz.TZDateTime(
tz.local,
now.year,
now.month,
now.day,
23,
59,
);
final result = service.nextInstanceOf(targetTime);
if (now.isBefore(todayTarget)) {
expect(result.hour, 23);
expect(result.minute, 59);
expect(result.day, now.day);
} else {
// After 23:59, scheduled for tomorrow
expect(result.hour, 23);
expect(result.minute, 59);
expect(result.day, now.day + 1);
}
});
});
});
}