@@ -491,6 +491,129 @@ describe("auth commands", () => {
491491 "default" ,
492492 ) ;
493493 } ) ;
494+
495+ it ( "saves tenantId and availableTenants to config" , async ( ) => {
496+ const tenants = [
497+ { tenantId : "city_a" , name : "Smart City A" , role : "tenant_admin" } ,
498+ { tenantId : "city_b" , role : "user" } ,
499+ ] ;
500+ client . rawRequest . mockResolvedValue (
501+ mockResponse ( { accessToken : "tok" , tenantId : "city_a" , availableTenants : tenants } ) ,
502+ ) ;
503+ vi . mocked ( promptTenantSelection ) . mockResolvedValue ( undefined ) ;
504+ const program = makeProgram ( ) ;
505+ await runCommand ( program , [ "auth" , "login" ] ) ;
506+ expect ( saveConfig ) . toHaveBeenCalledWith (
507+ expect . objectContaining ( {
508+ tenantId : "city_a" ,
509+ availableTenants : [
510+ { tenantId : "city_a" , name : "Smart City A" , role : "tenant_admin" } ,
511+ { tenantId : "city_b" , role : "user" } ,
512+ ] ,
513+ } ) ,
514+ "default" ,
515+ ) ;
516+ } ) ;
517+
518+ it ( "resolves tenant by name via --service flag" , async ( ) => {
519+ const tenants = [
520+ { tenantId : "tid-aaa" , name : "demo_smartcity" , role : "tenant_admin" } ,
521+ { tenantId : "tid-bbb" , name : "demo_bousai" , role : "user" } ,
522+ ] ;
523+ vi . mocked ( resolveOptions ) . mockReturnValue ( {
524+ url : "http://localhost:3000" ,
525+ profile : "default" ,
526+ token : "test-token" ,
527+ format : "json" ,
528+ service : "demo_bousai" ,
529+ } as never ) ;
530+ client . rawRequest
531+ . mockResolvedValueOnce (
532+ mockResponse ( { accessToken : "tok-a" , tenantId : "tid-aaa" , availableTenants : tenants } ) ,
533+ )
534+ . mockResolvedValueOnce (
535+ mockResponse ( { accessToken : "tok-b" , refreshToken : "ref-b" , tenantId : "tid-bbb" } ) ,
536+ ) ;
537+ const program = makeProgram ( ) ;
538+ await runCommand ( program , [ "auth" , "login" ] ) ;
539+ // Should NOT prompt — resolved by --service name
540+ expect ( promptTenantSelection ) . not . toHaveBeenCalled ( ) ;
541+ // Should re-login with resolved tenantId
542+ expect ( client . rawRequest ) . toHaveBeenLastCalledWith ( "POST" , "/auth/login" , {
543+ body : { email : "user@example.com" , password : "pass123" , tenantId : "tid-bbb" } ,
544+ skipTenantHeader : true ,
545+ } ) ;
546+ expect ( saveConfig ) . toHaveBeenCalledWith (
547+ expect . objectContaining ( { token : "tok-b" , service : "tid-bbb" , tenantId : "tid-bbb" } ) ,
548+ "default" ,
549+ ) ;
550+ } ) ;
551+
552+ it ( "resolves tenant by tenantId via --service flag" , async ( ) => {
553+ const tenants = [
554+ { tenantId : "city_a" , role : "tenant_admin" } ,
555+ { tenantId : "city_b" , role : "user" } ,
556+ ] ;
557+ vi . mocked ( resolveOptions ) . mockReturnValue ( {
558+ url : "http://localhost:3000" ,
559+ profile : "default" ,
560+ token : "test-token" ,
561+ format : "json" ,
562+ service : "city_b" ,
563+ } as never ) ;
564+ client . rawRequest
565+ . mockResolvedValueOnce (
566+ mockResponse ( { accessToken : "tok-a" , tenantId : "city_a" , availableTenants : tenants } ) ,
567+ )
568+ . mockResolvedValueOnce (
569+ mockResponse ( { accessToken : "tok-b" , tenantId : "city_b" } ) ,
570+ ) ;
571+ const program = makeProgram ( ) ;
572+ await runCommand ( program , [ "auth" , "login" ] ) ;
573+ expect ( promptTenantSelection ) . not . toHaveBeenCalled ( ) ;
574+ expect ( saveConfig ) . toHaveBeenCalledWith (
575+ expect . objectContaining ( { token : "tok-b" , service : "city_b" } ) ,
576+ "default" ,
577+ ) ;
578+ } ) ;
579+
580+ it ( "prints error when --service tenant name not found" , async ( ) => {
581+ const tenants = [
582+ { tenantId : "city_a" , name : "Smart City A" , role : "tenant_admin" } ,
583+ { tenantId : "city_b" , role : "user" } ,
584+ ] ;
585+ vi . mocked ( resolveOptions ) . mockReturnValue ( {
586+ url : "http://localhost:3000" ,
587+ profile : "default" ,
588+ token : "test-token" ,
589+ format : "json" ,
590+ service : "nonexistent" ,
591+ } as never ) ;
592+ client . rawRequest . mockResolvedValue (
593+ mockResponse ( { accessToken : "tok" , tenantId : "city_a" , availableTenants : tenants } ) ,
594+ ) ;
595+ const program = makeProgram ( ) ;
596+ await expect (
597+ runCommand ( program , [ "auth" , "login" ] ) ,
598+ ) . rejects . toThrow ( "process.exit" ) ;
599+ expect ( printError ) . toHaveBeenCalledWith (
600+ expect . stringContaining ( 'Tenant "nonexistent" not found' ) ,
601+ ) ;
602+ } ) ;
603+
604+ it ( "includes tenant label in success message" , async ( ) => {
605+ const tenants = [
606+ { tenantId : "city_a" , name : "Smart City A" , role : "tenant_admin" } ,
607+ ] ;
608+ client . rawRequest . mockResolvedValue (
609+ mockResponse ( { accessToken : "tok" , tenantId : "city_a" , availableTenants : tenants } ) ,
610+ ) ;
611+ const program = makeProgram ( ) ;
612+ await runCommand ( program , [ "auth" , "login" ] ) ;
613+ expect ( printSuccess ) . toHaveBeenCalledWith (
614+ "Login successful (tenant: Smart City A). Token saved to config." ,
615+ ) ;
616+ } ) ;
494617 } ) ;
495618
496619 describe ( "auth logout" , ( ) => {
0 commit comments