@@ -60,6 +60,21 @@ def setUp(self) -> None:
6060 "last_refresh" : "2029-01-01T00:00:00+00:00" ,
6161 }
6262
63+ @staticmethod
64+ def _agent_identity (account_id : str , email : str ) -> dict :
65+ return {
66+ "auth_mode" : "agent_identity" ,
67+ "agent_identity" : {
68+ "agent_runtime_id" : f"runtime-{ account_id } " ,
69+ "agent_private_key" : f"private-{ account_id } " ,
70+ "account_id" : account_id ,
71+ "chatgpt_user_id" : f"user-{ account_id } " ,
72+ "email" : email ,
73+ "plan_type" : "free" ,
74+ "chatgpt_account_is_fedramp" : False ,
75+ },
76+ }
77+
6378 def test_sub2api_export_uses_standard_account_envelope (self ) -> None :
6479 with patch .object (accounts_api , "require_admin" ), patch .object (
6580 accounts_api .account_service ,
@@ -100,6 +115,78 @@ def test_cpa_export_is_zip_of_codex_auth_files(self) -> None:
100115 self .assertEqual (exported ["type" ], "codex" )
101116 self .assertEqual (exported ["access_token" ], "access-token" )
102117
118+ def test_single_agent_identity_export_is_auth_json (self ) -> None :
119+ auth_json = self ._agent_identity ("one" , "one@example.test" )
120+ with patch .object (accounts_api , "require_admin" ), patch .object (
121+ accounts_api .account_service ,
122+ "list_accounts" ,
123+ return_value = [self .item ],
124+ ), patch .object (
125+ accounts_api ,
126+ "ensure_openai_agent_identity" ,
127+ return_value = auth_json ,
128+ ):
129+ response = self .client .post (
130+ "/api/accounts/export" ,
131+ json = {"access_tokens" : ["access-token" ], "format" : "agent_identity" },
132+ )
133+
134+ self .assertEqual (response .status_code , 200 )
135+ self .assertEqual (response .headers ["content-type" ], "application/json" )
136+ self .assertEqual (response .headers ["content-disposition" ], 'attachment; filename="auth.json"' )
137+ self .assertEqual (response .json (), auth_json )
138+
139+ def test_multiple_agent_identity_export_is_zip (self ) -> None :
140+ accounts = [
141+ {** self .item , "access_token" : "token-one" , "email" : "one@example.test" },
142+ {** self .item , "access_token" : "token-two" , "email" : "two@example.test" },
143+ ]
144+ identities = [
145+ self ._agent_identity ("one" , "one@example.test" ),
146+ self ._agent_identity ("two" , "two@example.test" ),
147+ ]
148+ with patch .object (accounts_api , "require_admin" ), patch .object (
149+ accounts_api .account_service ,
150+ "list_accounts" ,
151+ return_value = accounts ,
152+ ), patch .object (
153+ accounts_api ,
154+ "ensure_openai_agent_identity" ,
155+ side_effect = identities ,
156+ ):
157+ response = self .client .post (
158+ "/api/accounts/export" ,
159+ json = {"access_tokens" : [], "format" : "agent_identity" },
160+ )
161+
162+ self .assertEqual (response .status_code , 200 )
163+ self .assertEqual (response .headers ["content-type" ], "application/zip" )
164+ with zipfile .ZipFile (io .BytesIO (response .content )) as archive :
165+ self .assertEqual (archive .namelist (), [
166+ "one-example.test/auth.json" ,
167+ "two-example.test/auth.json" ,
168+ ])
169+ exported = [json .loads (archive .read (name )) for name in archive .namelist ()]
170+ self .assertEqual (exported , identities )
171+
172+ def test_agent_identity_list_does_not_include_private_keys (self ) -> None :
173+ summaries = [{
174+ "account_id" : "one" ,
175+ "email" : "one@example.test" ,
176+ "agent_runtime_id" : "runtime-one" ,
177+ "updated_at" : "2030-01-01T00:00:00+00:00" ,
178+ }]
179+ with patch .object (accounts_api , "require_admin" ), patch .object (
180+ accounts_api .openai_agent_identity_store ,
181+ "summary" ,
182+ return_value = summaries ,
183+ ):
184+ response = self .client .get ("/api/accounts/agent-identities" )
185+
186+ self .assertEqual (response .status_code , 200 )
187+ self .assertEqual (response .json (), {"total" : 1 , "items" : summaries })
188+ self .assertNotIn ("agent_private_key" , response .text )
189+
103190
104191if __name__ == "__main__" :
105192 unittest .main ()
0 commit comments