Files
transcriber/internal/service/metrics_test.go
T
av 1576d06735 внутренняя модель перестроена вокруг аудиозаписи
- audiorecords вместо transcribe_jobs: приложения (texts, structures,
  recognitions, record_events, topics) живут своими коллекциями, ссылки на
  исходник и на приведённую копию перестали переставляться
- рубеж называет достигнутое, отказ стал признаком остановки с причиной, а
  сторожей стало двое: число отказов и время в рубеже
- воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг
  выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
2026-08-14 20:20:33 +03:00

84 lines
3.3 KiB
Go

package service
import (
"context"
"os"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"git.vakhrushev.me/av/transcriber/internal/entity"
)
// Счёт работы конвейера живёт в шаге, а не в воркере: только шаг знает рубеж, с
// которого взята запись, а воркеры пула одинаковы и именем ничего не говорят.
// stageCount читает счётчик работы из общего реестра процесса. Судит реестр, а
// не переменную пакета: метка, потерянная в точке употребления, переменную не
// ломает, а на странице метрик видна.
func stageCount(t *testing.T, stage, errLabel string) float64 {
t.Helper()
families, err := prometheus.DefaultGatherer.Gather()
require.NoError(t, err)
for _, mf := range families {
if mf.GetName() != "transcriber_worker_job_count" {
continue
}
for _, m := range mf.GetMetric() {
var gotStage, gotErr string
for _, label := range m.GetLabel() {
switch label.GetName() {
case "stage":
gotStage = label.GetValue()
case "error":
gotErr = label.GetValue()
}
}
if gotStage == stage && gotErr == errLabel {
return m.GetCounter().GetValue()
}
}
}
return 0
}
// Критерий приёмки 11. Отказ виден с разрезом по шагу: счётчик отказов —
// единственный сигнал, по которому владелец сервиса замечает поломку, и без
// метки рубежа «падает приведение» и «падает распознавание» стали бы
// неразличимы.
func TestFailureIsCountedWithStageLabel(t *testing.T) {
env := newPipelineEnv(t, &okMetaViewer{}, &okConverter{})
beforeOk := stageCount(t, entity.StateUploaded, "false")
record := newTelegramRecord(t, env)
require.NoError(t, env.service.RunStep(t.Context()))
require.Equal(t, entity.StateNormalized, readRecord(t, env, record.Id).State)
assert.InDelta(t, beforeOk+1, stageCount(t, entity.StateUploaded, "false"), 0,
"успешный шаг засчитан с меткой своего рубежа")
// Теперь тот же рубеж, но с отказом.
failing := newPipelineEnv(t, &okMetaViewer{}, &failingStepConverter{})
beforeErr := stageCount(t, entity.StateUploaded, "true")
newTelegramRecord(t, failing)
require.Error(t, failing.service.RunStep(t.Context()))
assert.InDelta(t, beforeErr+1, stageCount(t, entity.StateUploaded, "true"), 0,
"отказ засчитан с меткой того же рубежа")
}
// failingStepConverter отказывает **отказом шага**, а не приговором записи:
// приговор счётчик отказов не растит, потому что шаг при нём отрабатывает.
type failingStepConverter struct{}
func (c *failingStepConverter) Convert(_ context.Context, _, dest string) error {
// Файл результата не создаётся вовсе — шаг отказывает на измерении копии.
return os.Remove(dest)
}