@@ -5,6 +5,7 @@ import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-libra
55import { afterEach , describe , expect , it , vi } from 'vitest' ;
66import type { AgentRuntimeOnboarding } from '../../../agent-runtime/onboarding' ;
77import { AddAccountForm , ServiceCatalogView } from '../add-flow' ;
8+ import type { ModelSources } from '../model-selection' ;
89
910function translateKey ( key : string ) : string {
1011 return key ;
@@ -39,6 +40,13 @@ function signedOutRuntimes(): AgentRuntimes {
3940 } ;
4041}
4142
43+ function addModel ( id = 'test-model' ) : void {
44+ fireEvent . change ( screen . getByPlaceholderText ( 'models.addPlaceholder' ) , {
45+ target : { value : id } ,
46+ } ) ;
47+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'models.add' } ) ) ;
48+ }
49+
4250describe ( 'subscription account creation' , ( ) => {
4351 it ( 'starts Claude login and creates the account only from the success callback' , ( ) => {
4452 const login = vi . fn ( ) ;
@@ -57,6 +65,8 @@ describe('subscription account creation', () => {
5765 /> ,
5866 ) ;
5967
68+ expect ( screen . queryByRole ( 'button' , { name : 'login' } ) ) . toBeNull ( ) ;
69+ addModel ( 'claude-sonnet-5' ) ;
6070 fireEvent . click ( screen . getByRole ( 'button' , { name : 'login' } ) ) ;
6171 expect ( login ) . toHaveBeenCalledWith ( 'claude-code' , expect . any ( Function ) ) ;
6272 expect ( onSubmit ) . not . toHaveBeenCalled ( ) ;
@@ -67,6 +77,7 @@ describe('subscription account creation', () => {
6777 expect . objectContaining ( {
6878 service : 'claude-sub' ,
6979 credential : { type : 'oauth' , agent : 'claude-code' } ,
80+ models : [ { id : 'claude-sonnet-5' } ] ,
7081 } ) ,
7182 ) ;
7283 } ) ;
@@ -126,12 +137,17 @@ describe('subscription account creation', () => {
126137 /> ,
127138 ) ;
128139
129- fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
140+ const submit = screen . getByRole ( 'button' , { name : 'form.submit' } ) ;
141+ expect ( submit ) . toHaveProperty ( 'disabled' , true ) ;
142+ expect ( screen . getByText ( 'models.required' ) ) . toBeTruthy ( ) ;
143+ addModel ( 'gpt-5.6-sol' ) ;
144+ fireEvent . click ( submit ) ;
130145 expect ( login ) . not . toHaveBeenCalled ( ) ;
131146 expect ( onSubmit ) . toHaveBeenCalledWith (
132147 expect . objectContaining ( {
133148 service : 'chatgpt-sub' ,
134149 credential : { type : 'oauth' , agent : 'codex' } ,
150+ models : [ { id : 'gpt-5.6-sol' } ] ,
135151 } ) ,
136152 ) ;
137153 } ) ;
@@ -154,12 +170,16 @@ describe('non-subscription account creation', () => {
154170
155171 fireEvent . change ( screen . getByPlaceholderText ( 'sk-ant-…' ) , { target : { value : 'sk-ant-test' } } ) ;
156172 fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
173+ await waitFor ( ( ) => expect ( onSubmit ) . not . toHaveBeenCalled ( ) ) ;
174+ addModel ( 'claude-sonnet-5' ) ;
175+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
157176 expect ( login ) . not . toHaveBeenCalled ( ) ;
158177 await waitFor ( ( ) =>
159178 expect ( onSubmit ) . toHaveBeenCalledWith (
160179 expect . objectContaining ( {
161180 service : 'anthropic-api' ,
162181 credential : { type : 'api-key' , key : 'sk-ant-test' } ,
182+ models : [ { id : 'claude-sonnet-5' } ] ,
163183 } ) ,
164184 ) ,
165185 ) ;
@@ -187,13 +207,15 @@ describe('non-subscription account creation', () => {
187207 const secret = document . querySelector < HTMLInputElement > ( 'input[type="password"]' ) ;
188208 if ( ! secret ) throw new Error ( 'credential input missing' ) ;
189209 fireEvent . change ( secret , { target : { value : 'stepfun-test-key' } } ) ;
210+ addModel ( 'step-3.5-flash' ) ;
190211 fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
191212
192213 await waitFor ( ( ) =>
193214 expect ( onSubmit ) . toHaveBeenCalledWith (
194215 expect . objectContaining ( {
195216 service : 'stepfun' ,
196217 credential : { type : 'api-key' , key : 'stepfun-test-key' } ,
218+ models : [ { id : 'step-3.5-flash' } ] ,
197219 } ) ,
198220 ) ,
199221 ) ;
@@ -217,10 +239,17 @@ describe('non-subscription account creation', () => {
217239
218240 it ( 'adds LinkCode Gateway only after the explicit user action' , async ( ) => {
219241 const createKey = vi . fn ( ) . mockResolvedValue ( 'lc-gateway-key' ) ;
242+ const probeInline = vi . fn ( ) . mockResolvedValue ( [ { id : 'anthropic/claude-sonnet-5' } ] ) ;
243+ const sources : ModelSources = {
244+ probeInline,
245+ probeAccount : vi . fn ( ) ,
246+ oauth : vi . fn ( ) ,
247+ } ;
220248 const onSubmit = vi . fn ( ) ;
221249 render (
222250 < AddAccountForm
223251 serviceId = "linkcode-gateway"
252+ sources = { sources }
224253 runtimes = { undefined }
225254 onboarding = { onboarding ( ) }
226255 busy = { false }
@@ -241,10 +270,15 @@ describe('non-subscription account creation', () => {
241270
242271 await waitFor ( ( ) => expect ( onSubmit ) . toHaveBeenCalledTimes ( 1 ) ) ;
243272 expect ( createKey ) . toHaveBeenCalledWith ( 'serviceName.linkcode-gateway' ) ;
273+ expect ( probeInline ) . toHaveBeenCalledWith ( 'linkcode-gateway' , {
274+ type : 'auth-token' ,
275+ token : 'lc-gateway-key' ,
276+ } ) ;
244277 expect ( onSubmit ) . toHaveBeenCalledWith (
245278 expect . objectContaining ( {
246279 service : 'linkcode-gateway' ,
247280 credential : { type : 'auth-token' , token : 'lc-gateway-key' } ,
281+ models : [ { id : 'anthropic/claude-sonnet-5' } ] ,
248282 } ) ,
249283 ) ;
250284 expect ( onSubmit . mock . calls [ 0 ] ?. [ 0 ] ) . not . toHaveProperty ( 'endpoint' ) ;
@@ -282,6 +316,7 @@ describe('non-subscription account creation', () => {
282316 const secret = container . querySelector ( 'input[type="password"]' ) ;
283317 if ( ! secret ) throw new Error ( 'credential input missing' ) ;
284318 fireEvent . change ( secret , { target : { value : 'cf-token' } } ) ;
319+ addModel ( 'gateway-model' ) ;
285320 fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
286321
287322 await waitFor ( ( ) => expect ( onSubmit ) . toHaveBeenCalled ( ) ) ;
@@ -290,8 +325,40 @@ describe('non-subscription account creation', () => {
290325 service : 'cloudflare-gateway' ,
291326 credential : { type : 'auth-token' , token : 'cf-token' } ,
292327 endpointParams : { account_id : '8f3a' , gateway_id : 'prod' } ,
328+ models : [ { id : 'gateway-model' } ] ,
293329 } ) ;
294330 // One key can resolve to a different endpoint per agent, so none is pinned here.
295331 expect ( account ) . not . toHaveProperty ( 'endpoint' ) ;
296332 } ) ;
333+
334+ it ( 'requires a model for a custom endpoint' , async ( ) => {
335+ const onSubmit = vi . fn ( ) ;
336+ const { container } = render (
337+ < AddAccountForm
338+ serviceId = "custom"
339+ runtimes = { undefined }
340+ onboarding = { onboarding ( ) }
341+ busy = { false }
342+ onBack = { vi . fn ( ) }
343+ onSubmit = { onSubmit }
344+ /> ,
345+ ) ;
346+
347+ fireEvent . change ( screen . getByRole ( 'textbox' , { name : 'form.label' } ) , {
348+ target : { value : 'Private endpoint' } ,
349+ } ) ;
350+ const secret = container . querySelector ( 'input[type="password"]' ) ;
351+ if ( ! secret ) throw new Error ( 'credential input missing' ) ;
352+ fireEvent . change ( secret , { target : { value : 'private-key' } } ) ;
353+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
354+ await waitFor ( ( ) => expect ( onSubmit ) . not . toHaveBeenCalled ( ) ) ;
355+
356+ addModel ( 'private-model' ) ;
357+ fireEvent . click ( screen . getByRole ( 'button' , { name : 'form.submit' } ) ) ;
358+ await waitFor ( ( ) =>
359+ expect ( onSubmit ) . toHaveBeenCalledWith (
360+ expect . objectContaining ( { models : [ { id : 'private-model' } ] } ) ,
361+ ) ,
362+ ) ;
363+ } ) ;
297364} ) ;
0 commit comments