test: починить тесты на Windows
- E2E (app): e2eFakeAPI переведён на v2 HTTP API opencode (/api/*) с определением агента по тексту промпта; Router получает processed-счётчик и WaitProcessed, e2eChannel.deliver ждёт асинхронную обработку — убирает гонку «запрос сразу после deliver» и коллатеральный 'database is closed'. - app_test: одинарные YAML-кавычки для путей Windows (backslash-escape) + закрытие Store в TestNew/TestNew_RunCtxCancel/TestNew_UpdateWiring. - config_test: абсолютный путь строится с корнем тома (C:\...) и одинарными кавычками YAML. - opencode/server_test: fakeServeBin на Windows — .cmd с ping (#!/bin/sh не исполняется). - worker_test: TestWorkerSemaphore поллит до целевого статуса вместо фиксированных sleep (git на Windows медленнее).
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -809,13 +808,41 @@ func TestWorkerStartStop(t *testing.T) {
|
||||
w.Stop()
|
||||
}
|
||||
|
||||
// waitTaskStatus ждёт, пока задача достигнет статуса want. На Windows git-операции
|
||||
// воркера заметно медленнее, чем на Linux, поэтому проверки в тестах не могут
|
||||
// полагаться на фиксированные sleep'ы — только на polling до целевого статуса.
|
||||
func waitTaskStatus(t *testing.T, ctx context.Context, s *storage.Storage, id int64, want storage.Status) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(30 * time.Second)
|
||||
for {
|
||||
task, err := s.GetTask(ctx, id)
|
||||
if err != nil {
|
||||
t.Fatalf("get task %d: %v", id, err)
|
||||
}
|
||||
if task.Status == want {
|
||||
return
|
||||
}
|
||||
switch task.Status {
|
||||
case storage.StatusFailed, storage.StatusTimeout:
|
||||
t.Fatalf("task %d: status %q, want %q", id, task.Status, want)
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("task %d: таймаут ожидания %q, последний статус %q", id, want, task.Status)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatalf("task %d: ctx done: %v", id, ctx.Err())
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerSemaphore(t *testing.T) {
|
||||
s := setupWorkerDB(t)
|
||||
|
||||
// создаём 2 ready-задачи
|
||||
for i := 0; i < 2; i++ {
|
||||
createReadyTask(t, s, fmt.Sprintf("task-%d", i))
|
||||
}
|
||||
task1 := createReadyTask(t, s, "task-0")
|
||||
task2 := createReadyTask(t, s, "task-1")
|
||||
|
||||
w := &Worker{
|
||||
Store: s,
|
||||
@@ -829,12 +856,12 @@ func TestWorkerSemaphore(t *testing.T) {
|
||||
w.sem = make(chan struct{}, 1)
|
||||
w.sem <- struct{}{}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// первый poll — запустит 1 задачу (макс. 1)
|
||||
w.pollAndDispatch(ctx)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
waitTaskStatus(t, ctx, s, task1.ID, storage.StatusSuccess)
|
||||
|
||||
// 1 должна быть success, 1 — всё ещё approved
|
||||
success, _ := s.ListTasks(ctx, storage.TaskFilter{Status: storage.StatusSuccess})
|
||||
@@ -848,7 +875,7 @@ func TestWorkerSemaphore(t *testing.T) {
|
||||
|
||||
// первая завершилась и вернула токен в сем — можем диспатчить вторую
|
||||
w.pollAndDispatch(ctx)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
waitTaskStatus(t, ctx, s, task2.ID, storage.StatusSuccess)
|
||||
|
||||
success, _ = s.ListTasks(ctx, storage.TaskFilter{Status: storage.StatusSuccess})
|
||||
if len(success) != 2 {
|
||||
|
||||
Reference in New Issue
Block a user