Coverage Summary for Class: SyncMeta (com.georgeci.moneysurfer.sync.repository)

Class Class, % Method, % Branch, % Line, % Instruction, %
SyncMeta 100% (1/1) 100% (1/1) 100% (6/6) 100% (27/27)


 package com.georgeci.moneysurfer.sync.repository
 
 import kotlinx.coroutines.flow.Flow
 import kotlin.time.Instant
 
 /**
  * Per-(scopeKey, collection) sync metadata.
  *
  * - [cursor] — `lastPulledAt` value used as `WHERE updatedAt > cursor` filter
  *   in the next pull. Updated only when remote rows are successfully written
  *   to local Room within the same transaction.
  * - [lastSyncSuccessAt] / [lastSyncAttemptAt] — timestamps for the UI badge.
  *
  * [scopeKey] is typically a workspace id string. [collection] is an opaque
  * Firestore collection name string (e.g. "accounts", "transactions").
  *
  * Lives in `sync:api` rather than next to its Room-backed implementation because
  * the Settings → Sync screen reads the cursors: "a cursor newer than the remote
  * change" is the answer to "why did the pull bring nothing", and a feature module
  * must not depend on `sync:default` to ask it.
  *
  * See SyncCoordinatorFAQ.md №14, sync.md §4.3.
  */
 interface SyncMetaRepository {
     suspend fun cursor(scopeKey: String, collection: String): Instant?
     suspend fun setCursor(scopeKey: String, collection: String, cursor: Instant)
     suspend fun markAttempt(scopeKey: String, collection: String, at: Instant)
     suspend fun markSuccess(scopeKey: String, collection: String, at: Instant)
     suspend fun clearScope(scopeKey: String)
     fun observe(scopeKey: String): Flow<List<SyncMeta>>
 }
 
 data class SyncMeta(
     val scopeKey: String,
     val collection: String,
     val lastPulledAt: Instant?,
     val lastSyncSuccessAt: Instant?,
     val lastSyncAttemptAt: Instant?,
 )