@@ -172,6 +172,98 @@ describe("BackendPostgres schema option", () => {
172172 } ) ;
173173} ) ;
174174
175+ describe ( "BackendPostgres JSON key preservation" , ( ) => {
176+ test ( "preserves uppercase snake case keys in workflow run input" , async ( ) => {
177+ const namespaceId = randomUUID ( ) ;
178+ const backend = await BackendPostgres . connect ( DEFAULT_POSTGRES_URL , {
179+ namespaceId,
180+ } ) ;
181+
182+ // https://github.com/openworkflowdev/openworkflow/issues/367
183+ const input = {
184+ env : {
185+ OPENAI_MODEL : "gpt-5.3-codex" ,
186+ OPENAI_BASE_URL : "http://127.0.0.1:8090/..." ,
187+ OPENAI_REASONING_EFFORT : "medium" ,
188+ } ,
189+ } ;
190+ const transformedModelKey = "OPENAI_MODEL" . replaceAll ( "_" , "" ) ;
191+ const transformedBaseUrlKey = "OPENAI_BASE_URL" . replaceAll ( "_" , "" ) ;
192+ const transformedReasoningEffortKey = "OPENAI_REASONING_EFFORT" . replaceAll (
193+ "_" ,
194+ "" ,
195+ ) ;
196+
197+ try {
198+ const workflowRun = await backend . createWorkflowRun ( {
199+ workflowName : "json-key-preservation" ,
200+ version : null ,
201+ idempotencyKey : null ,
202+ input,
203+ config : { } ,
204+ context : null ,
205+ parentStepAttemptNamespaceId : null ,
206+ parentStepAttemptId : null ,
207+ availableAt : null ,
208+ deadlineAt : null ,
209+ } ) ;
210+
211+ if (
212+ ! workflowRun . input ||
213+ typeof workflowRun . input !== "object" ||
214+ Array . isArray ( workflowRun . input )
215+ ) {
216+ throw new Error ( "Expected workflow run input object" ) ;
217+ }
218+
219+ const createEnv = ( workflowRun . input as { env ?: Record < string , string > } )
220+ . env ;
221+ if ( ! createEnv ) throw new Error ( "Expected workflow run input env" ) ;
222+ expect ( createEnv [ "OPENAI_MODEL" ] ) . toBe ( input . env . OPENAI_MODEL ) ;
223+ expect ( createEnv [ "OPENAI_BASE_URL" ] ) . toBe ( input . env . OPENAI_BASE_URL ) ;
224+ expect ( createEnv [ "OPENAI_REASONING_EFFORT" ] ) . toBe (
225+ input . env . OPENAI_REASONING_EFFORT ,
226+ ) ;
227+ expect ( createEnv [ transformedModelKey ] ) . toBeUndefined ( ) ;
228+ expect ( createEnv [ transformedBaseUrlKey ] ) . toBeUndefined ( ) ;
229+ expect ( createEnv [ transformedReasoningEffortKey ] ) . toBeUndefined ( ) ;
230+
231+ const pg = newPostgresMaxOne ( DEFAULT_POSTGRES_URL ) ;
232+ try {
233+ const workflowRunsTable = pg `${ pg ( DEFAULT_SCHEMA ) } .${ pg ( "workflow_runs" ) } ` ;
234+ const [ record ] = await pg <
235+ {
236+ input : {
237+ env ?: Record < string , string > ;
238+ } ;
239+ } [ ]
240+ > `
241+ SELECT "input"
242+ FROM ${ workflowRunsTable }
243+ WHERE "namespace_id" = ${ namespaceId }
244+ AND "id" = ${ workflowRun . id }
245+ LIMIT 1
246+ ` ;
247+
248+ const persistedEnv = record ?. input . env ;
249+ if ( ! persistedEnv ) throw new Error ( "Expected persisted workflow input" ) ;
250+ expect ( persistedEnv [ "OPENAI_MODEL" ] ) . toBe ( input . env . OPENAI_MODEL ) ;
251+ expect ( persistedEnv [ "OPENAI_BASE_URL" ] ) . toBe ( input . env . OPENAI_BASE_URL ) ;
252+ expect ( persistedEnv [ "OPENAI_REASONING_EFFORT" ] ) . toBe (
253+ input . env . OPENAI_REASONING_EFFORT ,
254+ ) ;
255+ expect ( persistedEnv [ transformedModelKey ] ) . toBeUndefined ( ) ;
256+ expect ( persistedEnv [ transformedBaseUrlKey ] ) . toBeUndefined ( ) ;
257+ expect ( persistedEnv [ transformedReasoningEffortKey ] ) . toBeUndefined ( ) ;
258+ } finally {
259+ await pg . end ( ) ;
260+ }
261+ } finally {
262+ await backend . stop ( ) ;
263+ }
264+ } ) ;
265+ } ) ;
266+
175267describe ( "BackendPostgres cancel fallback" , ( ) => {
176268 test ( "throws generic cancel error for non-standard workflow status" , async ( ) => {
177269 const backend = await BackendPostgres . connect ( DEFAULT_POSTGRES_URL , {
0 commit comments