@@ -1172,6 +1172,9 @@ describe('Planning operation contracts through the real foundation', () => {
11721172 expect ( result . status ) . toBe ( status )
11731173 expect ( result . result . success ) . toBe ( false )
11741174 expect ( result . result . output . updated ) . toBeUndefined ( )
1175+ expect ( mocks . fetch ) . toHaveBeenCalledTimes ( 1 )
1176+ }
1177+ )
11751178 it ( 'accepts only empty 204 as user-variable write confirmation' , async ( ) => {
11761179 const entry = CASES . find ( ( item ) => item . operation === 'set_user_variable_values' ) !
11771180 respond ( null , 204 )
@@ -1180,7 +1183,11 @@ describe('Planning operation contracts through the real foundation', () => {
11801183 expect ( ( await invoke ( entry . operation , entry . input ) ) . result . success ) . toBe ( false )
11811184 } )
11821185 it ( 'returns empty bounded lists without manufacturing completion flags' , async ( ) => {
1183- for ( const operation of [ 'list_user_variable_values' , 'list_planning_units' , 'get_planning_unit_history' ] ) {
1186+ for ( const operation of [
1187+ 'list_user_variable_values' ,
1188+ 'list_planning_units' ,
1189+ 'get_planning_unit_history' ,
1190+ ] ) {
11841191 const entry = CASES . find ( ( item ) => item . operation === operation ) !
11851192 respond ( { items : [ ] } )
11861193 const { result } = await invoke ( operation , entry . input )
@@ -1190,40 +1197,81 @@ describe('Planning operation contracts through the real foundation', () => {
11901197 }
11911198 } )
11921199 it ( 'rejects malformed user-variable values and oversized batches before requests' , async ( ) => {
1193- for ( const userVariableValues of [ [ ] , [ { name : 'CurrentEntity' } ] , Array ( 1001 ) . fill ( { userName : 'planner' , name : 'CurrentEntity' , dimension : 'Entity' , member : 'Marketing' } ) ] ) {
1194- expect ( ( await invoke ( 'set_user_variable_values' , { application : 'Vision' , userVariableValues } ) ) . status ) . toBe ( 400 )
1200+ for ( const userVariableValues of [
1201+ [ ] ,
1202+ [ { name : 'CurrentEntity' } ] ,
1203+ Array ( 1001 ) . fill ( {
1204+ userName : 'planner' ,
1205+ name : 'CurrentEntity' ,
1206+ dimension : 'Entity' ,
1207+ member : 'Marketing' ,
1208+ } ) ,
1209+ ] ) {
1210+ expect (
1211+ ( await invoke ( 'set_user_variable_values' , { application : 'Vision' , userVariableValues } ) ) . status
1212+ ) . toBe ( 400 )
11951213 }
11961214 expect ( mocks . fetch ) . not . toHaveBeenCalled ( )
11971215 } )
11981216 it ( 'preserves documented null planning-unit metadata and annotation sequences' , async ( ) => {
11991217 const units = CASES . find ( ( item ) => item . operation === 'list_planning_units' ) !
12001218 respond ( units . response )
12011219 expect ( ( await invoke ( units . operation , units . input ) ) . result . output . planningUnits ?. [ 0 ] ) . toMatchObject ( {
1202- name : null , secMember : null , scenarioAlias : null , versionAlias : null , puId : 50410 ,
1220+ name : null ,
1221+ secMember : null ,
1222+ scenarioAlias : null ,
1223+ versionAlias : null ,
1224+ puId : 50410 ,
12031225 } )
12041226 const history = CASES . find ( ( item ) => item . operation === 'get_planning_unit_history' ) !
12051227 respond ( history . response )
1206- expect ( ( await invoke ( history . operation , history . input ) ) . result . output . planningUnitHistory ?. [ 0 ] ) . toMatchObject ( {
1207- hasHistory : false , logSeq : - 1 , parentAnntSeq : 1 ,
1228+ expect (
1229+ ( await invoke ( history . operation , history . input ) ) . result . output . planningUnitHistory ?. [ 0 ]
1230+ ) . toMatchObject ( {
1231+ hasHistory : false ,
1232+ logSeq : - 1 ,
1233+ parentAnntSeq : 1 ,
12081234 } )
12091235 } )
12101236 it ( 'does not confuse approval confirmation with a job or echo unconfirmed input' , async ( ) => {
12111237 const entry = CASES . find ( ( item ) => item . operation === 'change_planning_unit_status' ) !
12121238 respond ( entry . response )
12131239 expect ( ( await invoke ( entry . operation , entry . input ) ) . result . output ) . toEqual ( {
1214- planningUnitAction : { pmMembers : '"Marketing"' , action : 'PROMOTE' , comments : '"Ready & reviewed + approved"' } ,
1240+ planningUnitAction : {
1241+ pmMembers : '"Marketing"' ,
1242+ action : 'PROMOTE' ,
1243+ comments : '"Ready & reviewed + approved"' ,
1244+ } ,
12151245 } )
12161246 for ( const response of [ { } , { links : [ ] } , { jobId : 42 , status : 0 } ] ) {
12171247 respond ( response )
12181248 expect ( ( await invoke ( entry . operation , entry . input ) ) . result . success ) . toBe ( false )
12191249 }
12201250 } )
12211251 it . each ( [
1222- [ 'https://other.example.com/HyperionPlanning/rest/v3/applications/Vision/planningunits/Forecast/actions' , 'POST' ] ,
1223- [ 'https://epm.example.com/HyperionPlanning/rest/v2/applications/Vision/planningunits/Forecast/actions' , 'POST' ] ,
1224- [ 'https://epm.example.com/HyperionPlanning/rest/v3/applications/Vision/planningunits/Forecast/actions' , 'GET' ] ,
1252+ [
1253+ 'https://other.example.com/HyperionPlanning/rest/v3/applications/Vision/planningunits/Forecast/actions' ,
1254+ 'POST' ,
1255+ ] ,
1256+ [
1257+ 'https://epm.example.com/HyperionPlanning/rest/v2/applications/Vision/planningunits/Forecast/actions' ,
1258+ 'POST' ,
1259+ ] ,
1260+ [
1261+ 'https://epm.example.com/HyperionPlanning/rest/v3/applications/Vision/planningunits/Forecast/actions' ,
1262+ 'GET' ,
1263+ ] ,
12251264 ] ) ( 'rejects invalid approval confirmation links without replay: %s %s' , async ( href , action ) => {
1226- respond ( { links : [ { rel : 'self' , href, action, data : { pmMembers : 'Marketing' , action : 'PROMOTE' , comments : '' } } ] } )
1265+ respond ( {
1266+ links : [
1267+ {
1268+ rel : 'self' ,
1269+ href,
1270+ action,
1271+ data : { pmMembers : 'Marketing' , action : 'PROMOTE' , comments : '' } ,
1272+ } ,
1273+ ] ,
1274+ } )
12271275 const entry = CASES . find ( ( item ) => item . operation === 'change_planning_unit_status' ) !
12281276 expect ( ( await invoke ( entry . operation , entry . input ) ) . result . success ) . toBe ( false )
12291277 expect ( mocks . fetch ) . toHaveBeenCalledTimes ( 1 )
@@ -1232,33 +1280,72 @@ describe('Planning operation contracts through the real foundation', () => {
12321280 const entry = CASES . find ( ( item ) => item . operation === 'get_insights' ) !
12331281 respond ( entry . response )
12341282 const { result } = await invoke ( entry . operation , entry . input )
1235- expect ( result . output ) . toMatchObject ( { insights : [ { id : '426' , outlierValue : 0 } ] , totalResults : 2 , hasMore : true } )
1283+ expect ( result . output ) . toMatchObject ( {
1284+ insights : [ { id : '426' , outlierValue : 0 } ] ,
1285+ totalResults : 2 ,
1286+ hasMore : true ,
1287+ } )
12361288 expect ( mocks . fetch ) . toHaveBeenCalledTimes ( 1 )
12371289 respond ( { items : [ ] , totalResults : 0 , hasMore : false } )
1238- expect ( ( await invoke ( entry . operation , entry . input ) ) . result . output ) . toEqual ( { insights : [ ] , totalResults : 0 , hasMore : false } )
1290+ expect ( ( await invoke ( entry . operation , entry . input ) ) . result . output ) . toEqual ( {
1291+ insights : [ ] ,
1292+ totalResults : 0 ,
1293+ hasMore : false ,
1294+ } )
12391295 respond ( { items : [ ] , totalResults : 0 } )
12401296 expect ( ( await invoke ( entry . operation , entry . input ) ) . result . success ) . toBe ( false )
12411297 } )
12421298 it ( 'requires the distinct insight slice and an explicit calendar for recomputation' , async ( ) => {
12431299 const entry = CASES . find ( ( item ) => item . operation === 'get_insights' ) !
1244- expect ( ( await invoke ( entry . operation , { ...entry . input , retrievalMode : 'FORCE_RECOMPUTE' } ) ) . status ) . toBe ( 400 )
1245- expect ( ( await invoke ( entry . operation , { ...entry . input , insightSlice : { pov : [ ] , columns : [ ] , rows : [ ] } } ) ) . status ) . toBe ( 400 )
1300+ expect (
1301+ ( await invoke ( entry . operation , { ...entry . input , retrievalMode : 'FORCE_RECOMPUTE' } ) ) . status
1302+ ) . toBe ( 400 )
1303+ expect (
1304+ (
1305+ await invoke ( entry . operation , {
1306+ ...entry . input ,
1307+ insightSlice : { pov : [ ] , columns : [ ] , rows : [ ] } ,
1308+ } )
1309+ ) . status
1310+ ) . toBe ( 400 )
12461311 expect ( mocks . fetch ) . not . toHaveBeenCalled ( )
12471312 respond ( entry . response )
1248- await invoke ( entry . operation , { ...entry . input , retrievalMode : 'FORCE_RECOMPUTE' , calendar : 'Fiscal' } )
1249- expect ( JSON . parse ( mocks . fetch . mock . calls [ 0 ] [ 2 ] . body ) ) . toMatchObject ( { retrievalMode : 'FORCE_RECOMPUTE' , calendar : 'Fiscal' } )
1313+ await invoke ( entry . operation , {
1314+ ...entry . input ,
1315+ retrievalMode : 'FORCE_RECOMPUTE' ,
1316+ calendar : 'Fiscal' ,
1317+ } )
1318+ expect ( JSON . parse ( mocks . fetch . mock . calls [ 0 ] [ 2 ] . body ) ) . toMatchObject ( {
1319+ retrievalMode : 'FORCE_RECOMPUTE' ,
1320+ calendar : 'Fiscal' ,
1321+ } )
12501322 expect ( mocks . fetch ) . toHaveBeenCalledTimes ( 1 )
12511323 } )
12521324 it ( 'sends only the selected summary mode and always requests text' , async ( ) => {
12531325 const get = CASES . find ( ( item ) => item . operation === 'get_insights' ) !
12541326 respond ( { summary : 'Variance summary' , warnings : [ ] , resolvedNarrative : { ignored : true } } )
1255- const ids = await invoke ( 'summarize_insights' , { ...get . input , summaryInputMode : 'ids' , insightIds : [ '426' ] , retrievalMode : 'FORCE_RECOMPUTE' , calendar : 'Stale' , summarySize : 80 } )
1327+ const ids = await invoke ( 'summarize_insights' , {
1328+ ...get . input ,
1329+ summaryInputMode : 'ids' ,
1330+ insightIds : [ '426' ] ,
1331+ retrievalMode : 'FORCE_RECOMPUTE' ,
1332+ calendar : 'Stale' ,
1333+ summarySize : 80 ,
1334+ } )
12561335 expect ( ids . result . output ) . toEqual ( { summary : 'Variance summary' } )
1257- expect ( JSON . parse ( mocks . fetch . mock . calls [ 0 ] [ 2 ] . body ) ) . toEqual ( { ids : [ '426' ] , format : 'text' , size : 80 } )
1336+ expect ( JSON . parse ( mocks . fetch . mock . calls [ 0 ] [ 2 ] . body ) ) . toEqual ( {
1337+ ids : [ '426' ] ,
1338+ format : 'text' ,
1339+ size : 80 ,
1340+ } )
12581341 await invoke ( 'summarize_insights' , { ...get . input , summaryInputMode : 'slice' , insightIds : [ '999' ] } )
12591342 expect ( JSON . parse ( mocks . fetch . mock . calls [ 1 ] [ 2 ] . body ) ) . toEqual ( {
1260- dataSourceType : 'CUBE' , location : 'Plan1' , slice : get . input . insightSlice ,
1261- retrievalMode : 'USE_EXISTING' , format : 'text' , size : 100 ,
1343+ dataSourceType : 'CUBE' ,
1344+ location : 'Plan1' ,
1345+ slice : get . input . insightSlice ,
1346+ retrievalMode : 'USE_EXISTING' ,
1347+ format : 'text' ,
1348+ size : 100 ,
12621349 } )
12631350 } )
12641351 it ( 'rejects incomplete summary inputs and undocumented JSON summaries' , async ( ) => {
@@ -1268,14 +1355,27 @@ describe('Planning operation contracts through the real foundation', () => {
12681355 { summaryInputMode : 'slice' , cube : 'Plan1' } ,
12691356 { summaryInputMode : 'slice' , insightSlice : { } } ,
12701357 { summaryInputMode : 'unknown' , insightIds : [ '426' ] } ,
1271- ] ) expect ( ( await invoke ( 'summarize_insights' , { application : 'Vision' , ...input } ) ) . status ) . toBe ( 400 )
1358+ ] )
1359+ expect ( ( await invoke ( 'summarize_insights' , { application : 'Vision' , ...input } ) ) . status ) . toBe ( 400 )
12721360 expect ( mocks . fetch ) . not . toHaveBeenCalled ( )
12731361 respond ( { summary : { text : 'Not a documented text result' } } )
1274- expect ( ( await invoke ( 'summarize_insights' , { application : 'Vision' , summaryInputMode : 'ids' , insightIds : [ '426' ] } ) ) . result . success ) . toBe ( false )
1362+ expect (
1363+ (
1364+ await invoke ( 'summarize_insights' , {
1365+ application : 'Vision' ,
1366+ summaryInputMode : 'ids' ,
1367+ insightIds : [ '426' ] ,
1368+ } )
1369+ ) . result . success
1370+ ) . toBe ( false )
12751371 } )
12761372 it ( 'rejects oversized insight summaries clearly instead of truncating them' , async ( ) => {
12771373 respond ( { summary : 'x' . repeat ( 16 * 1024 * 1024 ) } )
1278- const result = await invoke ( 'summarize_insights' , { application : 'Vision' , summaryInputMode : 'ids' , insightIds : [ '426' ] } )
1374+ const result = await invoke ( 'summarize_insights' , {
1375+ application : 'Vision' ,
1376+ summaryInputMode : 'ids' ,
1377+ insightIds : [ '426' ] ,
1378+ } )
12791379 expect ( result . status ) . toBe ( 413 )
12801380 expect ( result . result . success ) . toBe ( false )
12811381 expect ( result . result . output . summary ) . toBeUndefined ( )
0 commit comments