@@ -5,6 +5,7 @@ const mockAnd = jest.fn();
55const mockFindOne = jest . fn ( ) ;
66const mockQueryLean = jest . fn ( ) . mockResolvedValue ( [ ] ) ;
77const mockPopulate = jest . fn ( ) ;
8+ const mockEnginePopulate = jest . fn ( ) ;
89
910// mockAnd returns an object with .lean() so features.query.lean() works after .and()
1011mockAnd . mockReturnValue ( { lean : mockQueryLean } ) ;
@@ -14,7 +15,7 @@ const mockQueryEngine = jest.fn((query) => {
1415 query,
1516 filter ( ) { return engine ; } ,
1617 sort ( ) { return engine ; } ,
17- populate ( ) { return engine ; } ,
18+ populate ( ) { mockEnginePopulate ( ... arguments ) ; return engine ; } ,
1819 paginate ( ) { return engine ; } ,
1920 } ;
2021 return engine ;
@@ -38,13 +39,14 @@ jest.mock('@urbackend/common', () => ({
3839 } ,
3940 findOne : ( ...args ) => {
4041 mockFindOne ( ...args ) ;
41- return {
42+ const chainable = {
4243 populate : ( ...pArgs ) => {
4344 mockPopulate ( ...pArgs ) ;
44- return { lean : jest . fn ( ) . mockResolvedValue ( { _id : 'doc_1' } ) } ;
45+ return chainable ;
4546 } ,
46- lean : jest . fn ( ) . mockResolvedValue ( { _id : 'doc_1' } )
47+ lean : jest . fn ( ) . mockResolvedValue ( { _id : 'doc_1' } ) ,
4748 } ;
49+ return chainable ;
4850 } ,
4951 } ) ) ,
5052 QueryEngine : mockQueryEngine ,
@@ -89,6 +91,7 @@ function makeRes() {
8991describe ( 'data.controller read RLS filters' , ( ) => {
9092 beforeEach ( ( ) => {
9193 jest . clearAllMocks ( ) ;
94+ mockEnginePopulate . mockClear ( ) ;
9295 } ) ;
9396
9497 test ( 'getAllData applies rlsFilter to find()' , async ( ) => {
@@ -117,16 +120,28 @@ describe('data.controller read RLS filters', () => {
117120 expect ( res . json ) . toHaveBeenCalled ( ) ;
118121 } ) ;
119122
120- test ( 'getAllData passes populate param to QueryEngine ' , async ( ) => {
123+ test ( 'getAllData calls engine. populate() when populate param is provided ' , async ( ) => {
121124 const req = makeReq ( { query : { populate : 'author,comments' } } ) ;
122125 const res = makeRes ( ) ;
123126
124- // The real QueryEngine (not mocked) would call .populate() on the query
125- // But since we mock QueryEngine, we just check if populate() was part of the chain
126- // In this specific test file, mockQueryEngine is what's used.
127127 await getAllData ( req , res ) ;
128-
128+
129129 expect ( mockQueryEngine ) . toHaveBeenCalled ( ) ;
130+ expect ( mockEnginePopulate ) . toHaveBeenCalled ( ) ;
131+ } ) ;
132+
133+ test ( 'getAllData does not forward populate/expand to Mongo filter' , async ( ) => {
134+ const req = makeReq ( { query : { populate : 'author' , expand : 'category' , title : 'hello' } } ) ;
135+ const res = makeRes ( ) ;
136+
137+ await getAllData ( req , res ) ;
138+
139+ // mockFind is called with the raw Mongoose query (no args for Model.find())
140+ // The real filter exclusion is tested via the QueryEngine directly,
141+ // but we confirm find() was invoked and the request did not error out.
142+ expect ( mockFind ) . toHaveBeenCalledWith ( ) ;
143+ expect ( res . json ) . toHaveBeenCalled ( ) ;
144+ expect ( res . status ) . not . toHaveBeenCalledWith ( 500 ) ;
130145 } ) ;
131146
132147 test ( 'getSingleDoc calls populate on the query' , async ( ) => {
@@ -138,4 +153,16 @@ describe('data.controller read RLS filters', () => {
138153 expect ( mockPopulate ) . toHaveBeenCalledWith ( 'author' ) ;
139154 expect ( res . json ) . toHaveBeenCalled ( ) ;
140155 } ) ;
156+
157+ test ( 'getSingleDoc handles array-format populate param without crashing' , async ( ) => {
158+ const req = makeReq ( { query : { populate : [ 'author' , 'category' ] } } ) ;
159+ const res = makeRes ( ) ;
160+
161+ await getSingleDoc ( req , res ) ;
162+
163+ expect ( mockPopulate ) . toHaveBeenCalledWith ( 'author' ) ;
164+ expect ( mockPopulate ) . toHaveBeenCalledWith ( 'category' ) ;
165+ expect ( res . json ) . toHaveBeenCalled ( ) ;
166+ expect ( res . status ) . not . toHaveBeenCalledWith ( 500 ) ;
167+ } ) ;
141168} ) ;
0 commit comments