доменные ошибки сравниваются через errors.As, отказ Close не теряется

- признаки «работы нет» и «задача не найдена» узнаются по смыслу, а не
  приведением типа: обёртка `%w` на пути больше не превращает пустой прогон
  воркера в отказ раз в секунду
- отказ закрытия соединения с распознавателем доходит до вызывающего
  (`errors.Join`) либо до журнала; у `errcheck` включён `check-blank`, иначе
  критерий принимал реализацию, выбрасывающую отказ в пустоту
- заведены первые тесты пакета worker и capability `pipeline`; долг из четырёх
  замечаний линтера закрыт, гейт зелёный целиком
This commit is contained in:
av
2026-08-11 18:04:25 +03:00
parent b7dd060ba0
commit 2559d09fc8
24 changed files with 1554 additions and 48 deletions
@@ -2,6 +2,7 @@ package yandex
import (
"context"
"errors"
"fmt"
"strings"
@@ -52,8 +53,16 @@ func newSpeechKitService(cfg speechKitConfig) (*speechKitService, error) {
// Создаем защищенное соединение для Operations API
opConn, err := grpc.NewClient(OperationEndpoint, grpc.WithTransportCredentials(creds))
if err != nil {
sttConn.Close()
return nil, fmt.Errorf("failed to connect to Operations API: %w", err)
// Отказы независимы, и второй не теряется. На сегодняшнем клиенте он
// почти наверняка не наступит: grpc.NewClient ленив, соединение к этому
// моменту не открыто, и Close вернёт отказ только при повторном
// закрытии — то есть сообщит о нашей ошибке, а не о Yandex. Сборка
// оставлена как защита от смены реализации клиента; nil от закрытия
// errors.Join отбрасывает, и форма ошибки в обычном случае не меняется.
return nil, errors.Join(
fmt.Errorf("failed to connect to Operations API: %w", err),
sttConn.Close(),
)
}
sttClient := stt.NewAsyncRecognizerClient(sttConn)
@@ -77,10 +86,10 @@ func (s *speechKitService) Close() error {
if s.opConn != nil {
err2 = s.opConn.Close()
}
if err1 != nil {
return err1
}
return err2
// Отказы двух соединений независимы, и вернуть только первый — значит
// потерять половину причины: журнал пишется при остановке процесса, и
// восстановить утраченное будет уже негде.
return errors.Join(err1, err2)
}
// recognizeFileFromS3 запускает асинхронное распознавание файла из S3