@@ -21,7 +21,7 @@ jest.mock('@actions/core', () => mockCore);
2121const mockFetch = jest . fn ( ) ;
2222global . fetch = mockFetch ;
2323
24- const { run, pollTask, buildUrl, isGlobPattern, resolveFiles, worstStatus, getFileFormat, injectYamlVariables } = require ( './index' ) ;
24+ const { run, pollTask, buildUrl, isGlobPattern, resolveFiles, worstStatus, getFileFormat, injectYamlVariables, injectJsonVariables } = require ( './index' ) ;
2525
2626// Helpers
2727function mockInputs ( inputs ) {
@@ -1685,11 +1685,16 @@ variable:
16851685// ─── run() — JSON file format ──────────────────────────────────────────────
16861686
16871687describe ( 'run — JSON file format' , ( ) => {
1688- const SAMPLE_JSON_CHANGESET = JSON . stringify ( {
1688+ const SAMPLE_JSON_CHANGESET_OBJ = {
16891689 name : 'Test Queue' ,
16901690 environment : 'Development' ,
1691+ variable : [
1692+ { environment : null , mask_value : false , name : 'DogsName' , value : 'global value' } ,
1693+ { environment : 1 , mask_value : false , name : 'DogsName' , value : 'dev value' } ,
1694+ ] ,
16911695 action : [ { action : 'gencloud-create' , object_type : 'RoutingQueue' , data : { name : 'Test Queue' } } ]
1692- } ) ;
1696+ } ;
1697+ const SAMPLE_JSON_CHANGESET = JSON . stringify ( SAMPLE_JSON_CHANGESET_OBJ , null , 2 ) ;
16931698 const SAMPLE_JSON_FILE = path . join ( __dirname , '__test_changeset_sample__.json' ) ;
16941699
16951700 beforeAll ( ( ) => {
@@ -1724,9 +1729,9 @@ describe('run — JSON file format', () => {
17241729 headers : expect . objectContaining ( { 'Content-Type' : 'application/json' } ) ,
17251730 } )
17261731 ) ;
1727- // Body should be JSON with changeset field
1728- const body = JSON . parse ( mockFetch . mock . calls [ 0 ] [ 1 ] . body ) ;
1729- expect ( body . changeset ) . toBe ( SAMPLE_JSON_CHANGESET ) ;
1732+ // Body should be raw JSON changeset content (not wrapped)
1733+ const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body ;
1734+ expect ( body ) . toBe ( SAMPLE_JSON_CHANGESET ) ;
17301735 expect ( mockCore . setFailed ) . not . toHaveBeenCalled ( ) ;
17311736 } ) ;
17321737
@@ -1754,16 +1759,19 @@ describe('run — JSON file format', () => {
17541759 headers : expect . objectContaining ( { 'Content-Type' : 'application/json' } ) ,
17551760 } )
17561761 ) ;
1762+ // Body should be raw JSON changeset content (not wrapped)
1763+ const body = mockFetch . mock . calls [ 0 ] [ 1 ] . body ;
1764+ expect ( body ) . toBe ( SAMPLE_JSON_CHANGESET ) ;
17571765 expect ( mockCore . setFailed ) . not . toHaveBeenCalled ( ) ;
17581766 } ) ;
17591767
1760- test ( 'includes variables in JSON payload for .json files' , async ( ) => {
1768+ test ( 'injects variables into JSON changeset body for .json files' , async ( ) => {
17611769 mockInputs ( {
17621770 api_key : 'key' ,
17631771 base_url : 'https://test.inprod.io' ,
17641772 changeset_file : SAMPLE_JSON_FILE ,
17651773 validate_before_execute : 'false' ,
1766- changeset_variables : 'DB_PASSWORD=secret\nAPI_KEY=key123 ' ,
1774+ changeset_variables : 'DogsName=overridden\nNewVar=newval ' ,
17671775 } ) ;
17681776
17691777 const execResult = { run_id : 42 , changeset_name : 'Test' , environment : { id : 1 , name : 'Dev' } } ;
@@ -1776,8 +1784,79 @@ describe('run — JSON file format', () => {
17761784 await promise ;
17771785
17781786 const body = JSON . parse ( mockFetch . mock . calls [ 0 ] [ 1 ] . body ) ;
1779- expect ( body . changeset ) . toBe ( SAMPLE_JSON_CHANGESET ) ;
1780- expect ( body . variables ) . toEqual ( { DB_PASSWORD : 'secret' , API_KEY : 'key123' } ) ;
1787+ // Should NOT have a wrapper changeset/variables field
1788+ expect ( body . changeset ) . toBeUndefined ( ) ;
1789+ expect ( body . variables ) . toBeUndefined ( ) ;
1790+ // All existing DogsName entries should be removed, replaced with one injected entry
1791+ const dogsEntries = body . variable . filter ( v => v . name === 'DogsName' ) ;
1792+ expect ( dogsEntries ) . toHaveLength ( 1 ) ;
1793+ expect ( dogsEntries [ 0 ] ) . toEqual ( { environment : null , mask_value : true , name : 'DogsName' , value : 'overridden' } ) ;
1794+ // New variable should be added
1795+ const newVarEntry = body . variable . find ( v => v . name === 'NewVar' ) ;
1796+ expect ( newVarEntry ) . toEqual ( { environment : null , mask_value : true , name : 'NewVar' , value : 'newval' } ) ;
17811797 expect ( mockCore . setFailed ) . not . toHaveBeenCalled ( ) ;
17821798 } ) ;
1799+ } ) ;
1800+
1801+ // ─── injectJsonVariables ────────────────────────────────────────────────────
1802+
1803+ describe ( 'injectJsonVariables' , ( ) => {
1804+ const baseJson = JSON . stringify ( {
1805+ name : 'Test Queue' ,
1806+ variable : [
1807+ { environment : null , mask_value : false , name : 'existing_var' , value : 'old_value' } ,
1808+ { environment : 1 , mask_value : false , name : 'existing_var' , value : 'dev_value' } ,
1809+ { environment : null , mask_value : true , name : 'keep_var' , value : 'kept' } ,
1810+ ] ,
1811+ action : [ { action : 'gencloud-create' } ]
1812+ } , null , 2 ) ;
1813+
1814+ test ( 'injects new variables with mask_value: true' , ( ) => {
1815+ const result = JSON . parse ( injectJsonVariables ( baseJson , { NEW_VAR : 'new_value' } ) ) ;
1816+ const injected = result . variable . find ( v => v . name === 'NEW_VAR' ) ;
1817+ expect ( injected ) . toEqual ( { environment : null , mask_value : true , name : 'NEW_VAR' , value : 'new_value' } ) ;
1818+ } ) ;
1819+
1820+ test ( 'replaces all entries for an existing variable name' , ( ) => {
1821+ const result = JSON . parse ( injectJsonVariables ( baseJson , { existing_var : 'replaced_value' } ) ) ;
1822+ const matches = result . variable . filter ( v => v . name === 'existing_var' ) ;
1823+ expect ( matches ) . toHaveLength ( 1 ) ;
1824+ expect ( matches [ 0 ] . value ) . toBe ( 'replaced_value' ) ;
1825+ expect ( matches [ 0 ] . mask_value ) . toBe ( true ) ;
1826+ expect ( matches [ 0 ] . environment ) . toBeNull ( ) ;
1827+ } ) ;
1828+
1829+ test ( 'preserves existing variables that are not overridden' , ( ) => {
1830+ const result = JSON . parse ( injectJsonVariables ( baseJson , { existing_var : 'new' } ) ) ;
1831+ const kept = result . variable . find ( v => v . name === 'keep_var' ) ;
1832+ expect ( kept ) . toEqual ( { environment : null , mask_value : true , name : 'keep_var' , value : 'kept' } ) ;
1833+ } ) ;
1834+
1835+ test ( 'handles JSON with empty variable array' , ( ) => {
1836+ const jsonEmpty = JSON . stringify ( { name : 'Test' , variable : [ ] } ) ;
1837+ const result = JSON . parse ( injectJsonVariables ( jsonEmpty , { NEW_VAR : 'value' } ) ) ;
1838+ expect ( result . variable ) . toHaveLength ( 1 ) ;
1839+ expect ( result . variable [ 0 ] . name ) . toBe ( 'NEW_VAR' ) ;
1840+ } ) ;
1841+
1842+ test ( 'handles JSON with no variable field' , ( ) => {
1843+ const jsonNoVar = JSON . stringify ( { name : 'Test' , environment : 'Dev' } ) ;
1844+ const result = JSON . parse ( injectJsonVariables ( jsonNoVar , { NEW_VAR : 'value' } ) ) ;
1845+ expect ( result . variable ) . toHaveLength ( 1 ) ;
1846+ expect ( result . variable [ 0 ] . name ) . toBe ( 'NEW_VAR' ) ;
1847+ } ) ;
1848+
1849+ test ( 'injects multiple variables at once' , ( ) => {
1850+ const result = JSON . parse ( injectJsonVariables ( baseJson , { VAR_A : 'aaa' , VAR_B : 'bbb' } ) ) ;
1851+ const varA = result . variable . find ( v => v . name === 'VAR_A' ) ;
1852+ const varB = result . variable . find ( v => v . name === 'VAR_B' ) ;
1853+ expect ( varA ) . toEqual ( { environment : null , mask_value : true , name : 'VAR_A' , value : 'aaa' } ) ;
1854+ expect ( varB ) . toEqual ( { environment : null , mask_value : true , name : 'VAR_B' , value : 'bbb' } ) ;
1855+ } ) ;
1856+
1857+ test ( 'preserves other JSON fields' , ( ) => {
1858+ const result = JSON . parse ( injectJsonVariables ( baseJson , { NEW_VAR : 'val' } ) ) ;
1859+ expect ( result . name ) . toBe ( 'Test Queue' ) ;
1860+ expect ( result . action ) . toEqual ( [ { action : 'gencloud-create' } ] ) ;
1861+ } ) ;
17831862} ) ;
0 commit comments