fix(copilot): harden provider configuration flow
This commit is contained in:
+24
@@ -86,4 +86,28 @@ describe('ProviderConfiguration', () => {
|
||||
expect(wrapper.emitted('save')).toBeUndefined();
|
||||
confirm.mockRestore();
|
||||
});
|
||||
|
||||
it('allows clearing a saved key without allowing a connection test', async () => {
|
||||
const confirm = vi.spyOn(window, 'confirm').mockReturnValue(true);
|
||||
const wrapper = mount(ProviderConfiguration, {
|
||||
props: { config: configuredProvider },
|
||||
});
|
||||
|
||||
await wrapper.get('[data-test-id="clear-chat-api-key"]').setValue(true);
|
||||
|
||||
expect(
|
||||
wrapper.get('[data-test-id="save-provider"]').attributes('disabled')
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
wrapper.get('[data-test-id="test-provider"]').attributes('disabled')
|
||||
).toBeDefined();
|
||||
|
||||
await wrapper.get('[data-test-id="save-provider"]').trigger('click');
|
||||
expect(confirm).toHaveBeenCalledOnce();
|
||||
expect(wrapper.emitted('save')?.[0]?.[0].chat).toMatchObject({
|
||||
api_key: '',
|
||||
clear_api_key: true,
|
||||
});
|
||||
confirm.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
+30
-9
@@ -130,21 +130,36 @@ const hasEmbeddingCredential = computed(
|
||||
(Boolean(props.config?.embedding?.api_key?.configured) &&
|
||||
!form.embedding.clearApiKey)
|
||||
);
|
||||
const isFormComplete = computed(
|
||||
const isConfigurationValid = computed(
|
||||
() =>
|
||||
Boolean(
|
||||
form.chat.provider &&
|
||||
form.chat.baseUrl &&
|
||||
form.chat.model &&
|
||||
form.embedding.model &&
|
||||
form.embedding.dimensions >= 1 &&
|
||||
hasChatCredential.value &&
|
||||
hasEmbeddingCredential.value
|
||||
form.embedding.dimensions >= 1
|
||||
) &&
|
||||
(!isSeparateEmbedding.value ||
|
||||
Boolean(form.embedding.provider && form.embedding.baseUrl)) &&
|
||||
!invalidAnthropicReuse.value
|
||||
);
|
||||
const canTest = computed(
|
||||
() =>
|
||||
isConfigurationValid.value &&
|
||||
hasChatCredential.value &&
|
||||
hasEmbeddingCredential.value
|
||||
);
|
||||
const canSave = computed(
|
||||
() =>
|
||||
isConfigurationValid.value &&
|
||||
(hasChatCredential.value ||
|
||||
(Boolean(props.config?.chat?.api_key?.configured) &&
|
||||
form.chat.clearApiKey)) &&
|
||||
(!isSeparateEmbedding.value ||
|
||||
hasEmbeddingCredential.value ||
|
||||
(Boolean(props.config?.embedding?.api_key?.configured) &&
|
||||
form.embedding.clearApiKey))
|
||||
);
|
||||
const activeHealth = computed(() => props.testResult || props.config?.health);
|
||||
const reindexProgress = computed(() => {
|
||||
if (!props.reindexStatus?.total) return 0;
|
||||
@@ -296,7 +311,7 @@ const resultClass = ok =>
|
||||
'CAPTAIN_SETTINGS.PROVIDER.API_KEY_CONFIGURED',
|
||||
{
|
||||
masked:
|
||||
config.chat.api_key.masked_value ||
|
||||
config.chat.api_key.masked_value ||
|
||||
t(
|
||||
'CAPTAIN_SETTINGS.PROVIDER.API_KEY_MASKED'
|
||||
),
|
||||
@@ -312,7 +327,11 @@ const resultClass = ok =>
|
||||
v-if="config?.chat?.api_key?.configured && !readOnly"
|
||||
class="flex items-center gap-2 text-xs text-n-ruby-11"
|
||||
>
|
||||
<input v-model="form.chat.clearApiKey" type="checkbox" />
|
||||
<input
|
||||
v-model="form.chat.clearApiKey"
|
||||
data-test-id="clear-chat-api-key"
|
||||
type="checkbox"
|
||||
/>
|
||||
{{ t('CAPTAIN_SETTINGS.PROVIDER.CLEAR_CHAT_KEY') }}
|
||||
</label>
|
||||
</label>
|
||||
@@ -393,7 +412,8 @@ const resultClass = ok =>
|
||||
'CAPTAIN_SETTINGS.PROVIDER.API_KEY_CONFIGURED',
|
||||
{
|
||||
masked:
|
||||
config.embedding.api_key.masked_value ||
|
||||
config.embedding.api_key
|
||||
.masked_value ||
|
||||
t(
|
||||
'CAPTAIN_SETTINGS.PROVIDER.API_KEY_MASKED'
|
||||
),
|
||||
@@ -408,6 +428,7 @@ const resultClass = ok =>
|
||||
>
|
||||
<input
|
||||
v-model="form.embedding.clearApiKey"
|
||||
data-test-id="clear-embedding-api-key"
|
||||
type="checkbox"
|
||||
/>
|
||||
{{ t('CAPTAIN_SETTINGS.PROVIDER.CLEAR_EMBEDDING_KEY') }}
|
||||
@@ -618,7 +639,7 @@ const resultClass = ok =>
|
||||
type="button"
|
||||
data-test-id="test-provider"
|
||||
class="rounded-lg border border-n-weak bg-n-solid-2 px-4 py-2 text-sm font-medium text-n-slate-12 hover:bg-n-alpha-2 disabled:opacity-60"
|
||||
:disabled="isTesting || isSaving || !isFormComplete"
|
||||
:disabled="isTesting || isSaving || !canTest"
|
||||
@click="test"
|
||||
>
|
||||
{{
|
||||
@@ -631,7 +652,7 @@ const resultClass = ok =>
|
||||
type="button"
|
||||
data-test-id="save-provider"
|
||||
class="rounded-lg bg-n-brand px-4 py-2 text-sm font-medium text-white hover:bg-n-brand/90 disabled:opacity-60"
|
||||
:disabled="isSaving || isTesting || !isFormComplete"
|
||||
:disabled="isSaving || isTesting || !canSave"
|
||||
@click="save"
|
||||
>
|
||||
{{
|
||||
|
||||
Reference in New Issue
Block a user