File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -3,12 +3,12 @@ import "server-only";
33import { api } from "@repo/backend/convex/_generated/api" ;
44import { fetchQuery } from "convex/nextjs" ;
55import { Effect , Schema } from "effect" ;
6- import { cacheLife } from "next/cache" ;
76import type { Locale } from "next-intl" ;
87import type {
98 ActiveLearningProfile ,
109 LearningProgramCatalog ,
1110} from "@/components/programs/contract" ;
11+ import { applyContentRuntimeCache } from "@/lib/content/cache" ;
1212
1313/** Expected failure while reading the current user's learning profile. */
1414class ActiveLearningProfileReadError extends Schema . TaggedError < ActiveLearningProfileReadError > ( ) (
@@ -47,7 +47,7 @@ export async function getLearningProgramCatalog(
4747 locale : Locale
4848) : Promise < LearningProgramCatalog > {
4949 "use cache" ;
50- cacheLife ( "contentRuntime" ) ;
50+ applyContentRuntimeCache ( ) ;
5151
5252 return await fetchQuery ( api . learningPrograms . queries . listSelectablePrograms , {
5353 locale,
Original file line number Diff line number Diff line change 1+ import {
2+ preservedProgramCatalogTableNames ,
3+ resettableTableNames ,
4+ } from "@repo/backend/convex/contentSync/reset/spec" ;
5+ import { describe , expect , it } from "vitest" ;
6+
7+ describe ( "contentSync/reset spec" , ( ) => {
8+ it ( "resets derived coverage while preserving referenced program catalog rows" , ( ) => {
9+ expect ( resettableTableNames ) . toContain ( "learningProgramCoverage" ) ;
10+ expect ( resettableTableNames ) . not . toContain ( "learningPrograms" ) ;
11+ expect ( resettableTableNames ) . not . toContain ( "learningProgramSources" ) ;
12+ expect ( preservedProgramCatalogTableNames ) . toEqual ( [
13+ "learningPrograms" ,
14+ "learningProgramSources" ,
15+ ] ) ;
16+ } ) ;
17+ } ) ;
Original file line number Diff line number Diff line change @@ -59,3 +59,17 @@ export const resettableTableNames = [
5959] as const satisfies readonly TableNames [ ] ;
6060
6161export type ResettableTableName = ( typeof resettableTableNames ) [ number ] ;
62+
63+ /**
64+ * Program catalog rows are sync-managed but intentionally preserved by the broad
65+ * content reset because learning profiles, plans, and plan items store generated
66+ * program document IDs. Coverage rows are derived from content routes and remain
67+ * reset-managed because they can be rebuilt without orphaning user state.
68+ */
69+ export const preservedProgramCatalogTableNames = [
70+ "learningPrograms" ,
71+ "learningProgramSources" ,
72+ ] as const satisfies readonly TableNames [ ] ;
73+
74+ export type PreservedProgramCatalogTableName =
75+ ( typeof preservedProgramCatalogTableNames ) [ number ] ;
Original file line number Diff line number Diff line change @@ -146,7 +146,27 @@ describe("sync-content reset", () => {
146146 expect ( log ) . toHaveBeenCalledWith ( " Quran Verses: 0" ) ;
147147 expect ( log ) . toHaveBeenCalledWith ( " Total derived items: 0" ) ;
148148 expect ( logSuccess ) . toHaveBeenCalledWith (
149- "\nDatabase is already empty. Nothing to delete."
149+ "\nReset-managed content is already empty. Nothing to delete."
150+ ) ;
151+ expect ( log ) . not . toHaveBeenCalledWith ( "\nTo delete all content, run:" ) ;
152+ } ) ;
153+
154+ it ( "preserves program catalog rows without treating them as reset-managed content" , async ( ) => {
155+ vi . mocked ( getContentCounts ) . mockReturnValue (
156+ Effect . succeed ( {
157+ ...emptyCounts ,
158+ learningProgramSources : 5 ,
159+ learningPrograms : 4 ,
160+ } )
161+ ) ;
162+
163+ await Effect . runPromise ( reset ( config , { force : false } ) ) ;
164+
165+ expect ( log ) . toHaveBeenCalledWith ( " Learning Programs: 4" ) ;
166+ expect ( log ) . toHaveBeenCalledWith ( " Learning Program Srcs: 5" ) ;
167+ expect ( log ) . toHaveBeenCalledWith ( " Preserved program catalog rows: 9" ) ;
168+ expect ( logSuccess ) . toHaveBeenCalledWith (
169+ "\nReset-managed content is already empty. Nothing to delete."
150170 ) ;
151171 expect ( log ) . not . toHaveBeenCalledWith ( "\nTo delete all content, run:" ) ;
152172 } ) ;
Original file line number Diff line number Diff line change @@ -446,22 +446,22 @@ export const reset = Effect.fn("sync.reset")(function* (
446446 counts . quranVerses +
447447 counts . audioContentSources +
448448 counts . contentAudios ;
449+ const preservedProgramCatalog =
450+ counts . learningPrograms + counts . learningProgramSources ;
449451
450452 log ( `\n Total content items: ${ totalContent } ` ) ;
451453 log ( ` Total related items: ${ totalRelated } ` ) ;
452454 log ( ` Total runtime items: ${ totalRuntime } ` ) ;
453455 log ( ` Total derived items: ${ totalDerived } ` ) ;
454- log (
455- ` Program catalog rows: ${ counts . learningPrograms + counts . learningProgramSources } `
456- ) ;
456+ log ( ` Preserved program catalog rows: ${ preservedProgramCatalog } ` ) ;
457457
458458 if (
459459 totalContent === 0 &&
460460 totalRelated === 0 &&
461461 totalRuntime === 0 &&
462462 totalDerived === 0
463463 ) {
464- logSuccess ( "\nDatabase is already empty. Nothing to delete." ) ;
464+ logSuccess ( "\nReset-managed content is already empty. Nothing to delete." ) ;
465465 return ;
466466 }
467467
You can’t perform that action at this time.
0 commit comments