@@ -198,6 +198,70 @@ describe('ObjectStackClient', () => {
198198 } ) ;
199199} ) ;
200200
201+ describe ( 'Reports namespace (#3587 gap closure)' , ( ) => {
202+ it ( 'reports.list pins GET /reports with filters and unwraps {data}' , async ( ) => {
203+ const { client, fetchMock } = createMockClient ( { data : [ { id : 'r1' } ] } ) ;
204+ const rows = await client . reports . list ( { object : 'lead' , ownerId : 'u1' } ) ;
205+ expect ( String ( fetchMock . mock . calls [ 0 ] [ 0 ] ) ) . toBe (
206+ 'http://localhost:3000/api/v1/reports?object=lead&ownerId=u1' ,
207+ ) ;
208+ expect ( rows ) . toEqual ( [ { id : 'r1' } ] ) ;
209+ } ) ;
210+
211+ it ( 'reports.save pins POST /reports' , async ( ) => {
212+ const { client, fetchMock } = createMockClient ( { id : 'r1' } ) ;
213+ await client . reports . save ( { name : 'Pipeline' , object : 'lead' } ) ;
214+ const [ url , init ] = fetchMock . mock . calls [ 0 ] ;
215+ expect ( String ( url ) ) . toBe ( 'http://localhost:3000/api/v1/reports' ) ;
216+ expect ( init . method ) . toBe ( 'POST' ) ;
217+ expect ( JSON . parse ( init . body ) ) . toEqual ( { name : 'Pipeline' , object : 'lead' } ) ;
218+ } ) ;
219+
220+ it ( 'reports.get / delete pin /reports/:id and delete tolerates 204' , async ( ) => {
221+ const { client, fetchMock } = createMockClient ( { id : 'r1' } ) ;
222+ await client . reports . get ( 'r1' ) ;
223+ expect ( String ( fetchMock . mock . calls [ 0 ] [ 0 ] ) ) . toBe ( 'http://localhost:3000/api/v1/reports/r1' ) ;
224+
225+ const del = createMockClient ( undefined , 204 ) ;
226+ // A 204 has no JSON body — the method must not try to parse one.
227+ del . fetchMock . mockResolvedValue ( { ok : true , status : 204 , statusText : 'No Content' , json : async ( ) => { throw new Error ( 'no body' ) ; } , headers : new Headers ( ) } ) ;
228+ const out = await del . client . reports . delete ( 'r1' ) ;
229+ expect ( String ( del . fetchMock . mock . calls [ 0 ] [ 0 ] ) ) . toBe ( 'http://localhost:3000/api/v1/reports/r1' ) ;
230+ expect ( del . fetchMock . mock . calls [ 0 ] [ 1 ] . method ) . toBe ( 'DELETE' ) ;
231+ expect ( out ) . toEqual ( { deleted : true } ) ;
232+ } ) ;
233+
234+ it ( 'reports.run pins POST /reports/:id/run' , async ( ) => {
235+ const { client, fetchMock } = createMockClient ( { rows : [ ] } ) ;
236+ await client . reports . run ( 'r1' ) ;
237+ const [ url , init ] = fetchMock . mock . calls [ 0 ] ;
238+ expect ( String ( url ) ) . toBe ( 'http://localhost:3000/api/v1/reports/r1/run' ) ;
239+ expect ( init . method ) . toBe ( 'POST' ) ;
240+ } ) ;
241+
242+ it ( 'reports.schedule pins POST /reports/:id/schedule with the schedule body' , async ( ) => {
243+ const { client, fetchMock } = createMockClient ( { id : 's1' } ) ;
244+ await client . reports . schedule ( 'r1' , { recipients : [ 'a@example.com' ] , cronExpression : '0 8 * * 1' } ) ;
245+ const [ url , init ] = fetchMock . mock . calls [ 0 ] ;
246+ expect ( String ( url ) ) . toBe ( 'http://localhost:3000/api/v1/reports/r1/schedule' ) ;
247+ expect ( JSON . parse ( init . body ) ) . toEqual ( { recipients : [ 'a@example.com' ] , cronExpression : '0 8 * * 1' } ) ;
248+ } ) ;
249+
250+ it ( 'reports.listSchedules / unschedule pin the schedule routes' , async ( ) => {
251+ const { client, fetchMock } = createMockClient ( { data : [ { id : 's1' } ] } ) ;
252+ const rows = await client . reports . listSchedules ( 'r1' ) ;
253+ expect ( String ( fetchMock . mock . calls [ 0 ] [ 0 ] ) ) . toBe ( 'http://localhost:3000/api/v1/reports/r1/schedules' ) ;
254+ expect ( rows ) . toEqual ( [ { id : 's1' } ] ) ;
255+
256+ const del = createMockClient ( undefined , 204 ) ;
257+ del . fetchMock . mockResolvedValue ( { ok : true , status : 204 , statusText : 'No Content' , json : async ( ) => { throw new Error ( 'no body' ) ; } , headers : new Headers ( ) } ) ;
258+ const out = await del . client . reports . unschedule ( 's1' ) ;
259+ expect ( String ( del . fetchMock . mock . calls [ 0 ] [ 0 ] ) ) . toBe ( 'http://localhost:3000/api/v1/reports/schedules/s1' ) ;
260+ expect ( del . fetchMock . mock . calls [ 0 ] [ 1 ] . method ) . toBe ( 'DELETE' ) ;
261+ expect ( out ) . toEqual ( { deleted : true } ) ;
262+ } ) ;
263+ } ) ;
264+
201265describe ( 'Approvals namespace (ADR-0019)' , ( ) => {
202266 it ( 'should list approval requests with filters' , async ( ) => {
203267 const { client, fetchMock } = createMockClient ( {
0 commit comments