test: colma i buchi negli unit test esistenti
- UpdateService.checkUpdate() coperto via HttpOverrides con fake HttpClient: versione più recente/uguale/più vecchia, status != 200, body malformato, errore di rete - ConverterProvider.loadDb() con l'asset reale + validazione che ogni voce di conversions.json abbia yield positivo - MealPlannerProvider.removeItem() sui rami colazione e cena (prima era coperto solo pranzo) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:biteplan/features/converter/providers/converter_provider.dart';
|
import 'package:biteplan/features/converter/providers/converter_provider.dart';
|
||||||
import 'package:biteplan/features/converter/models/conversion_entry.dart';
|
import 'package:biteplan/features/converter/models/conversion_entry.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
group('ConverterProvider', () {
|
group('ConverterProvider', () {
|
||||||
late ConverterProvider provider;
|
late ConverterProvider provider;
|
||||||
|
|
||||||
@@ -187,5 +191,45 @@ void main() {
|
|||||||
expect(notified, true);
|
expect(notified, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('loadDb()', () {
|
||||||
|
test('carica il database e la ricerca trova alimenti noti', () async {
|
||||||
|
await provider.loadDb();
|
||||||
|
provider.search('riso');
|
||||||
|
expect(provider.results, isNotEmpty);
|
||||||
|
provider.search('pollo');
|
||||||
|
expect(provider.results, isNotEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('la ricerca è case-insensitive', () async {
|
||||||
|
await provider.loadDb();
|
||||||
|
provider.search('RISO');
|
||||||
|
expect(provider.results, isNotEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('notifica i listener', () async {
|
||||||
|
var notified = false;
|
||||||
|
provider.addListener(() => notified = true);
|
||||||
|
await provider.loadDb();
|
||||||
|
expect(notified, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('conversions.json', () {
|
||||||
|
test('ogni metodo di ogni alimento ha uno yield positivo', () async {
|
||||||
|
final raw =
|
||||||
|
await rootBundle.loadString('assets/data/conversions.json');
|
||||||
|
final json = jsonDecode(raw) as Map<String, dynamic>;
|
||||||
|
expect(json, isNotEmpty);
|
||||||
|
json.forEach((food, methods) {
|
||||||
|
(methods as Map).forEach((method, data) {
|
||||||
|
final yield_ = (data as Map)['yield'];
|
||||||
|
expect(yield_, isA<num>(), reason: '$food/$method');
|
||||||
|
expect((yield_ as num).toDouble(), greaterThan(0),
|
||||||
|
reason: '$food/$method');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,24 @@ void main() {
|
|||||||
expect(provider.plan.days['lunedi']!.pranzo, ['pasta', 'frutta']);
|
expect(provider.plan.days['lunedi']!.pranzo, ['pasta', 'frutta']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('removeItem rimuove dallo slot colazione', () async {
|
||||||
|
final provider = MealPlannerProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.addItem('lunedi', 'colazione', 'latte');
|
||||||
|
provider.addItem('lunedi', 'colazione', 'caffè');
|
||||||
|
provider.removeItem('lunedi', 'colazione', 0);
|
||||||
|
expect(provider.plan.days['lunedi']!.colazione, ['caffè']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removeItem rimuove dallo slot cena', () async {
|
||||||
|
final provider = MealPlannerProvider();
|
||||||
|
await provider.load();
|
||||||
|
provider.addItem('lunedi', 'cena', 'pollo');
|
||||||
|
provider.addItem('lunedi', 'cena', 'verdure');
|
||||||
|
provider.removeItem('lunedi', 'cena', 1);
|
||||||
|
expect(provider.plan.days['lunedi']!.cena, ['pollo']);
|
||||||
|
});
|
||||||
|
|
||||||
test('clearAll svuota tutti i giorni', () async {
|
test('clearAll svuota tutti i giorni', () async {
|
||||||
final provider = MealPlannerProvider();
|
final provider = MealPlannerProvider();
|
||||||
await provider.load();
|
await provider.load();
|
||||||
|
|||||||
@@ -1,7 +1,50 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:biteplan/core/constants/app_constants.dart';
|
||||||
import 'package:biteplan/shared/services/update_service.dart';
|
import 'package:biteplan/shared/services/update_service.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
group('UpdateService.checkUpdate', () {
|
||||||
|
tearDown(() => HttpOverrides.global = null);
|
||||||
|
|
||||||
|
test('ritorna la versione quando il tag remoto è più recente', () async {
|
||||||
|
HttpOverrides.global =
|
||||||
|
_FakeHttpOverrides(body: '{"tag_name":"v99.0.0"}');
|
||||||
|
expect(await UpdateService.checkUpdate(), '99.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ritorna null quando il tag remoto è uguale alla versione corrente',
|
||||||
|
() async {
|
||||||
|
HttpOverrides.global =
|
||||||
|
_FakeHttpOverrides(body: '{"tag_name":"v$kAppVersion"}');
|
||||||
|
expect(await UpdateService.checkUpdate(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ritorna null quando il tag remoto è più vecchio', () async {
|
||||||
|
HttpOverrides.global = _FakeHttpOverrides(body: '{"tag_name":"v0.0.1"}');
|
||||||
|
expect(await UpdateService.checkUpdate(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ritorna null con status diverso da 200', () async {
|
||||||
|
HttpOverrides.global = _FakeHttpOverrides(
|
||||||
|
statusCode: 404, body: '{"message":"Not Found"}');
|
||||||
|
expect(await UpdateService.checkUpdate(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ritorna null con body malformato', () async {
|
||||||
|
HttpOverrides.global = _FakeHttpOverrides(body: 'non json');
|
||||||
|
expect(await UpdateService.checkUpdate(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ritorna null se la connessione fallisce', () async {
|
||||||
|
HttpOverrides.global = _FakeHttpOverrides(fail: true);
|
||||||
|
expect(await UpdateService.checkUpdate(), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('UpdateService.parseTagName', () {
|
group('UpdateService.parseTagName', () {
|
||||||
test('strips v prefix from tag_name', () {
|
test('strips v prefix from tag_name', () {
|
||||||
expect(UpdateService.parseTagName('{"tag_name":"v2.1.0"}'), '2.1.0');
|
expect(UpdateService.parseTagName('{"tag_name":"v2.1.0"}'), '2.1.0');
|
||||||
@@ -71,3 +114,66 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Fake HTTP per checkUpdate ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class _FakeHttpOverrides extends HttpOverrides {
|
||||||
|
_FakeHttpOverrides({this.statusCode = 200, this.body = '', this.fail = false});
|
||||||
|
final int statusCode;
|
||||||
|
final String body;
|
||||||
|
final bool fail;
|
||||||
|
|
||||||
|
@override
|
||||||
|
HttpClient createHttpClient(SecurityContext? context) =>
|
||||||
|
_FakeHttpClient(statusCode: statusCode, body: body, fail: fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeHttpClient extends Fake implements HttpClient {
|
||||||
|
_FakeHttpClient(
|
||||||
|
{required this.statusCode, required this.body, required this.fail});
|
||||||
|
final int statusCode;
|
||||||
|
final String body;
|
||||||
|
final bool fail;
|
||||||
|
|
||||||
|
@override
|
||||||
|
set connectionTimeout(Duration? value) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HttpClientRequest> getUrl(Uri url) async {
|
||||||
|
if (fail) throw const SocketException('rete assente');
|
||||||
|
return _FakeHttpClientRequest(statusCode: statusCode, body: body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close({bool force = false}) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeHttpClientRequest extends Fake implements HttpClientRequest {
|
||||||
|
_FakeHttpClientRequest({required this.statusCode, required this.body});
|
||||||
|
final int statusCode;
|
||||||
|
final String body;
|
||||||
|
|
||||||
|
@override
|
||||||
|
HttpHeaders get headers => _FakeHttpHeaders();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HttpClientResponse> close() async =>
|
||||||
|
_FakeHttpClientResponse(statusCode: statusCode, body: body);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeHttpHeaders extends Fake implements HttpHeaders {
|
||||||
|
@override
|
||||||
|
void set(String name, Object value, {bool preserveHeaderCase = false}) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeHttpClientResponse extends Fake implements HttpClientResponse {
|
||||||
|
_FakeHttpClientResponse({required this.statusCode, required this.body});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final int statusCode;
|
||||||
|
final String body;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) =>
|
||||||
|
Stream.value(utf8.encode(body)).transform(streamTransformer);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user