@@ -136,79 +136,23 @@ function makeSearchDigest(overrides: Record<string, unknown> = {}) {
136136}
137137
138138function makeOfficialFirstCategoryCtx (
139- curatedDigests : Array < ReturnType < typeof makeSearchDigest > > ,
140- officialBadgePages : Array < Array < { skillId : string } > > = [
141- curatedDigests . map ( ( digest ) => ( { skillId : digest . skillId } ) ) ,
142- ] ,
143139 publicDigestPages : Array < {
144140 page : Array < ReturnType < typeof makeSearchDigest > > ;
145141 hasMore : boolean ;
146142 indexKeys : unknown [ ] [ ] ;
147- } > = [ ] ,
143+ } > ,
148144) {
149- const digestsBySkillId = new Map ( curatedDigests . map ( ( digest ) => [ digest . skillId , digest ] ) ) ;
150145 let publicDigestPageIndex = 0 ;
151- getPageMock . mockImplementation (
152- async (
153- _ctx : unknown ,
154- request : {
155- table : string ;
156- endIndexKey ?: unknown [ ] ;
157- startIndexKey ?: unknown [ ] ;
158- } ,
159- ) => {
160- if ( request . table !== "skillBadges" ) {
161- return (
162- publicDigestPages [ publicDigestPageIndex ++ ] ?? {
163- page : [ ] ,
164- hasMore : false ,
165- indexKeys : [ ] ,
166- }
167- ) ;
146+ getPageMock . mockImplementation ( async ( ) => {
147+ return (
148+ publicDigestPages [ publicDigestPageIndex ++ ] ?? {
149+ page : [ ] ,
150+ hasMore : false ,
151+ indexKeys : [ ] ,
168152 }
169- const kind = request . endIndexKey ?. [ 0 ] ;
170- const pageIndex =
171- request . startIndexKey && request . startIndexKey . length > 1
172- ? Number ( request . startIndexKey [ 1 ] ) + 1
173- : 0 ;
174- const pages = kind === "official" ? officialBadgePages : [ [ ] ] ;
175- const page = pages [ pageIndex ] ?? [ ] ;
176- return {
177- page,
178- hasMore : pageIndex < pages . length - 1 ,
179- indexKeys : page . map ( ( badge , index ) => [ kind , pageIndex , index , badge . skillId ] ) ,
180- } ;
181- } ,
182- ) ;
183- return {
184- db : {
185- query : vi . fn ( ( table : string ) => {
186- if ( table === "skillSearchDigest" ) {
187- return {
188- withIndex : vi . fn (
189- (
190- _indexName : string ,
191- builder : ( q : { eq : ( field : string , value : string ) => unknown } ) => unknown ,
192- ) => {
193- let skillId = "" ;
194- const queryBuilder = {
195- eq : ( _field : string , value : string ) => {
196- skillId = value ;
197- return queryBuilder ;
198- } ,
199- } ;
200- builder ( queryBuilder ) ;
201- return {
202- unique : vi . fn ( ) . mockResolvedValue ( digestsBySkillId . get ( skillId ) ?? null ) ,
203- } ;
204- } ,
205- ) ,
206- } ;
207- }
208- throw new Error ( `Unexpected table: ${ table } ` ) ;
209- } ) ,
210- } ,
211- } ;
153+ ) ;
154+ } ) ;
155+ return { db : { query : vi . fn ( ) } } ;
212156}
213157
214158function legacyCursor ( key : unknown [ ] ) : string {
@@ -522,7 +466,15 @@ describe("public skill list deterministic cursors", () => {
522466 primaryCategory : "dev-tools" ,
523467 updatedAt : 2 ,
524468 } ) ;
525- const ctx = makeOfficialFirstCategoryCtx ( [ official ] , undefined , [
469+ const ctx = makeOfficialFirstCategoryCtx ( [
470+ {
471+ page : [ community , official ] ,
472+ hasMore : false ,
473+ indexKeys : [
474+ [ undefined , 2 , 2 , "skillSearchDigest:community-dev" ] ,
475+ [ undefined , 1 , 1 , "skillSearchDigest:official-dev" ] ,
476+ ] ,
477+ } ,
526478 {
527479 page : [ community , official ] ,
528480 hasMore : false ,
@@ -580,7 +532,7 @@ describe("public skill list deterministic cursors", () => {
580532 expect ( third . nextCursor ) . toBeNull ( ) ;
581533 } ) ;
582534
583- it ( "loads older curated badges beyond the first badge page " , async ( ) => {
535+ it ( "continues the bounded curated scan before community fallback " , async ( ) => {
584536 const official = makeSearchDigest ( {
585537 skillId : "skills:older-official-dev" ,
586538 slug : "older-official-dev" ,
@@ -596,17 +548,23 @@ describe("public skill list deterministic cursors", () => {
596548 primaryCategory : "dev-tools" ,
597549 updatedAt : 2 ,
598550 } ) ;
599- const ctx = makeOfficialFirstCategoryCtx (
600- [ official ] ,
601- [ [ { skillId : "skills:unrelated-newer-official" } ] , [ { skillId : official . skillId } ] ] ,
602- [
603- {
604- page : [ community ] ,
605- hasMore : false ,
606- indexKeys : [ [ undefined , 2 , 2 , "skillSearchDigest:newer-community-dev" ] ] ,
607- } ,
608- ] ,
609- ) ;
551+ const ctx = makeOfficialFirstCategoryCtx ( [
552+ {
553+ page : [ community ] ,
554+ hasMore : true ,
555+ indexKeys : [ [ undefined , 2 , 2 , "skillSearchDigest:newer-community-dev" ] ] ,
556+ } ,
557+ {
558+ page : [ official ] ,
559+ hasMore : false ,
560+ indexKeys : [ [ undefined , 1 , 1 , "skillSearchDigest:older-official-dev" ] ] ,
561+ } ,
562+ {
563+ page : [ community ] ,
564+ hasMore : false ,
565+ indexKeys : [ [ undefined , 2 , 2 , "skillSearchDigest:newer-community-dev" ] ] ,
566+ } ,
567+ ] ) ;
610568
611569 const result = await listPublicPageV4Handler ( ctx as never , {
612570 categorySlug : "dev-tools" ,
@@ -620,6 +578,64 @@ describe("public skill list deterministic cursors", () => {
620578 ) . toEqual ( [ "older-official-dev" ] ) ;
621579 } ) ;
622580
581+ it ( "resumes the curated phase after reaching the per-request scan budget" , async ( ) => {
582+ const official = makeSearchDigest ( {
583+ skillId : "skills:older-official-dev" ,
584+ slug : "older-official-dev" ,
585+ displayName : "Older Official Dev" ,
586+ primaryCategory : "dev-tools" ,
587+ badges : { official : { byUserId : "users:admin" , at : 1 } } ,
588+ updatedAt : 1 ,
589+ } ) ;
590+ const noisePages = Array . from ( { length : 4 } , ( _ , index ) => {
591+ const updatedAt = 10 - index ;
592+ return {
593+ page : [
594+ makeSearchDigest ( {
595+ skillId : `skills:community-${ updatedAt } ` ,
596+ slug : `community-${ updatedAt } ` ,
597+ displayName : `Community ${ updatedAt } ` ,
598+ primaryCategory : "dev-tools" ,
599+ updatedAt,
600+ } ) ,
601+ ] ,
602+ hasMore : true ,
603+ indexKeys : [ [ undefined , updatedAt , updatedAt , `skillSearchDigest:community-${ updatedAt } ` ] ] ,
604+ } ;
605+ } ) ;
606+ const ctx = makeOfficialFirstCategoryCtx ( [
607+ ...noisePages ,
608+ {
609+ page : [ official ] ,
610+ hasMore : false ,
611+ indexKeys : [ [ undefined , 1 , 1 , "skillSearchDigest:older-official-dev" ] ] ,
612+ } ,
613+ { page : [ ] , hasMore : false , indexKeys : [ ] } ,
614+ ] ) ;
615+
616+ const first = await listPublicPageV4Handler ( ctx as never , {
617+ categorySlug : "dev-tools" ,
618+ officialFirst : true ,
619+ numItems : 1 ,
620+ sort : "updated" ,
621+ } ) ;
622+ expect ( first . page ) . toEqual ( [ ] ) ;
623+ expect ( first . hasMore ) . toBe ( true ) ;
624+ expect ( first . nextCursor ) . toContain ( "skillofficialfirst:" ) ;
625+
626+ const second = await listPublicPageV4Handler ( ctx as never , {
627+ cursor : first . nextCursor ! ,
628+ categorySlug : "dev-tools" ,
629+ officialFirst : true ,
630+ numItems : 1 ,
631+ sort : "updated" ,
632+ } ) ;
633+ expect (
634+ ( second . page as Array < { skill : { slug : string } } > ) . map ( ( entry ) => entry . skill . slug ) ,
635+ ) . toEqual ( [ "older-official-dev" ] ) ;
636+ expect ( second . hasMore ) . toBe ( false ) ;
637+ } ) ;
638+
623639 it ( "does not advertise an empty community page after a full curated category page" , async ( ) => {
624640 const official = makeSearchDigest ( {
625641 skillId : "skills:official-dev" ,
@@ -629,7 +645,12 @@ describe("public skill list deterministic cursors", () => {
629645 badges : { official : { byUserId : "users:admin" , at : 1 } } ,
630646 updatedAt : 1 ,
631647 } ) ;
632- const ctx = makeOfficialFirstCategoryCtx ( [ official ] , undefined , [
648+ const ctx = makeOfficialFirstCategoryCtx ( [
649+ {
650+ page : [ official ] ,
651+ hasMore : false ,
652+ indexKeys : [ [ undefined , 1 , 1 , "skillSearchDigest:official-dev" ] ] ,
653+ } ,
633654 {
634655 page : [ official ] ,
635656 hasMore : false ,
0 commit comments