import 'package:adhd_scheduler_core/scheduler_core.dart'; import 'package:test/test.dart'; void main() { group('Persistence index contract', () { test('index names and key fields are stable and adapter-neutral', () { final names = {}; for (final spec in PersistenceIndexCatalog.all) { expect(spec.name, isNotEmpty); expect(spec.name.trim(), spec.name); expect(spec.name.contains(' '), isFalse); expect(names.add(spec.name), isTrue, reason: spec.name); expect(spec.collection, isNotEmpty); expect(spec.keys, isNotEmpty); expect(spec.keys.first.fieldName, DocumentFields.ownerId); for (final key in spec.keys) { expect(key.fieldName, isNotEmpty); expect(key.fieldName.contains('.'), isFalse); } } }); test('every repository query has an index-backed access path', () { const requiredQueries = { RepositoryQueryNames.taskById, RepositoryQueryNames.taskByOwner, RepositoryQueryNames.taskByStatus, RepositoryQueryNames.taskByProject, RepositoryQueryNames.taskByParent, RepositoryQueryNames.taskBacklogCandidates, RepositoryQueryNames.taskScheduledWindow, RepositoryQueryNames.taskLocalDay, RepositoryQueryNames.projectByOwner, RepositoryQueryNames.projectById, RepositoryQueryNames.lockedBlockByOwner, RepositoryQueryNames.lockedBlockById, RepositoryQueryNames.lockedOverrideByDate, RepositoryQueryNames.taskActivityByOwner, RepositoryQueryNames.taskActivityByOperation, RepositoryQueryNames.taskActivityByTask, RepositoryQueryNames.taskActivityByProject, RepositoryQueryNames.projectStatisticsByProject, RepositoryQueryNames.ownerSettingsByOwner, RepositoryQueryNames.noticeAcknowledgementByOwner, RepositoryQueryNames.noticeAcknowledgementById, RepositoryQueryNames.operationByIdempotencyKey, RepositoryQueryNames.schedulingSnapshotById, RepositoryQueryNames.schedulingSnapshotWindow, }; expect(PersistenceIndexCatalog.supportedQueries, containsAll(requiredQueries)); }); test('uniqueness and archive/partial index requirements are explicit', () { expect( uniqueIndexNames, containsAll({ 'tasks_owner_id_unique', 'projects_owner_id_unique', 'project_stats_owner_project_unique', 'locked_blocks_owner_id_unique', 'locked_overrides_owner_id_unique', 'task_activities_owner_id_unique', 'owner_settings_owner_unique', 'notice_ack_owner_notice_unique', 'operations_owner_operation_unique', 'snapshots_owner_id_unique', }), ); expect( indexByName('projects_owner_archived_name').keys.map( (key) => key.fieldName, ), contains(ProjectDocumentFields.archivedAt)); expect( indexByName('locked_blocks_owner_archived_name').keys.map( (key) => key.fieldName, ), contains(LockedBlockDocumentFields.archivedAt)); expect( indexByName('tasks_owner_scheduled_window').partialFilterFields, containsAll({ TaskDocumentFields.scheduledStart, TaskDocumentFields.scheduledEnd, }), ); expect( indexByName('tasks_owner_parent_status').partialFilterFields, contains(TaskDocumentFields.parentTaskId), ); }); test('collection retention does not target authoritative state', () { expect( PersistenceCollections.authoritative.intersection( PersistenceCollections.internalRetained, ), isEmpty, ); expect(PersistencePayloadLimits.taskActivityRetention.inDays, 365); expect(PersistencePayloadLimits.applicationOperationRetention.inDays, 90); expect(PersistencePayloadLimits.schedulingSnapshotRetention.inDays, 30); }); test('payload guards reject unbounded embedded growth', () { final metadata = { for (var index = 0; index <= PersistencePayloadLimits.maxTaskActivityMetadataEntries; index++) 'key-$index': index, }; final taskIds = List.generate( PersistencePayloadLimits.maxSchedulingSnapshotTasks + 1, (index) => 'task-$index', ); final appliedActivityIds = List.generate( PersistencePayloadLimits.maxAppliedActivityIds + 1, (index) => 'activity-$index', ); final metadataResult = PersistencePayloadGuard.mapEntries( fieldName: TaskActivityDocumentFields.metadata, value: metadata, limit: PersistencePayloadLimits.maxTaskActivityMetadataEntries, ); final snapshotResult = PersistencePayloadGuard.listLength( fieldName: SchedulingSnapshotDocumentFields.tasks, value: taskIds, limit: PersistencePayloadLimits.maxSchedulingSnapshotTasks, ); final activityIdsResult = PersistencePayloadGuard.listLength( fieldName: ProjectStatisticsDocumentFields.appliedActivityIds, value: appliedActivityIds, limit: PersistencePayloadLimits.maxAppliedActivityIds, ); expect(metadataResult.isValid, isFalse); expect(metadataResult.actual, metadata.length); expect(snapshotResult.isValid, isFalse); expect(snapshotResult.fieldName, SchedulingSnapshotDocumentFields.tasks); expect(activityIdsResult.isValid, isFalse); expect(activityIdsResult.limit, PersistencePayloadLimits.maxAppliedActivityIds); expect( PersistencePayloadGuard.listLength( fieldName: SchedulingSnapshotDocumentFields.notices, value: const [], limit: PersistencePayloadLimits.maxSchedulingSnapshotNotices, ).isValid, isTrue, ); }); }); } Set get uniqueIndexNames { return { for (final spec in PersistenceIndexCatalog.all) if (spec.unique) spec.name, }; } PersistenceIndexSpec indexByName(String name) { return PersistenceIndexCatalog.all.singleWhere((spec) => spec.name == name); }