@@ -344,6 +344,235 @@ describe('createConfigStore', () => {
344344 } ) ;
345345 } ) ;
346346
347+ describe ( 'vars (key-value store)' , ( ) => {
348+ it ( 'should return null for getVar when no context is active' , ( ) => {
349+ const store = createStore ( ) ;
350+ expect ( store . getVar ( 'orgId' ) ) . toBeNull ( ) ;
351+ } ) ;
352+
353+ it ( 'should throw on setVar when no context is active' , ( ) => {
354+ const store = createStore ( ) ;
355+ expect ( ( ) => store . setVar ( 'orgId' , 'abc-123' ) ) . toThrow ( 'No active context' ) ;
356+ } ) ;
357+
358+ it ( 'should set and get a var in the current context' , ( ) => {
359+ const store = createStore ( ) ;
360+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
361+ store . setCurrentContext ( 'production' ) ;
362+ store . setVar ( 'orgId' , 'abc-123' ) ;
363+ expect ( store . getVar ( 'orgId' ) ) . toBe ( 'abc-123' ) ;
364+ } ) ;
365+
366+ it ( 'should set and get a var by explicit context name' , ( ) => {
367+ const store = createStore ( ) ;
368+ store . createContext ( 'staging' , { endpoint : 'https://staging.example.com/graphql' } ) ;
369+ store . setVar ( 'orgId' , 'staging-org' , 'staging' ) ;
370+ expect ( store . getVar ( 'orgId' , 'staging' ) ) . toBe ( 'staging-org' ) ;
371+ } ) ;
372+
373+ it ( 'should overwrite existing var' , ( ) => {
374+ const store = createStore ( ) ;
375+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
376+ store . setCurrentContext ( 'production' ) ;
377+ store . setVar ( 'orgId' , 'old' ) ;
378+ store . setVar ( 'orgId' , 'new' ) ;
379+ expect ( store . getVar ( 'orgId' ) ) . toBe ( 'new' ) ;
380+ } ) ;
381+
382+ it ( 'should return null for non-existent var' , ( ) => {
383+ const store = createStore ( ) ;
384+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
385+ store . setCurrentContext ( 'production' ) ;
386+ expect ( store . getVar ( 'nonexistent' ) ) . toBeNull ( ) ;
387+ } ) ;
388+
389+ it ( 'should delete a var' , ( ) => {
390+ const store = createStore ( ) ;
391+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
392+ store . setCurrentContext ( 'production' ) ;
393+ store . setVar ( 'orgId' , 'abc-123' ) ;
394+ const deleted = store . deleteVar ( 'orgId' ) ;
395+ expect ( deleted ) . toBe ( true ) ;
396+ expect ( store . getVar ( 'orgId' ) ) . toBeNull ( ) ;
397+ } ) ;
398+
399+ it ( 'should return false when deleting non-existent var' , ( ) => {
400+ const store = createStore ( ) ;
401+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
402+ store . setCurrentContext ( 'production' ) ;
403+ expect ( store . deleteVar ( 'nonexistent' ) ) . toBe ( false ) ;
404+ } ) ;
405+
406+ it ( 'should return false for deleteVar when no context is active' , ( ) => {
407+ const store = createStore ( ) ;
408+ expect ( store . deleteVar ( 'orgId' ) ) . toBe ( false ) ;
409+ } ) ;
410+
411+ it ( 'should list all vars for current context' , ( ) => {
412+ const store = createStore ( ) ;
413+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
414+ store . setCurrentContext ( 'production' ) ;
415+ store . setVar ( 'orgId' , 'abc-123' ) ;
416+ store . setVar ( 'defaultDatabase' , 'my-app' ) ;
417+ const vars = store . listVars ( ) ;
418+ expect ( vars ) . toEqual ( { orgId : 'abc-123' , defaultDatabase : 'my-app' } ) ;
419+ } ) ;
420+
421+ it ( 'should return empty object for listVars when no context is active' , ( ) => {
422+ const store = createStore ( ) ;
423+ expect ( store . listVars ( ) ) . toEqual ( { } ) ;
424+ } ) ;
425+
426+ it ( 'should isolate vars between contexts' , ( ) => {
427+ const store = createStore ( ) ;
428+ store . createContext ( 'production' , { endpoint : 'https://api.example.com/graphql' } ) ;
429+ store . createContext ( 'staging' , { endpoint : 'https://staging.example.com/graphql' } ) ;
430+
431+ store . setVar ( 'orgId' , 'prod-org' , 'production' ) ;
432+ store . setVar ( 'orgId' , 'staging-org' , 'staging' ) ;
433+
434+ expect ( store . getVar ( 'orgId' , 'production' ) ) . toBe ( 'prod-org' ) ;
435+ expect ( store . getVar ( 'orgId' , 'staging' ) ) . toBe ( 'staging-org' ) ;
436+ } ) ;
437+ } ) ;
438+
439+ describe ( 'getClientConfig' , ( ) => {
440+ it ( 'should return endpoint and auth header from store' , ( ) => {
441+ const store = createStore ( ) ;
442+ store . createContext ( 'production' , {
443+ endpoint : 'https://api.example.com/graphql' ,
444+ targets : {
445+ auth : { endpoint : 'https://auth.example.com/graphql' } ,
446+ public : { endpoint : 'https://public.example.com/graphql' } ,
447+ } ,
448+ } ) ;
449+ store . setCurrentContext ( 'production' ) ;
450+ store . setCredentials ( 'production' , { token : 'my-jwt-token' } ) ;
451+
452+ const config = store . getClientConfig ( 'auth' ) ;
453+ expect ( config . endpoint ) . toBe ( 'https://auth.example.com/graphql' ) ;
454+ expect ( config . headers [ 'Authorization' ] ) . toBe ( 'Bearer my-jwt-token' ) ;
455+ } ) ;
456+
457+ it ( 'should return config for explicit context name' , ( ) => {
458+ const store = createStore ( ) ;
459+ store . createContext ( 'staging' , {
460+ endpoint : 'https://staging.example.com/graphql' ,
461+ targets : {
462+ public : { endpoint : 'https://public.staging.example.com/graphql' } ,
463+ } ,
464+ } ) ;
465+ store . setCredentials ( 'staging' , { token : 'staging-token' } ) ;
466+
467+ const config = store . getClientConfig ( 'public' , 'staging' ) ;
468+ expect ( config . endpoint ) . toBe ( 'https://public.staging.example.com/graphql' ) ;
469+ expect ( config . headers [ 'Authorization' ] ) . toBe ( 'Bearer staging-token' ) ;
470+ } ) ;
471+
472+ it ( 'should return config without auth header when no credentials' , ( ) => {
473+ const store = createStore ( ) ;
474+ store . createContext ( 'production' , {
475+ endpoint : 'https://api.example.com/graphql' ,
476+ } ) ;
477+ store . setCurrentContext ( 'production' ) ;
478+
479+ const config = store . getClientConfig ( 'public' ) ;
480+ expect ( config . endpoint ) . toBe ( 'https://api.example.com/graphql' ) ;
481+ expect ( config . headers [ 'Authorization' ] ) . toBeUndefined ( ) ;
482+ } ) ;
483+
484+ it ( 'should fall back to env vars when no context exists' , ( ) => {
485+ const store = createStore ( ) ;
486+ process . env [ 'TESTAPP_PUBLIC_ENDPOINT' ] = 'https://env.example.com/graphql' ;
487+ process . env [ 'TESTAPP_TOKEN' ] = 'env-token' ;
488+
489+ try {
490+ const config = store . getClientConfig ( 'public' ) ;
491+ expect ( config . endpoint ) . toBe ( 'https://env.example.com/graphql' ) ;
492+ expect ( config . headers [ 'Authorization' ] ) . toBe ( 'Bearer env-token' ) ;
493+ } finally {
494+ delete process . env [ 'TESTAPP_PUBLIC_ENDPOINT' ] ;
495+ delete process . env [ 'TESTAPP_TOKEN' ] ;
496+ }
497+ } ) ;
498+
499+ it ( 'should fall back to generic endpoint env var' , ( ) => {
500+ const store = createStore ( ) ;
501+ process . env [ 'TESTAPP_ENDPOINT' ] = 'https://generic.example.com/graphql' ;
502+
503+ try {
504+ const config = store . getClientConfig ( 'auth' ) ;
505+ expect ( config . endpoint ) . toBe ( 'https://generic.example.com/graphql' ) ;
506+ expect ( config . headers [ 'Authorization' ] ) . toBeUndefined ( ) ;
507+ } finally {
508+ delete process . env [ 'TESTAPP_ENDPOINT' ] ;
509+ }
510+ } ) ;
511+
512+ it ( 'should prefer target-specific env var over generic' , ( ) => {
513+ const store = createStore ( ) ;
514+ process . env [ 'TESTAPP_AUTH_ENDPOINT' ] = 'https://auth-specific.example.com/graphql' ;
515+ process . env [ 'TESTAPP_ENDPOINT' ] = 'https://generic.example.com/graphql' ;
516+ process . env [ 'TESTAPP_TOKEN' ] = 'env-token' ;
517+
518+ try {
519+ const config = store . getClientConfig ( 'auth' ) ;
520+ expect ( config . endpoint ) . toBe ( 'https://auth-specific.example.com/graphql' ) ;
521+ } finally {
522+ delete process . env [ 'TESTAPP_AUTH_ENDPOINT' ] ;
523+ delete process . env [ 'TESTAPP_ENDPOINT' ] ;
524+ delete process . env [ 'TESTAPP_TOKEN' ] ;
525+ }
526+ } ) ;
527+
528+ it ( 'should throw with actionable error when nothing is configured' , ( ) => {
529+ const store = createStore ( ) ;
530+ expect ( ( ) => store . getClientConfig ( 'public' ) ) . toThrow (
531+ / N o c o n f i g u r a t i o n f o u n d f o r t a r g e t " p u b l i c " /
532+ ) ;
533+ expect ( ( ) => store . getClientConfig ( 'public' ) ) . toThrow (
534+ / T E S T A P P _ P U B L I C _ E N D P O I N T /
535+ ) ;
536+ } ) ;
537+
538+ it ( 'should prioritize store over env vars' , ( ) => {
539+ const store = createStore ( ) ;
540+ store . createContext ( 'production' , {
541+ endpoint : 'https://api.example.com/graphql' ,
542+ targets : {
543+ public : { endpoint : 'https://store.example.com/graphql' } ,
544+ } ,
545+ } ) ;
546+ store . setCurrentContext ( 'production' ) ;
547+ store . setCredentials ( 'production' , { token : 'store-token' } ) ;
548+ process . env [ 'TESTAPP_PUBLIC_ENDPOINT' ] = 'https://env.example.com/graphql' ;
549+ process . env [ 'TESTAPP_TOKEN' ] = 'env-token' ;
550+
551+ try {
552+ const config = store . getClientConfig ( 'public' ) ;
553+ expect ( config . endpoint ) . toBe ( 'https://store.example.com/graphql' ) ;
554+ expect ( config . headers [ 'Authorization' ] ) . toBe ( 'Bearer store-token' ) ;
555+ } finally {
556+ delete process . env [ 'TESTAPP_PUBLIC_ENDPOINT' ] ;
557+ delete process . env [ 'TESTAPP_TOKEN' ] ;
558+ }
559+ } ) ;
560+
561+ it ( 'should fall back to main endpoint when target not in store targets' , ( ) => {
562+ const store = createStore ( ) ;
563+ store . createContext ( 'production' , {
564+ endpoint : 'https://api.example.com/graphql' ,
565+ targets : {
566+ auth : { endpoint : 'https://auth.example.com/graphql' } ,
567+ } ,
568+ } ) ;
569+ store . setCurrentContext ( 'production' ) ;
570+
571+ const config = store . getClientConfig ( 'public' ) ;
572+ expect ( config . endpoint ) . toBe ( 'https://api.example.com/graphql' ) ;
573+ } ) ;
574+ } ) ;
575+
347576 describe ( 'full workflow' , ( ) => {
348577 it ( 'should support the complete context + auth workflow' , ( ) => {
349578 const store = createStore ( ) ;
0 commit comments