@@ -576,6 +576,180 @@ describe("cli", () => {
576576 expect ( out ) . toContain ( '"ok": true' ) ;
577577 } ) ;
578578
579+ // --- Body flag JSON parsing tests ---
580+
581+ function createPostApiDeps ( ) {
582+ const localDir = `${ cwd } /.ocli` ;
583+ const profilesPath = `${ localDir } /profiles.ini` ;
584+ const cachePath = `${ localDir } /specs/body-api.json` ;
585+
586+ const spec = {
587+ openapi : "3.0.0" ,
588+ paths : {
589+ "/{org_slug}/{repo_slug}/ci/workflows/{workflow_name}/trigger" : {
590+ post : {
591+ summary : "Trigger workflow" ,
592+ parameters : [
593+ { name : "org_slug" , in : "path" , required : true , schema : { type : "string" } } ,
594+ { name : "repo_slug" , in : "path" , required : true , schema : { type : "string" } } ,
595+ { name : "workflow_name" , in : "path" , required : true , schema : { type : "string" } } ,
596+ ] ,
597+ } ,
598+ } ,
599+ } ,
600+ } ;
601+
602+ const iniContent = [
603+ "[body-api]" ,
604+ "api_base_url = https://api.example.com" ,
605+ "api_basic_auth = " ,
606+ "api_bearer_token = tok" ,
607+ "openapi_spec_source = /spec.json" ,
608+ `openapi_spec_cache = ${ cachePath } ` ,
609+ "include_endpoints = " ,
610+ "exclude_endpoints = " ,
611+ "" ,
612+ ] . join ( "\n" ) ;
613+
614+ const capturedConfigs : unknown [ ] = [ ] ;
615+ const fakeHttpClient : HttpClient = {
616+ request : async ( config : any ) => {
617+ capturedConfigs . push ( config ) ;
618+ return { status : 200 , statusText : "OK" , headers : { } , config, data : { ok : true } } ;
619+ } ,
620+ } ;
621+
622+ const { profileStore, openapiLoader } = createCliDeps ( cwd , homeDir , {
623+ [ profilesPath ] : iniContent ,
624+ [ cachePath ] : JSON . stringify ( spec ) ,
625+ [ `${ localDir } /current` ] : "body-api" ,
626+ } ) ;
627+
628+ return { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } ;
629+ }
630+
631+ it ( "parses JSON object body flags before sending request" , async ( ) => {
632+ const { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } = createPostApiDeps ( ) ;
633+
634+ await run (
635+ [
636+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
637+ "--org_slug" , "myorg" ,
638+ "--repo_slug" , "myrepo" ,
639+ "--workflow_name" , "deploy" ,
640+ "--revision" , "main" ,
641+ "--workflow_revision" , "main" ,
642+ "--input" , '{"values":[{"name":"FOO","value":"bar"}]}' ,
643+ ] ,
644+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
645+ ) ;
646+
647+ expect ( capturedConfigs ) . toHaveLength ( 1 ) ;
648+ const config = capturedConfigs [ 0 ] as { data : Record < string , unknown > } ;
649+ expect ( config . data ) . toEqual ( {
650+ revision : "main" ,
651+ workflow_revision : "main" ,
652+ input : {
653+ values : [ { name : "FOO" , value : "bar" } ] ,
654+ } ,
655+ } ) ;
656+ } ) ;
657+
658+ it ( "parses boolean body flags before sending request" , async ( ) => {
659+ const { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } = createPostApiDeps ( ) ;
660+
661+ await run (
662+ [
663+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
664+ "--org_slug" , "myorg" ,
665+ "--repo_slug" , "myrepo" ,
666+ "--workflow_name" , "deploy" ,
667+ "--shared" , "true" ,
668+ "--draft" , "false" ,
669+ ] ,
670+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
671+ ) ;
672+
673+ expect ( capturedConfigs ) . toHaveLength ( 1 ) ;
674+ const config = capturedConfigs [ 0 ] as { data : Record < string , unknown > } ;
675+ expect ( config . data . shared ) . toBe ( true ) ;
676+ expect ( config . data . draft ) . toBe ( false ) ;
677+ } ) ;
678+
679+ it ( "parses null body flags before sending request" , async ( ) => {
680+ const { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } = createPostApiDeps ( ) ;
681+
682+ await run (
683+ [
684+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
685+ "--org_slug" , "myorg" ,
686+ "--repo_slug" , "myrepo" ,
687+ "--workflow_name" , "deploy" ,
688+ "--description" , "null" ,
689+ ] ,
690+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
691+ ) ;
692+
693+ expect ( capturedConfigs ) . toHaveLength ( 1 ) ;
694+ const config = capturedConfigs [ 0 ] as { data : Record < string , unknown > } ;
695+ expect ( config . data . description ) . toBeNull ( ) ;
696+ } ) ;
697+
698+ it ( "parses JSON array body flags before sending request" , async ( ) => {
699+ const { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } = createPostApiDeps ( ) ;
700+
701+ await run (
702+ [
703+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
704+ "--org_slug" , "myorg" ,
705+ "--repo_slug" , "myrepo" ,
706+ "--workflow_name" , "deploy" ,
707+ "--tags" , '["ci","deploy"]' ,
708+ ] ,
709+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
710+ ) ;
711+
712+ expect ( capturedConfigs ) . toHaveLength ( 1 ) ;
713+ const config = capturedConfigs [ 0 ] as { data : Record < string , unknown > } ;
714+ expect ( config . data . tags ) . toEqual ( [ "ci" , "deploy" ] ) ;
715+ } ) ;
716+
717+ it ( "preserves plain string body flags" , async ( ) => {
718+ const { profileStore, openapiLoader, fakeHttpClient, capturedConfigs } = createPostApiDeps ( ) ;
719+
720+ await run (
721+ [
722+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
723+ "--org_slug" , "myorg" ,
724+ "--repo_slug" , "myrepo" ,
725+ "--workflow_name" , "deploy" ,
726+ "--revision" , "main" ,
727+ ] ,
728+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
729+ ) ;
730+
731+ expect ( capturedConfigs ) . toHaveLength ( 1 ) ;
732+ const config = capturedConfigs [ 0 ] as { data : Record < string , unknown > } ;
733+ expect ( config . data . revision ) . toBe ( "main" ) ;
734+ } ) ;
735+
736+ it ( "throws on invalid JSON-like body flags" , async ( ) => {
737+ const { profileStore, openapiLoader, fakeHttpClient } = createPostApiDeps ( ) ;
738+
739+ await expect (
740+ run (
741+ [
742+ "org_slug_repo_slug_ci_workflows_workflow_name_trigger" ,
743+ "--org_slug" , "myorg" ,
744+ "--repo_slug" , "myrepo" ,
745+ "--workflow_name" , "deploy" ,
746+ "--input" , '{"values":' ,
747+ ] ,
748+ { cwd, profileStore, openapiLoader, httpClient : fakeHttpClient , stdout : ( ) => { } }
749+ )
750+ ) . rejects . toThrow ( "Invalid JSON body value" ) ;
751+ } ) ;
752+
579753 it ( "sends custom headers from profile in API requests" , async ( ) => {
580754 const localDir = `${ cwd } /.ocli` ;
581755 const profilesPath = `${ localDir } /profiles.ini` ;
0 commit comments