From 396f5193d3686877c03309ce39f1fbeed98e3832 Mon Sep 17 00:00:00 2001 From: Gros Frumos Date: Tue, 17 Mar 2026 15:49:37 +0200 Subject: [PATCH] kin: auto-commit after pipeline --- tests/test_api.py | 23 ++++++++++++++ web/frontend/src/api.ts | 3 +- web/frontend/src/views/SettingsView.vue | 41 +++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index d71bf25..f4d9924 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2151,3 +2151,26 @@ def test_create_project_with_test_command(client): conn.close() assert row is not None assert row[0] == "npm test" + + +def test_patch_project_test_command_empty_string_stores_empty(client): + """KIN-ARCH-008: PATCH с пустой строкой сохраняет пустую строку (не NULL, в отличие от deploy_command).""" + client.patch("/api/projects/p1", json={"test_command": "pytest -v"}) + client.patch("/api/projects/p1", json={"test_command": ""}) + + from core.db import init_db + conn = init_db(api_module.DB_PATH) + row = conn.execute("SELECT test_command FROM projects WHERE id = 'p1'").fetchone() + conn.close() + assert row[0] == "" + + +def test_get_projects_includes_test_command(client): + """KIN-ARCH-008: GET /api/projects возвращает test_command — нужно для инициализации фронтенда.""" + client.patch("/api/projects/p1", json={"test_command": "cargo test"}) + r = client.get("/api/projects") + assert r.status_code == 200 + projects = r.json() + p1 = next((p for p in projects if p["id"] == "p1"), None) + assert p1 is not None + assert p1["test_command"] == "cargo test" diff --git a/web/frontend/src/api.ts b/web/frontend/src/api.ts index 69cae1d..537a466 100644 --- a/web/frontend/src/api.ts +++ b/web/frontend/src/api.ts @@ -70,6 +70,7 @@ export interface Project { autocommit_enabled: number | null obsidian_vault_path: string | null deploy_command: string | null + test_command: string | null created_at: string total_tasks: number done_tasks: number @@ -315,7 +316,7 @@ export const api = { post<{ updated: string[]; count: number }>(`/projects/${projectId}/audit/apply`, { task_ids: taskIds }), patchTask: (id: string, data: { status?: string; execution_mode?: string; priority?: number; route_type?: string; title?: string; brief_text?: string; acceptance_criteria?: string }) => patch(`/tasks/${id}`, data), - patchProject: (id: string, data: { execution_mode?: string; autocommit_enabled?: boolean; obsidian_vault_path?: string; deploy_command?: string; project_type?: string; ssh_host?: string; ssh_user?: string; ssh_key_path?: string; ssh_proxy_jump?: string }) => + patchProject: (id: string, data: { execution_mode?: string; autocommit_enabled?: boolean; obsidian_vault_path?: string; deploy_command?: string; test_command?: string; project_type?: string; ssh_host?: string; ssh_user?: string; ssh_key_path?: string; ssh_proxy_jump?: string }) => patch(`/projects/${id}`, data), deployProject: (projectId: string) => post(`/projects/${projectId}/deploy`, {}), diff --git a/web/frontend/src/views/SettingsView.vue b/web/frontend/src/views/SettingsView.vue index d3f910c..59bb026 100644 --- a/web/frontend/src/views/SettingsView.vue +++ b/web/frontend/src/views/SettingsView.vue @@ -5,11 +5,14 @@ import { api, type Project, type ObsidianSyncResult } from '../api' const projects = ref([]) const vaultPaths = ref>({}) const deployCommands = ref>({}) +const testCommands = ref>({}) const saving = ref>({}) const savingDeploy = ref>({}) +const savingTest = ref>({}) const syncing = ref>({}) const saveStatus = ref>({}) const saveDeployStatus = ref>({}) +const saveTestStatus = ref>({}) const syncResults = ref>({}) const error = ref(null) @@ -19,6 +22,7 @@ onMounted(async () => { for (const p of projects.value) { vaultPaths.value[p.id] = p.obsidian_vault_path ?? '' deployCommands.value[p.id] = p.deploy_command ?? '' + testCommands.value[p.id] = p.test_command ?? '' } } catch (e) { error.value = String(e) @@ -51,6 +55,19 @@ async function saveDeployCommand(projectId: string) { } } +async function saveTestCommand(projectId: string) { + savingTest.value[projectId] = true + saveTestStatus.value[projectId] = '' + try { + await api.patchProject(projectId, { test_command: testCommands.value[projectId] }) + saveTestStatus.value[projectId] = 'Saved' + } catch (e) { + saveTestStatus.value[projectId] = `Error: ${e}` + } finally { + savingTest.value[projectId] = false + } +} + async function runSync(projectId: string) { syncing.value[projectId] = true syncResults.value[projectId] = null @@ -112,6 +129,30 @@ async function runSync(projectId: string) { +
+ + +

Команда запуска тестов, выполняется через shell в директории проекта.

+
+ +
+ + + {{ saveTestStatus[project.id] }} + +
+