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) }