From 37d92243366b3628c2d3f1d3ceee0b7bb751c970 Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Mon, 5 Oct 2026 14:00:24 +0300 Subject: [PATCH 1/4] test: verify external connection widget --- .github/workflows/app-smoke.yml | 8 ++++++++ README.md | 10 ++++++++++ Taskfile.yml | 6 ++++++ compose/connection-test.local.yaml | 15 +++++++++++++++ config/versions.env | 10 +++++----- docs/development.md | 6 ++++++ docs/runbook.md | 11 +++++++++++ scripts/check-compose.ps1 | 22 ++++++++++++++++++++-- scripts/run-test-lab.ps1 | 2 +- scripts/start-local.ps1 | 5 +++++ 10 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 compose/connection-test.local.yaml diff --git a/.github/workflows/app-smoke.yml b/.github/workflows/app-smoke.yml index 9d3e2f8..8619d68 100644 --- a/.github/workflows/app-smoke.yml +++ b/.github/workflows/app-smoke.yml @@ -130,6 +130,14 @@ jobs: shell: pwsh run: ./scripts/run-test-lab.ps1 + - name: Включить тестовый профиль подключения + shell: pwsh + run: ./scripts/start-local.ps1 -Apps -ConnectionTest + + - name: Проверить виджет подключения + shell: pwsh + run: ./scripts/run-test-lab.ps1 -TaskName test:connection + - name: Удалить только тестовый Compose project if: always() shell: pwsh diff --git a/README.md b/README.md index 6198b2e..292f677 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,16 @@ task test:model Команда проверяет путь до локальной модели, разбор относительной даты и создание действия, но не входит в обычный CI: на CPU ответ может занимать десятки секунд. +Чтобы проверить поведение при отсутствии подключения Google Calendar, выполни: + +```powershell +task test:connection +``` + +Команда поднимает отдельный тестовый override только с коннектором `google-calendar` и запускает +сценарий из `test-lab`. Он проверяет виджет подключения, OAuth state, PKCE и свежую ссылку при +повторном запросе. Настоящий Google API не вызывается, реальные credentials не нужны. + Профиль направляет Agent Runtime на `host.docker.internal:11434`, выбирает `qwen2.5:7b` и увеличивает тайм-ауты только на пути до модели. Значения можно переопределить в локальном `.env`. Ollama и модель не входят в Compose: разработчик управляет ими отдельно, а CI остаётся быстрым и воспроизводимым. diff --git a/Taskfile.yml b/Taskfile.yml index 378ac94..fac6df1 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -107,6 +107,12 @@ tasks: - task: services:model:up - '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/run-test-lab.ps1 -TaskName test:model' + test:connection: + desc: Поднять тестовый Google OAuth профиль и проверить виджет подключения + cmds: + - '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/start-local.ps1 -Apps -ConnectionTest' + - '{{.POWERSHELL}} {{.POWERSHELL_ARGS}} -File ./scripts/run-test-lab.ps1 -TaskName test:connection' + verify: desc: Выполнить локальные проверки конфигурации и документации cmds: diff --git a/compose/connection-test.local.yaml b/compose/connection-test.local.yaml new file mode 100644 index 0000000..5a157d2 --- /dev/null +++ b/compose/connection-test.local.yaml @@ -0,0 +1,15 @@ +services: + conversation-service: + environment: + AVAILABLE_CONNECTORS: google-calendar + CONNECTION_URL: http://connection-service:8080 + depends_on: + connection-service: + condition: service_healthy + + connection-service: + environment: + GOOGLE_OAUTH_ENABLED: "true" + GOOGLE_OAUTH_CLIENT_ID: portable-agent-connection-test + GOOGLE_OAUTH_CLIENT_SECRET: local-test-value-not-a-real-secret + GOOGLE_OAUTH_REDIRECT_URI: https://portable-agent.test/api/v1/connections/callback diff --git a/config/versions.env b/config/versions.env index f1a3225..380b48e 100644 --- a/config/versions.env +++ b/config/versions.env @@ -13,12 +13,12 @@ LOKI_IMAGE=grafana/loki:3.7.7 BUSYBOX_IMAGE=busybox:1.37.0 WIREMOCK_IMAGE=wiremock/wiremock:3.13.2 CLOUDFLARED_IMAGE=cloudflare/cloudflared@sha256:072c067d25ccbe61d46e18f0d0723255f2bb5304f7317caa95b27031520ff92c -CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:c1f8dbf209d641008a627230d4d793d4454cbaf3 +CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:1ab140f73121e256b55e749468f010dd1fc57b3b AGENT_RUNTIME_IMAGE=ghcr.io/portable-agent/agent-runtime:f50ea7d5c722c93df1519e7ac1d12d210f439726 ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:74152ac0a8832a8879126db2c500ae555c9b77f7 -CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:fb069a35c6a8406f88f665e8197ebfbc3564008a +CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:574b2995ab4b613cc844fbff37f276749afd5571 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:922390b293345f7483dcdee20d5020f417bd7f39 CALENDAR_MCP_IMAGE=ghcr.io/portable-agent/calendar-mcp:30b869b4f9f1c23a4be59e8cf5a3b3a3d3c89799 -CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:f86ff7b1b3fa4da16ff580d5e36f29eccdbf1c1a -TELEGRAM_ADAPTER_IMAGE=ghcr.io/portable-agent/telegram-adapter:c2be5c859cb7b3ddb8fae4b3561979b8db611e9e -TEST_LAB_REF=6969e1bbae930146cd149276167137ff74fb2e98 +CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:24e1e4f0f28a075dc0b8376d383351f05cf7ccff +TELEGRAM_ADAPTER_IMAGE=ghcr.io/portable-agent/telegram-adapter:bd9d19a0154340524438a16638ecf308873a5adf +TEST_LAB_REF=c91a81cd97ac2e3b6f8ad78783b2289040c0dc7c diff --git a/docs/development.md b/docs/development.md index 7873f3f..7fec88b 100644 --- a/docs/development.md +++ b/docs/development.md @@ -22,6 +22,12 @@ Taskfile — единая точка входа. Внутренние PowerShell Команда `task test:model` сама поднимает этот профиль и запускает отдельный opt-in сценарий из `test-lab`; быстрый `task test:e2e` по-прежнему использует детерминированную demo-модель. +Команда `task test:connection` поднимает приложения с override +`compose/connection-test.local.yaml`. В нём Conversation Service предлагает только +`google-calendar`, а Connection Service строит ссылку с тестовой OAuth-конфигурацией. Сценарий не +обращается к Google: он проверяет контракт виджета, state, PKCE и выдачу свежей ссылки. Реальные +credentials по-прежнему хранятся только в локальном `.env` и для этой проверки не нужны. + Допустимые имена: `channel-gateway`, `conversation-service`, `agent-runtime`, `action-service`, `mcp-gateway`, `calendar-mcp`, `google-calendar-mcp`, `connection-service` и `telegram-adapter`. Неизвестное имя отклоняется до вызова Docker. diff --git a/docs/runbook.md b/docs/runbook.md index 4b7d361..f11a24f 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -40,6 +40,17 @@ Channel Gateway, создаёт действие со случайным `reques Action Service с событием в тестовом календарном коннекторе. Если `test-lab` лежит в другой папке, задай `TEST_LAB_PATH` в `.env`. +Путь запроса подключения внешнего календаря проверяется отдельно: + +```powershell +task test:connection +``` + +Сценарий ожидает виджет `connection` со ссылкой Google, state и PKCE. Если вместо него вернулся +виджет подтверждения, проверь, что команда запустила `compose/connection-test.local.yaml`, а в +Conversation Service значение `AVAILABLE_CONNECTORS` равно `google-calendar`. В чистом CI база +Connection Service пуста; локальное уже созданное подключение может изменить ожидаемый ответ. + Первый Docker build скачивает Gradle и Python packages и может быть заметно медленнее повторных запусков. BuildKit сохраняет слои для следующих сборок. diff --git a/scripts/check-compose.ps1 b/scripts/check-compose.ps1 index 2af6085..c55c959 100644 --- a/scripts/check-compose.ps1 +++ b/scripts/check-compose.ps1 @@ -228,6 +228,7 @@ foreach ($taskName in @( "doctor", "status", "test:e2e", + "test:connection", "test:model", "down", "reset" @@ -245,6 +246,10 @@ if ($taskfile -notmatch 'scripts/start-local\.ps1' ` if ($startScript -notmatch '\[string\]\$Service' -or $startScript -notmatch '\$Service') { throw "start-local должен уметь запускать один выбранный сервис." } +if ($startScript -notmatch '\[switch\]\$ConnectionTest' ` + -or $startScript -notmatch 'compose/connection-test.local.yaml') { + throw "start-local должен поддерживать изолированный connection acceptance профиль." +} $serviceScriptPath = "scripts/service-local.ps1" if (-not (Test-Path -LiteralPath $serviceScriptPath)) { throw "Нет безопасного управления отдельным локальным сервисом." @@ -282,11 +287,21 @@ $testLabRunner = Get-Content -Raw -LiteralPath $testLabRunnerPath if ($testLabRunner -notmatch '\$containerIds\s*=\s*@\(\s*\r?\n\s*\(& docker ps') { throw "Результат docker ps должен сохраняться массивом до обращения по индексу." } -foreach ($required in @("TEST_LAB_PATH", "portable-agent-realm.json", "CALENDAR_TEST_API_KEY", "DOCKER_NETWORK", "docker inspect", "com.docker.compose.service=channel-gateway", "http://channel-gateway:8080", "http://action-service:8080", "http://calendar-mcp:8080", "task", "test:e2e", "test:model", "TaskName")) { +foreach ($required in @("TEST_LAB_PATH", "portable-agent-realm.json", "CALENDAR_TEST_API_KEY", "DOCKER_NETWORK", "docker inspect", "com.docker.compose.service=channel-gateway", "http://channel-gateway:8080", "http://action-service:8080", "http://calendar-mcp:8080", "task", "test:e2e", "test:model", "test:connection", "TaskName")) { if ($testLabRunner -notmatch [regex]::Escape($required)) { throw "Адаптер test-lab не содержит $required." } } +$connectionOverridePath = "compose/connection-test.local.yaml" +if (-not (Test-Path -LiteralPath $connectionOverridePath)) { + throw "Нет Compose override для connection acceptance." +} +$connectionOverride = Get-Content -Raw -LiteralPath $connectionOverridePath +foreach ($required in @("AVAILABLE_CONNECTORS", "google-calendar", "GOOGLE_OAUTH_ENABLED", "GOOGLE_OAUTH_CLIENT_ID", "GOOGLE_OAUTH_CLIENT_SECRET", "GOOGLE_OAUTH_REDIRECT_URI")) { + if ($connectionOverride -notmatch [regex]::Escape($required)) { + throw "Connection acceptance override не содержит $required." + } +} $versions = Get-Content -Raw -LiteralPath "config/versions.env" if ($versions -notmatch '(?m)^CLOUDFLARED_IMAGE=cloudflare/cloudflared@sha256:[0-9a-f]{64}\r?$') { throw "Cloudflared image должен быть закреплён digest." @@ -380,7 +395,7 @@ if (-not (Test-Path -LiteralPath $appWorkflowPath)) { throw "Нет CI-проверки полного Compose-среза." } $appWorkflow = Get-Content -Raw -LiteralPath $appWorkflowPath -foreach ($required in @("versions.env", "channel-gateway", "agent-runtime", "CONVERSATION_SERVICE_CONTEXT", "repository: portable-agent/conversation-service", 'ref: ${{ steps.versions.outputs.conversation }}', "CONNECTION_SERVICE_CONTEXT", "repository: portable-agent/connection-service", 'ref: ${{ steps.versions.outputs.connection }}', "TELEGRAM_ADAPTER_CONTEXT", "repository: portable-agent/telegram-adapter", 'ref: ${{ steps.versions.outputs.telegram }}', "repository: portable-agent/test-lab", 'ref: ${{ steps.versions.outputs.test_lab }}', "start-local.ps1 -Apps", "run-test-lab.ps1", "stop-local.ps1 -DeleteData", "if: always()")) { +foreach ($required in @("versions.env", "channel-gateway", "agent-runtime", "CONVERSATION_SERVICE_CONTEXT", "repository: portable-agent/conversation-service", 'ref: ${{ steps.versions.outputs.conversation }}', "CONNECTION_SERVICE_CONTEXT", "repository: portable-agent/connection-service", 'ref: ${{ steps.versions.outputs.connection }}', "TELEGRAM_ADAPTER_CONTEXT", "repository: portable-agent/telegram-adapter", 'ref: ${{ steps.versions.outputs.telegram }}', "repository: portable-agent/test-lab", 'ref: ${{ steps.versions.outputs.test_lab }}', "start-local.ps1 -Apps", "ConnectionTest", "test:connection", "run-test-lab.ps1", "stop-local.ps1 -DeleteData", "if: always()")) { if ($appWorkflow -notmatch [regex]::Escape($required)) { throw "CI-проверка полного среза не содержит $required." } @@ -394,5 +409,8 @@ if ($LASTEXITCODE -ne 0) { throw "Compose config содержит ошибку." & docker compose --env-file .env.example --env-file config/versions.env -f compose/compose.yaml ` -f compose/apps.local.yaml -f compose/model.local.yaml --profile core --profile apps config --quiet if ($LASTEXITCODE -ne 0) { throw "Compose config локальной AI-модели содержит ошибку." } +& docker compose --env-file .env.example --env-file config/versions.env -f compose/compose.yaml ` + -f compose/apps.local.yaml -f compose/connection-test.local.yaml --profile core --profile apps config --quiet +if ($LASTEXITCODE -ne 0) { throw "Compose config connection acceptance содержит ошибку." } Write-Host "Compose config прошёл проверку." diff --git a/scripts/run-test-lab.ps1 b/scripts/run-test-lab.ps1 index fd8b6e5..969f445 100644 --- a/scripts/run-test-lab.ps1 +++ b/scripts/run-test-lab.ps1 @@ -1,6 +1,6 @@ param( [string]$TestLabPath = "", - [ValidateSet("test:e2e", "test:model")] + [ValidateSet("test:e2e", "test:model", "test:connection")] [string]$TaskName = "test:e2e" ) diff --git a/scripts/start-local.ps1 b/scripts/start-local.ps1 index 16cbf41..7772fa2 100644 --- a/scripts/start-local.ps1 +++ b/scripts/start-local.ps1 @@ -2,6 +2,7 @@ [switch]$Observe, [switch]$Apps, [switch]$Model, + [switch]$ConnectionTest, [ValidateSet("channel-gateway", "agent-runtime", "action-service", "conversation-service", "mcp-gateway", "calendar-mcp", "google-calendar-mcp", "connection-service", "telegram-adapter")] [string]$Service ) @@ -10,6 +11,9 @@ if ($Apps -and $Service) { throw "Используй -Apps или -Service, но if ($Model -and -not ($Apps -or $Service)) { throw "Используй -Model вместе с -Apps или -Service." } +if ($ConnectionTest -and -not ($Apps -or $Service)) { + throw "Используй -ConnectionTest вместе с -Apps или -Service." +} . "$PSScriptRoot/local-settings.ps1" $envFiles = @("--env-file", ".env.example", "--env-file", "config/versions.env") if (Test-Path .env) { $envFiles += @("--env-file", ".env") } @@ -18,6 +22,7 @@ $runningOnWindows = $PSVersionTable.PSEdition -eq "Desktop" -or $IsWindows if ($runningOnWindows) { $composeFiles += @("-f", "compose/windows.local.yaml") } $appComposeFiles = @($composeFiles + @("-f", "compose/apps.local.yaml")) if ($Model) { $appComposeFiles += @("-f", "compose/model.local.yaml") } +if ($ConnectionTest) { $appComposeFiles += @("-f", "compose/connection-test.local.yaml") } Initialize-LocalTelegramKey Initialize-LocalConnectionKey $coreProfiles = @("--profile", "core") From 2ed557a7190a1cfc0f6c769b084de98122cbc666 Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Mon, 5 Oct 2026 14:31:14 +0300 Subject: [PATCH 2/4] chore: use secured connection service image --- config/versions.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/versions.env b/config/versions.env index 380b48e..5186828 100644 --- a/config/versions.env +++ b/config/versions.env @@ -19,6 +19,6 @@ ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:74152ac0a8832a8879126 CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:574b2995ab4b613cc844fbff37f276749afd5571 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:922390b293345f7483dcdee20d5020f417bd7f39 CALENDAR_MCP_IMAGE=ghcr.io/portable-agent/calendar-mcp:30b869b4f9f1c23a4be59e8cf5a3b3a3d3c89799 -CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:24e1e4f0f28a075dc0b8376d383351f05cf7ccff +CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:255d3bd9793fe171d10bcaa0140ef58533c65997 TELEGRAM_ADAPTER_IMAGE=ghcr.io/portable-agent/telegram-adapter:bd9d19a0154340524438a16638ecf308873a5adf TEST_LAB_REF=c91a81cd97ac2e3b6f8ad78783b2289040c0dc7c From a7a83b238354ffb4c19247c2bc326dfdfefa4777 Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Mon, 5 Oct 2026 14:38:28 +0300 Subject: [PATCH 3/4] chore: refresh connection service image --- config/versions.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/versions.env b/config/versions.env index 5186828..8e40d68 100644 --- a/config/versions.env +++ b/config/versions.env @@ -19,6 +19,6 @@ ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:74152ac0a8832a8879126 CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:574b2995ab4b613cc844fbff37f276749afd5571 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:922390b293345f7483dcdee20d5020f417bd7f39 CALENDAR_MCP_IMAGE=ghcr.io/portable-agent/calendar-mcp:30b869b4f9f1c23a4be59e8cf5a3b3a3d3c89799 -CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:255d3bd9793fe171d10bcaa0140ef58533c65997 +CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:8863dd5c49982aa33964c2bfeba2d74bc73489a0 TELEGRAM_ADAPTER_IMAGE=ghcr.io/portable-agent/telegram-adapter:bd9d19a0154340524438a16638ecf308873a5adf TEST_LAB_REF=c91a81cd97ac2e3b6f8ad78783b2289040c0dc7c From a06cc51adbb3d0986346ba140253f8e27d8b4068 Mon Sep 17 00:00:00 2001 From: DanliaQwerty20 Date: Tue, 6 Oct 2026 11:46:56 +0300 Subject: [PATCH 4/4] chore: pin merged connection flow releases --- config/versions.env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/versions.env b/config/versions.env index 8e40d68..db77fec 100644 --- a/config/versions.env +++ b/config/versions.env @@ -16,9 +16,9 @@ CLOUDFLARED_IMAGE=cloudflare/cloudflared@sha256:072c067d25ccbe61d46e18f0d0723255 CHANNEL_GATEWAY_IMAGE=ghcr.io/portable-agent/channel-gateway:1ab140f73121e256b55e749468f010dd1fc57b3b AGENT_RUNTIME_IMAGE=ghcr.io/portable-agent/agent-runtime:f50ea7d5c722c93df1519e7ac1d12d210f439726 ACTION_SERVICE_IMAGE=ghcr.io/portable-agent/action-service:74152ac0a8832a8879126db2c500ae555c9b77f7 -CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:574b2995ab4b613cc844fbff37f276749afd5571 +CONVERSATION_SERVICE_IMAGE=ghcr.io/portable-agent/conversation-service:952204ae41f403763870ce4e2d532ab0affa7c13 MCP_GATEWAY_IMAGE=ghcr.io/portable-agent/mcp-gateway:922390b293345f7483dcdee20d5020f417bd7f39 CALENDAR_MCP_IMAGE=ghcr.io/portable-agent/calendar-mcp:30b869b4f9f1c23a4be59e8cf5a3b3a3d3c89799 -CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:8863dd5c49982aa33964c2bfeba2d74bc73489a0 +CONNECTION_SERVICE_IMAGE=ghcr.io/portable-agent/connection-service:582f144f0db21806595723bf0f931e53a73b2071 TELEGRAM_ADAPTER_IMAGE=ghcr.io/portable-agent/telegram-adapter:bd9d19a0154340524438a16638ecf308873a5adf -TEST_LAB_REF=c91a81cd97ac2e3b6f8ad78783b2289040c0dc7c +TEST_LAB_REF=39ef3dfbb6156e2f4b0df0a3b38cb9e327568f21