H-337: add local Captain acceptance fixture (#63)

* H-337: add local Captain acceptance fixture

* fix(H-337): close acceptance fixture review gaps

* fix(H-337): reject non-string skill metadata

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-20 20:05:40 +08:00
committed by GitHub
co-authored by rogee
parent 60ad320e8d
commit abb1bed424
12 changed files with 818 additions and 19 deletions
@@ -0,0 +1,80 @@
import { flushPromises, shallowMount } from '@vue/test-utils';
import AssistantSystemSettingsForm from './AssistantSystemSettingsForm.vue';
const Editor = {
name: 'Editor',
props: ['modelValue'],
emits: ['update:modelValue'],
template:
'<input data-editor :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" />',
};
const Button = {
name: 'Button',
emits: ['click'],
template: '<button type="button" @click="$emit(\'click\')">save</button>',
};
const mountForm = assistant =>
shallowMount(AssistantSystemSettingsForm, {
props: { assistant },
global: { stubs: { Editor, Button } },
});
it('saves additional instructions for Captain V2 runtime prompts', async () => {
const wrapper = mountForm({
config: {
handoff_message: 'handoff',
resolution_message: 'resolved',
instructions: 'old instructions',
temperature: 0.5,
},
});
expect(wrapper.findAll('[data-editor]')[2].element.value).toBe(
'old instructions'
);
await wrapper.findAll('[data-editor]')[2].setValue('staged SOUL instructions');
await wrapper.find('button').trigger('click');
await flushPromises();
expect(wrapper.emitted('submit')[0][0].config.instructions).toBe(
'staged SOUL instructions'
);
});
it.each([
[
'empty config',
{ config: {} },
{
handoff_message: '',
resolution_message: '',
instructions: '',
temperature: 1,
},
],
[
'assistant created before instructions existed',
{
config: {
handoff_message: 'handoff',
resolution_message: 'resolved',
temperature: 0.5,
},
},
{
handoff_message: 'handoff',
resolution_message: 'resolved',
instructions: '',
temperature: 0.5,
},
],
])('saves %s without instructions', async (_, assistant, expectedConfig) => {
const wrapper = mountForm(assistant);
await wrapper.find('button').trigger('click');
await flushPromises();
expect(wrapper.emitted('submit')[0][0].config).toMatchObject(expectedConfig);
});
@@ -3,8 +3,6 @@ import { reactive, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useVuelidate } from '@vuelidate/core';
import { minLength } from '@vuelidate/validators';
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
import { useAccount } from 'dashboard/composables/useAccount';
import Button from 'dashboard/components-next/button/Button.vue';
import Editor from 'dashboard/components-next/Editor/Editor.vue';
@@ -19,11 +17,6 @@ const props = defineProps({
const emit = defineEmits(['submit']);
const { t } = useI18n();
const { isCloudFeatureEnabled } = useAccount();
const isCaptainV2Enabled = computed(() =>
isCloudFeatureEnabled(FEATURE_FLAGS.CAPTAIN_V2)
);
const initialState = {
handoffMessage: '',
@@ -54,9 +47,9 @@ const formErrors = computed(() => ({
const updateStateFromAssistant = assistant => {
const { config = {} } = assistant;
state.handoffMessage = config.handoff_message;
state.resolutionMessage = config.resolution_message;
state.instructions = config.instructions;
state.handoffMessage = config.handoff_message ?? '';
state.resolutionMessage = config.resolution_message ?? '';
state.instructions = config.instructions ?? '';
state.temperature = config.temperature || 1;
};
@@ -64,12 +57,9 @@ const handleSystemMessagesUpdate = async () => {
const validations = [
v$.value.handoffMessage.$validate(),
v$.value.resolutionMessage.$validate(),
v$.value.instructions.$validate(),
];
if (!isCaptainV2Enabled.value) {
validations.push(v$.value.instructions.$validate());
}
const result = await Promise.all(validations).then(results =>
results.every(Boolean)
);
@@ -80,14 +70,11 @@ const handleSystemMessagesUpdate = async () => {
...props.assistant.config,
handoff_message: state.handoffMessage,
resolution_message: state.resolutionMessage,
instructions: state.instructions,
temperature: state.temperature || 1,
},
};
if (!isCaptainV2Enabled.value) {
payload.config.instructions = state.instructions;
}
emit('submit', payload);
};
@@ -121,7 +108,6 @@ watch(
/>
<Editor
v-if="!isCaptainV2Enabled"
v-model="state.instructions"
:label="t('CAPTAIN.ASSISTANTS.FORM.INSTRUCTIONS.LABEL')"
:placeholder="t('CAPTAIN.ASSISTANTS.FORM.INSTRUCTIONS.PLACEHOLDER')"