import 'package:audio_service/audio_service.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/servicios/navegacion_auto.dart'; /// Android Auto entitlement gating — the id-shape matrix. /// /// REWRITTEN for fix/auto-quality-guidelines item 10. This suite used to /// assert the opposite design: that every non-root id, for a free-tier user, /// collapsed to a single non-playable `premium:info` row. Google Play cited /// that browse tree against the Android for Cars App Quality Guidelines, so /// the contract is now content-scoping — the free tier sees LESS, never a /// row that does nothing. /// /// [respuestaBloqueadaPorEntitlement]'s return VALUE is covered in /// `navegacion_auto_destacadas_test.dart`; this file pins the decision /// surface ([idPermitidoEnFree]) across every id shape the tree can produce, /// including the stale/deep-linked ones a head unit's cached tree replays. void main() { const gratuitas = [ Emisora(uuid: 'libre-1', nombre: 'Libre 1', url: 'https://libre1.example'), Emisora(uuid: 'libre-2', nombre: 'Libre 2', url: 'https://libre2.example'), ]; group('idPermitidoEnFree', () { test('la raíz siempre pasa: es lo único que decide qué ve el tier', () { expect( idPermitidoEnFree( AudioService.browsableRootId, destacadas: gratuitas, ), isTrue, ); }); test('la carpeta gratuita pasa', () { expect( idPermitidoEnFree( ConstructorArbolAuto.idDestacadas, destacadas: gratuitas, ), isTrue, ); }); test('un emisora: del set gratuito pasa', () { for (final e in gratuitas) { expect( idPermitidoEnFree('emisora:${e.uuid}', destacadas: gratuitas), isTrue, reason: e.uuid, ); } }); test('las carpetas premium NO pasan', () { for (final id in [ ConstructorArbolAuto.idFavoritos, ConstructorArbolAuto.idTodas, ConstructorArbolAuto.idMisEmisoras, ConstructorArbolAuto.idMusicaLocal, ConstructorArbolAuto.idEcualizador, ]) { expect(idPermitidoEnFree(id, destacadas: gratuitas), isFalse, reason: id); } }); test('un id rancio/deep-link de antes de una bajada de tier NO pasa: ' 'ésa es la propiedad de seguridad que el rediseño tenía que ' 'conservar', () { for (final id in [ 'emisora:uuid-del-catalogo', 'grupo:algun-grupo', 'pista:doc-id', 'carpeta_local:doc-id', 'carpeta_local_reproducir:doc-id', 'carpeta_local_aleatorio:doc-id', 'eq_preset:Rock', 'premium:info', // la fila muerta que ya no existe '', ]) { expect(idPermitidoEnFree(id, destacadas: gratuitas), isFalse, reason: id); } }); test('emisora: con uuid vacío NO pasa (id malformado, no comodín)', () { expect(idPermitidoEnFree('emisora:', destacadas: gratuitas), isFalse); }); test('con el set gratuito vacío solo pasan la raíz y su carpeta', () { expect( idPermitidoEnFree(AudioService.browsableRootId, destacadas: const []), isTrue, ); expect( idPermitidoEnFree( ConstructorArbolAuto.idDestacadas, destacadas: const [], ), isTrue, ); expect( idPermitidoEnFree('emisora:libre-1', destacadas: const []), isFalse, ); }); }); group('respuestaBloqueadaPorEntitlement', () { test('premium: ningún id se bloquea, ni siquiera uno inventado', () { for (final id in [ AudioService.browsableRootId, ConstructorArbolAuto.idTodas, 'emisora:cualquier-cosa', 'basura', ]) { expect( respuestaBloqueadaPorEntitlement( parentMediaId: id, premium: true, destacadas: gratuitas, ), isNull, reason: id, ); } }); test('free: lo bloqueado NUNCA incluye un item no reproducible', () { final bloqueada = respuestaBloqueadaPorEntitlement( parentMediaId: ConstructorArbolAuto.idMisEmisoras, premium: false, destacadas: gratuitas, ); expect(bloqueada, isNotNull); expect(bloqueada!.every((m) => m.playable == true), isTrue); }); }); }