import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as p; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/modelos/grupo_favoritos.dart'; import 'package:pluriwave/servicios/servicio_favoritos.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; void main() { late Directory tempDir; setUpAll(() { sqfliteFfiInit(); }); setUp(() async { tempDir = await Directory.systemTemp.createTemp('pluriwave_favoritos_'); }); tearDown(() async { if (await tempDir.exists()) { await tempDir.delete(recursive: true); } }); ServicioFavoritos crearServicio() { return ServicioFavoritos( databaseFactory: databaseFactoryFfi, databasePathProvider: () async => tempDir.path, ); } test( 'primera instalación crea esquema completo y guarda favoritos', () async { final servicio = crearServicio(); addTearDown(servicio.cerrar); await servicio.agregar(_emisora('radio-1', 'Radio Uno')); final favoritos = await servicio.obtenerTodos(); final grupos = await servicio.obtenerGrupos(); expect(favoritos, hasLength(1)); expect(favoritos.single.grupoFavoritosId, GrupoFavoritos.sinAsignarId); expect(grupos, hasLength(1)); expect(grupos.single.esSinAsignar, isTrue); }, ); test('migra esquema antiguo sin grupo ni columnas nuevas', () async { final dbPath = p.join(tempDir.path, 'pluriwave.db'); final db = await databaseFactoryFfi.openDatabase( dbPath, options: OpenDatabaseOptions( version: 1, onCreate: (db, version) async { await db.execute(''' CREATE TABLE favoritos ( id INTEGER PRIMARY KEY AUTOINCREMENT, uuid TEXT NOT NULL UNIQUE, nombre TEXT NOT NULL, url TEXT NOT NULL ) '''); await db.insert('favoritos', { 'uuid': 'legacy-1', 'nombre': 'Legacy Radio', 'url': 'https://example.com/legacy.mp3', }); }, ), ); await db.close(); final servicio = crearServicio(); addTearDown(servicio.cerrar); final favoritos = await servicio.obtenerTodos(); final grupos = await servicio.obtenerGrupos(); expect(favoritos, hasLength(1)); expect(favoritos.single.uuid, 'legacy-1'); expect(favoritos.single.grupoFavoritosId, GrupoFavoritos.sinAsignarId); expect(grupos.singleWhere((g) => g.esSinAsignar).nombre, 'Sin asignar'); final grupo = await servicio.crearGrupo('Viajes'); await servicio.asignarGrupo('legacy-1', grupo.id); expect((await servicio.obtenerTodos()).single.grupoFavoritosId, grupo.id); }); test('eliminar grupo reasigna sus favoritos a Sin asignar', () async { final servicio = crearServicio(); addTearDown(servicio.cerrar); await servicio.agregar(_emisora('radio-1', 'Radio Uno')); final grupo = await servicio.crearGrupo('Trabajo'); await servicio.asignarGrupo('radio-1', grupo.id); await servicio.eliminarGrupo(grupo.id); final favoritos = await servicio.obtenerTodos(); final grupos = await servicio.obtenerGrupos(); expect(favoritos.single.grupoFavoritosId, GrupoFavoritos.sinAsignarId); expect(grupos.any((g) => g.id == grupo.id), isFalse); }); // ── Primitivas de restauración (importarConfig) ──────────────────────────── // // `agregar` es la primitiva de "marcar como favorita": fuerza `sin_asignar` // y un `orden` nuevo a propósito, porque una emisora recién marcada no // pertenece a ningún grupo. Reusarla para RESTAURAR una copia de seguridad // destruía justo los dos campos que la copia traía. `restaurarFavorito` es // la primitiva que faltaba. test('restaurarFavorito preserva el grupo y el orden de la copia', () async { final servicio = crearServicio(); addTearDown(servicio.cerrar); await servicio.restaurarGrupo( const GrupoFavoritos(id: 'grupo-rock', nombre: 'Rock', orden: 7), ); await servicio.restaurarFavorito( _emisora( 'radio-1', 'Radio Uno', ).copyWith(orden: 42, grupoFavoritosId: 'grupo-rock'), ); final favoritos = await servicio.obtenerTodos(); expect(favoritos.single.grupoFavoritosId, 'grupo-rock'); expect(favoritos.single.orden, 42); }); test('restaurarFavorito cae a Sin asignar cuando el grupo de la copia no ' 'existe (copia editada a mano o restauración parcial)', () async { final servicio = crearServicio(); addTearDown(servicio.cerrar); await servicio.restaurarFavorito( _emisora( 'radio-1', 'Radio Uno', ).copyWith(orden: 3, grupoFavoritosId: 'grupo-fantasma'), ); final favoritos = await servicio.obtenerTodos(); expect(favoritos.single.grupoFavoritosId, GrupoFavoritos.sinAsignarId); expect(favoritos.single.orden, 3); }); test('restaurarGrupo hace upsert preservando id, nombre y orden, y NUNCA ' 'duplica el grupo protegido', () async { final servicio = crearServicio(); addTearDown(servicio.cerrar); await servicio.restaurarGrupo( const GrupoFavoritos(id: 'grupo-jazz', nombre: 'Jazz', orden: 5), ); // Segunda pasada (reimportar la misma copia): upsert, no duplicado. await servicio.restaurarGrupo( const GrupoFavoritos(id: 'grupo-jazz', nombre: 'Jazz renombrado', orden: 5), ); await servicio.restaurarGrupo( const GrupoFavoritos( id: GrupoFavoritos.sinAsignarId, nombre: 'Unassigned', orden: 0, protegido: true, ), ); final grupos = await servicio.obtenerGrupos(); final jazz = grupos.singleWhere((g) => g.id == 'grupo-jazz'); expect(jazz.nombre, 'Jazz renombrado'); expect(jazz.orden, 5); // El protegido conserva su nombre local: no se importa desde la copia. expect(grupos.singleWhere((g) => g.esSinAsignar).nombre, 'Sin asignar'); }); } Emisora _emisora(String uuid, String nombre) { return Emisora( uuid: uuid, nombre: nombre, url: 'https://example.com/$uuid.mp3', codec: 'MP3', bitrate: 192, ); }