Sprint 01: bug fixes + suite TDD de integración (69 tests)#4
Merged
Conversation
Resuelve 8 bugs detectados por verificación end-to-end del Sprint 00 contra
la BD de Supabase real, más establece una suite de tests automatizada
(69 tests, 11 suites, ~2.5min) que valida lógica pura + integración.
Bugs cerrados:
- F-01: print() en producción → envueltos en #if DEBUG (3 archivos)
- F-03: +10 puntos al verificar bloqueados por RLS → migration 002
(policy "Moderators update any profile" en public.profiles)
- F-04: votes_count no se incrementa → migration 003
(trigger SQL bump_votes_count con SECURITY DEFINER)
- F-05: notif de comentario no se inserta → trigger SQL
trg_comment_notify en public.comments (SECURITY DEFINER)
- F-06: select("*, author:profiles(*)") ambiguo después de
añadir FK verified_by → embed explícito
profiles!exercises_author_id_fkey en SupabaseExerciseRepository
- F-07: joinDuel falla por policy → migration 004
(split "Players update active duels" + "Anyone joins waiting duels")
- F-09: deleteAccount no borra profile → migration 005
(policy "Users delete own profile")
- F-10: realtime de notifications no entrega callbacks →
UUID lowercase en filter + JSONDecoder ISO8601 custom para timestamps
con fracciones de segundo.
Bonus: arreglado bug donde DuelLobbyView.startBotDuel creaba el
DuelViewModel pero nunca invocaba vm.startBotDuel(category:), dejando
el VM en phase=.lobby y la pantalla aparentemente "en blanco".
Migraciones SQL aplicadas en la BD remota (Docs/migrations/{002..005}.sql)
y reflejadas en supabase_schema.sql.
Tests:
- 22 unit (SM-2, UserLevel, ChessPiece)
- 47 integration (Auth, Exercise, Community, UserProfile, Duel, Realtime)
- TestSupabase / TestUserFactory / ResultExt como infraestructura
- TestSecrets.swift permanece local (gitignored) con service_role + PAT
- Inject AppState.authService into DuelViewModel for bot duels (was using separate local AuthService via DependencyContainer, so points never reached the SupabaseAuthService that ProfileView observes). - Award reputation in finishDuel (+20 win, +5 loss/tie). - Add duelWon/duelLost/tournamentWon constants. - Make DuelViewModel Identifiable for fullScreenCover(item:). - ActiveDuelView: @ObservedObject + dark color scheme so text is visible. - DuelResultView: 'Volver' button uses dmPrimary background + white text.
…lution Multiplayer: - Add subscribeToDuel/fetchDuel to DuelRepository (Supabase + Local). - DuelLobbyView: creator now enters a waiting room with realtime sub to the duels row. When opponent joins (status=active, player2_id set) both clients launch ActiveDuelView in sync. - DuelViewModel: subscribe to duels row in vs-player mode so we detect remote finish (status=finished, winner_id) and mirror server scores to recover from missed question events. Chess piece overhaul: - Switch from lead-based to absolute progression so a tied correct doesn't downgrade you. - Track per-question answer times. Award piece-progress only to the correct AND faster answer (tie awards both). Slower correct answer still counts in raw myScore but skips piece evolution. - updateChessPieces() runs once per question in advanceQuestion, not on each submit — kills the evolve/downgrade flicker.
…ency filter - createPrivateDuel now sets lobbyMode=.matchmaking immediately so UI shows the spinner instead of freezing on main menu during network call. - findWaitingDuel filters created_at > now-60s to avoid joining stale abandoned duels from test users.
…y UI Scoring (Kahoot-style): - myScore += timeRemaining when answering correctly (0–15 pts per question). - Bot: pts = 15 - floor(botDelay). Realtime: pts = 15 - floor(opTimeMs/1000). - Tie is now near-impossible (requires identical ms-precision timing). - Added myCorrectCount/opponentCorrectCount separate from pts score. Result screen: - New 'EMPATE' state with blue badge and 'equal.circle.fill' icon. - Stats row: Correctas | Pts velocidad | Reputación ganada. - Subtitle adapts to win/tie/loss. Active duel UI: - Header shows '(Tú)' badge next to your name, green label. - Header shows pts score live during duel. - Piece board shows player name under each piece.
Scoring: - myScore += max(0, 15000 - elapsedMs) per correct answer. - Bot: pts = max(0, 15000 - botDelay*1000). - Realtime: pts = max(0, 15000 - opponentTimeMs). - Max 15000 pts/question × 10 = 150000 total. Ties now nearly impossible. Display fix: - DuelResultView subtitle now uses myCorrectCount (count of correct answers) instead of myScore (which is now ms-based pts). The 'Resolviste N ejercicios' text no longer shows 1 when 10 were correct.
…tion - Updated test cases to replace `piece(for:)` with `piece(forCorrect:)` for consistency in lead evaluations. - Ensured that all lead scenarios (negative, zero, positive) correctly utilize the new method. - Maintained the integrity of the tests while improving clarity and accuracy in lead handling.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sprint 01 cierra 8 bugs detectados por verificación end-to-end del Sprint 00 contra la BD de Supabase real, y establece una suite de 69 tests automatizada (22 unit + 47 integration) que valida lógica pura y flujos completos contra el proyecto Supabase del equipo.
Bugs cerrados (productivos)
print()activos enModules/Core/Utils/NotificationService.swift,Modules/Data/Local/LocalStore.swift,Modules/Core/Wrappers/OTPServiceProtocol.swift#if DEBUG ... #endifawardPointsTodel moderator al verificar ejercicio no actualizapointsdel autor (RLS rechaza UPDATE silencioso)002: policyModerators update any profileexercises.votes_countsiempre 0 al votar (no había trigger)003: triggerbump_votes_count(SECURITY DEFINER)trg_comment_notifyenpublic.commentsque la crea server-sideselect("*, author:profiles(*)")ambiguo después de añadir FKverified_by— feed Explore devolvía error PGRST201 en producciónprofiles!exercises_author_id_fkeyen 6 sitios deSupabaseExerciseRepository.swiftjoinDuellanzabaPGRST116porque la policy exigía ser ya player1/player2 antes de unirse004: split enPlayers update active duels+Anyone joins waiting duelsdeleteAccountno borraba la fila deprofiles(no había policy DELETE)005: policyUsers delete own profilenotificationsnunca entregaba callbackslowercased()+JSONDecodercondateDecodingStrategycustom para timestamps ISO8601 con fracciones de segundoExtra
DuelLobbyView.startBotDuel()creaba elDuelViewModelpero nunca invocabavm.startBotDuel(category:), dejando el VM enphase=.lobbyy la pantalla aparentemente en blanco. Ahora dispara el countdown → questions correctamente.Migraciones SQL aplicadas
Las migraciones
002..005ya están aplicadas en la BD remota vía Management API.supabase_schema.sqlestá actualizado para reflejar el estado declarativo.001_disable_rls.sqlqueda como referencia histórica — no se aplicó (preferimos policies específicas).Si recreás el proyecto desde cero, basta correr
supabase_schema.sqlpara tener el schema con todas las policies + el triggertrg_comment_notify.Test plan
tuist generate --no-open && xcodebuild test -workspace dailymath.xcworkspace -scheme dailymath -destination 'id=<iPhone17Pro-26.2>'→ 69/69 PASS en ~2.5minxcodebuild build ...→BUILD SUCCEEDEDNotas operativas para reviewer
TestSecrets.swiftcontieneservice_roleypersonal access tokeny queda gitignored — cada dev configura el suyo localmente.test_<uuid>@dailymath.testy limpieza en tearDown (decisión documentada del Sprint 00).CD85FB23-...). Usarxcodebuilddirecto, notuist xcodebuild(workaround documentado en F-02 del Sprint 00).🤖 Generated with Claude Code