хранилище переехало с PocketBase на SQLite со своим каталогом файлов
- база своя: два пула, захват одним UPDATE ... RETURNING, шаги схемы на goose под файловым замком, одна миграция начальной схемы вместо семи прежних - транспорт переписан на net/http: свои слои, свой ограничитель частоты, отдача файла с проверкой владельца; панель /_/ и пространство /api/ исчезли - по находкам ревью: журнал не пишет путь под корнем приложения, ключ бюджета читается справа налево, узнавание известного идёт читающим пулом
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
@@ -15,20 +17,19 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"git.vakhrushev.me/av/transcriber/internal/adapter/recognizer"
|
||||
pbrepo "git.vakhrushev.me/av/transcriber/internal/adapter/repo/pocketbase"
|
||||
"git.vakhrushev.me/av/transcriber/internal/adapter/repo/pocketbase/migrations"
|
||||
sqliterepo "git.vakhrushev.me/av/transcriber/internal/adapter/repo/sqlite"
|
||||
"git.vakhrushev.me/av/transcriber/internal/clock"
|
||||
"git.vakhrushev.me/av/transcriber/internal/contract"
|
||||
"git.vakhrushev.me/av/transcriber/internal/entity"
|
||||
"git.vakhrushev.me/av/transcriber/internal/ident"
|
||||
"git.vakhrushev.me/av/transcriber/internal/service"
|
||||
)
|
||||
|
||||
@@ -66,19 +67,33 @@ func readableMetaViewer() *stubMetaViewer {
|
||||
return &stubMetaViewer{seconds: 42}
|
||||
}
|
||||
|
||||
const testMarkup = `<!doctype html><html lang="ru"><body>приложение</body></html>`
|
||||
|
||||
// builtDist — собранное приложение, каким его видит раздача.
|
||||
func builtDist() fs.FS {
|
||||
return fstest.MapFS{
|
||||
"index.html": {Data: []byte(testMarkup)},
|
||||
"assets/index-abc123.js": {Data: []byte("console.log(1)")},
|
||||
"favicon.ico": {Data: []byte("значок")},
|
||||
}
|
||||
}
|
||||
|
||||
// testEnv — собранное окружение одной проверки. Каталог данных свой у каждой:
|
||||
// рабочий каталог процесса проверки не трогают.
|
||||
type testEnv struct {
|
||||
mux http.Handler
|
||||
handler *AppHandler
|
||||
app core.App
|
||||
db *sqliterepo.DB
|
||||
store *sqliterepo.Store
|
||||
users *sqliterepo.UserRepository
|
||||
files *sqliterepo.FileRepository
|
||||
journal *journalBuffer
|
||||
// login — логин у провайдера, которым доверенный источник называет
|
||||
// пришедшего. Адреса приложения закрыты узнаванием, и проверка, судящая их
|
||||
// по существу, обязана назваться ровно так же, как это делает прокси.
|
||||
login string
|
||||
// account — учётная запись, которой принадлежит этот логин.
|
||||
account *core.Record
|
||||
account *contract.UserAccount
|
||||
}
|
||||
|
||||
// Доверенный источник проверок.
|
||||
@@ -93,10 +108,68 @@ const (
|
||||
func testTrustedNetworks(t *testing.T) []netip.Prefix {
|
||||
t.Helper()
|
||||
|
||||
prefix, err := netip.ParsePrefix("10.0.0.0/8")
|
||||
// Подсетей две: своя для запросов через `httptest.NewRecorder` и петлевая
|
||||
// для тех проверок, которым нужен **настоящий** HTTP-запрос через поднятую
|
||||
// цепочку слоёв — у них адрес пира ставит ядро, и он петлевой.
|
||||
networks := make([]netip.Prefix, 0, 2)
|
||||
for _, raw := range []string{"10.0.0.0/8", "127.0.0.0/8"} {
|
||||
prefix, err := netip.ParsePrefix(raw)
|
||||
require.NoError(t, err)
|
||||
networks = append(networks, prefix)
|
||||
}
|
||||
|
||||
return networks
|
||||
}
|
||||
|
||||
// liveServer поднимает настоящий HTTP-сервер на этой поверхности.
|
||||
//
|
||||
// Нужен там, где проверка судит поведение, которого у вызова обработчика
|
||||
// напрямую не бывает: разбор заголовка диапазона идёт по настоящему запросу, а
|
||||
// адрес пира ставит ядро.
|
||||
func (e *testEnv) liveServer(t *testing.T) *httptest.Server {
|
||||
t.Helper()
|
||||
|
||||
server := httptest.NewServer(e.mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// liveResponse — готовый ответ настоящего запроса: тело прочитано и закрыто, и
|
||||
// проверке остаётся судить значения.
|
||||
type liveResponse struct {
|
||||
StatusCode int
|
||||
Header http.Header
|
||||
ContentLength int64
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// liveRequest шлёт настоящий запрос от имени учётной записи проверки.
|
||||
func (e *testEnv) liveRequest(
|
||||
t *testing.T, server *httptest.Server, path string, headers map[string]string,
|
||||
) liveResponse {
|
||||
t.Helper()
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, server.URL+path, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set(LoginHeader, e.login)
|
||||
for name, value := range headers {
|
||||
req.Header.Set(name, value)
|
||||
}
|
||||
|
||||
res, err := server.Client().Do(req)
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, res.Body.Close()) }()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
return []netip.Prefix{prefix}
|
||||
return liveResponse{
|
||||
StatusCode: res.StatusCode,
|
||||
Header: res.Header,
|
||||
ContentLength: res.ContentLength,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
// asUser делает запрос запросом названного человека: ставит заголовок и адрес
|
||||
@@ -113,30 +186,23 @@ func (e *testEnv) serve(w http.ResponseWriter, req *http.Request) {
|
||||
e.mux.ServeHTTP(w, asUser(req, e.login))
|
||||
}
|
||||
|
||||
// newTestAccount заводит учётную запись с известным логином.
|
||||
//
|
||||
// Запись создаётся прямым сохранением, а не запросом к API: заводить её
|
||||
// запросом нельзя — создание закрыто шагом схемы. Прямое сохранение идёт мимо
|
||||
// правил доступа так же, как идёт заведение записи первым обращением.
|
||||
func newTestAccount(t *testing.T, app core.App) (*core.Record, string) {
|
||||
t.Helper()
|
||||
// get шлёт запрос **неузнанным**: заголовка на нём нет вовсе.
|
||||
func (e *testEnv) get(path string) *httptest.ResponseRecorder {
|
||||
w := httptest.NewRecorder()
|
||||
e.mux.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
|
||||
return w
|
||||
}
|
||||
|
||||
const login = "person"
|
||||
// getAs шлёт запрос от имени названного логина.
|
||||
func (e *testEnv) getAs(login, path string) *httptest.ResponseRecorder {
|
||||
w := httptest.NewRecorder()
|
||||
e.mux.ServeHTTP(w, asUser(httptest.NewRequest(http.MethodGet, path, nil), login))
|
||||
return w
|
||||
}
|
||||
|
||||
users, err := app.FindCollectionByNameOrId("users")
|
||||
require.NoError(t, err)
|
||||
|
||||
record := core.NewRecord(users)
|
||||
record.Set(migrations.ProviderLoginField, login)
|
||||
record.Set("email", "person@example.com")
|
||||
record.Set("verified", true)
|
||||
// Случайный пароль ставит и сам сервис, когда заводит запись первым
|
||||
// обращением: запись auth-коллекции без пароля не сохраняется, а войти по
|
||||
// нему всё равно нельзя — парольный вход выключен шагом схемы.
|
||||
record.SetRandomPassword()
|
||||
require.NoError(t, app.Save(record))
|
||||
|
||||
return record, login
|
||||
// getOwn шлёт запрос от имени учётной записи проверки.
|
||||
func (e *testEnv) getOwn(path string) *httptest.ResponseRecorder {
|
||||
return e.getAs(e.login, path)
|
||||
}
|
||||
|
||||
// journalBuffer — перехваченный журнал одной проверки. Свой на случай: общий на
|
||||
@@ -162,45 +228,72 @@ func (b *journalBuffer) String() string {
|
||||
return b.text.String()
|
||||
}
|
||||
|
||||
// newTestStorage поднимает хранилище на пустом каталоге и накатывает схему —
|
||||
// ровно тем же путём, каким это делает сервис при старте.
|
||||
func newTestStorage(t *testing.T) core.App {
|
||||
// newTestStorage поднимает базу на пустом каталоге и накатывает схему — ровно
|
||||
// тем же путём, каким это делает сервис при старте.
|
||||
func newTestStorage(t *testing.T) (*sqliterepo.DB, *sqliterepo.Store, string) {
|
||||
t.Helper()
|
||||
|
||||
app, err := pbrepo.New(t.TempDir())
|
||||
dir := t.TempDir()
|
||||
db, err := sqliterepo.Open(dir, sqliterepo.Settings{BusyTimeoutMs: 5000, ReadConnections: 4})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
if err := app.ResetBootstrapState(); err != nil {
|
||||
t.Logf("не удалось закрыть хранилище: %v", err)
|
||||
if err := db.Close(); err != nil {
|
||||
t.Logf("не удалось закрыть базу: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
return app
|
||||
require.NoError(t, sqliterepo.Migrate(
|
||||
context.Background(), db, dir, slog.New(slog.DiscardHandler),
|
||||
))
|
||||
|
||||
return db, sqliterepo.NewStore(dir), dir
|
||||
}
|
||||
|
||||
// envOptions — чем одна проверка отличается от другой.
|
||||
type envOptions struct {
|
||||
metaviewer contract.AudioMetaViewer
|
||||
dist fs.FS
|
||||
built bool
|
||||
}
|
||||
|
||||
func setupTestEnv(t *testing.T, metaviewer contract.AudioMetaViewer) *testEnv {
|
||||
app := newTestStorage(t)
|
||||
return setupEnv(t, envOptions{metaviewer: metaviewer, dist: builtDist(), built: true})
|
||||
}
|
||||
|
||||
pbrepo.BindPanelRules(app)
|
||||
// setupEnv собирает поверхность сервиса тем же способом, что и точка входа:
|
||||
// адреса порождает перечень адресного пространства, слои одевает та же цепочка.
|
||||
// Собери проверка поверхность иначе — и судила бы она не то.
|
||||
func setupEnv(t *testing.T, opts envOptions) *testEnv {
|
||||
t.Helper()
|
||||
|
||||
db, store, _ := newTestStorage(t)
|
||||
|
||||
recordRepo := sqliterepo.NewAudioRecordRepository(db)
|
||||
textRepo := sqliterepo.NewTextRepository(db)
|
||||
structureRepo := sqliterepo.NewStructureRepository(db)
|
||||
fileRepo := sqliterepo.NewFileRepository(db, store)
|
||||
users := sqliterepo.NewUserRepository(db)
|
||||
|
||||
recordRepo := pbrepo.NewAudioRecordRepository(app)
|
||||
textRepo := pbrepo.NewTextRepository(app)
|
||||
repos := service.Repositories{
|
||||
Records: recordRepo,
|
||||
Files: pbrepo.NewFileRepository(app),
|
||||
Files: fileRepo,
|
||||
Texts: textRepo,
|
||||
Structures: pbrepo.NewStructureRepository(app),
|
||||
Recognitions: pbrepo.NewRecognitionRepository(app),
|
||||
Events: pbrepo.NewRecordEventRepository(app),
|
||||
Structures: structureRepo,
|
||||
Recognitions: sqliterepo.NewRecognitionRepository(db, store),
|
||||
Events: sqliterepo.NewRecordEventRepository(db),
|
||||
}
|
||||
|
||||
// Журнал уходит в буфер, а не в никуда: по нему судит проверка запрета на
|
||||
// имя отправителя. Вывод прогона от этого не меняется — ERROR-строки ветки
|
||||
// отказа по-прежнему не попадают на экран и не размывают признак, по
|
||||
// которому отличают новый красный шаг гейта от объявленного долга.
|
||||
// отказа по-прежнему не попадают на экран.
|
||||
journal := &journalBuffer{}
|
||||
logger := slog.New(slog.NewTextHandler(journal, nil))
|
||||
|
||||
metaviewer := opts.metaviewer
|
||||
if metaviewer == nil {
|
||||
metaviewer = readableMetaViewer()
|
||||
}
|
||||
|
||||
trsService := service.NewTranscribeService(
|
||||
repos,
|
||||
metaviewer,
|
||||
@@ -210,29 +303,31 @@ func setupTestEnv(t *testing.T, metaviewer contract.AudioMetaViewer) *testEnv {
|
||||
logger,
|
||||
)
|
||||
|
||||
handler := NewAppHandler(recordRepo, textRepo, pbrepo.NewStructureRepository(app), trsService, logger)
|
||||
handler := NewAppHandler(recordRepo, textRepo, structureRepo, fileRepo, trsService, logger)
|
||||
|
||||
// Роутер собирается тем же способом, что и боевой: слой узнавания вешается
|
||||
// корневым, маршруты вешает сам обработчик, и проверка судит ту же цепочку,
|
||||
// что и прод. Собери роутер иначе — и проверка судила бы не то.
|
||||
r, err := apis.NewRouter(app)
|
||||
mounts := ServiceMounts(
|
||||
AppChain(handler.Routes(), users, testTrustedNetworks(t), logger),
|
||||
http.NotFoundHandler(),
|
||||
)
|
||||
webapp := NewWebappHandler(opts.dist, opts.built, logger)
|
||||
mux := BuildHandler(mounts, webapp, logger)
|
||||
|
||||
account, _, err := users.EnsureUser(contract.Identity{
|
||||
Login: "person",
|
||||
Name: "Человек",
|
||||
Email: "person@example.com",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
mounts := ServiceMounts(handler, http.NotFoundHandler())
|
||||
r.Bind(TrustedHeaderIdentity(app, mounts, testTrustedNetworks(t), logger))
|
||||
handler.Register(r)
|
||||
|
||||
mux, err := r.BuildMux()
|
||||
require.NoError(t, err)
|
||||
|
||||
account, login := newTestAccount(t, app)
|
||||
|
||||
return &testEnv{
|
||||
mux: mux,
|
||||
handler: handler,
|
||||
app: app,
|
||||
db: db,
|
||||
store: store,
|
||||
users: users,
|
||||
files: fileRepo,
|
||||
journal: journal,
|
||||
login: login,
|
||||
login: "person",
|
||||
account: account,
|
||||
}
|
||||
}
|
||||
@@ -285,65 +380,115 @@ func intakeItemOf(t *testing.T, w *httptest.ResponseRecorder) IntakeItem {
|
||||
return items[0]
|
||||
}
|
||||
|
||||
// storedFileNames отдаёт имена, под которыми файлы легли в хранилище.
|
||||
// storedFileNames отдаёт имена, под которыми копии легли в каталог данных.
|
||||
func storedFileNames(t *testing.T, env *testEnv) []string {
|
||||
records, err := env.app.FindAllRecords(migrations.FilesCollection)
|
||||
t.Helper()
|
||||
|
||||
rows, err := env.db.Reader().QueryContext(context.Background(), "SELECT file_name FROM files")
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, rows.Close()) }()
|
||||
|
||||
var names []string
|
||||
for _, record := range records {
|
||||
names = append(names, record.GetStringSlice("file")...)
|
||||
for rows.Next() {
|
||||
var name string
|
||||
require.NoError(t, rows.Scan(&name))
|
||||
names = append(names, name)
|
||||
}
|
||||
require.NoError(t, rows.Err())
|
||||
return names
|
||||
}
|
||||
|
||||
// countFiles считает записи о файлах.
|
||||
// countRows считает строки названной таблицы.
|
||||
func countRows(t *testing.T, env *testEnv, table string) int {
|
||||
t.Helper()
|
||||
|
||||
var total int
|
||||
require.NoError(t, env.db.Reader().QueryRowContext(context.Background(), "SELECT COUNT(*) FROM "+table).Scan(&total))
|
||||
return total
|
||||
}
|
||||
|
||||
// countFiles считает строки о копиях записей.
|
||||
func countFiles(t *testing.T, env *testEnv) int {
|
||||
records, err := env.app.FindAllRecords(migrations.FilesCollection)
|
||||
require.NoError(t, err)
|
||||
return len(records)
|
||||
return countRows(t, env, "files")
|
||||
}
|
||||
|
||||
// countJobs считает заведённые аудиозаписи.
|
||||
func countJobs(t *testing.T, env *testEnv) int {
|
||||
records, err := env.app.FindAllRecords(migrations.RecordsCollection)
|
||||
require.NoError(t, err)
|
||||
return len(records)
|
||||
return countRows(t, env, "audio_records")
|
||||
}
|
||||
|
||||
// jobWithFile заводит задачу вместе с её записью: ссылка на файл обязательна
|
||||
// схемой, потому что без неё задача не пройдёт ни одного шага.
|
||||
// jobWithFile заводит запись вместе с её принятой копией: без ссылки на файл
|
||||
// запись не прошла бы ни одного шага.
|
||||
func jobWithFile(t *testing.T, env *testEnv) *entity.AudioRecord {
|
||||
t.Helper()
|
||||
|
||||
repo := pbrepo.NewFileRepository(env.app)
|
||||
work, err := repo.Stage(".mp3", strings.NewReader("запись"))
|
||||
return jobWithFileOf(t, env, env.account.ID)
|
||||
}
|
||||
|
||||
// jobWithFileOf — то же, но от имени названного владельца.
|
||||
func jobWithFileOf(t *testing.T, env *testEnv, ownerID string) *entity.AudioRecord {
|
||||
t.Helper()
|
||||
|
||||
recordID := ident.New()
|
||||
|
||||
work, err := env.files.Stage(".mp3", strings.NewReader("запись"))
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, work.Close()) }()
|
||||
|
||||
// Владелец — учётная запись проверки: задача, пришедшая из веба, без
|
||||
// владельца больше не заводится, и фикстура без него описывала бы состояние,
|
||||
// которого в проде не бывает.
|
||||
file, err := repo.Create("sample.mp3", work, contract.FileMeta{Format: "mp3"}, env.account.Id)
|
||||
// Владелец — учётная запись проверки: ничьей записи в хранилище не бывает, и
|
||||
// фикстура без владельца описывала бы состояние, которого в проде нет.
|
||||
file, err := env.files.Create(
|
||||
recordID, "sample.mp3", work, contract.FileMeta{Format: "mp3"}, ownerID,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
record := &entity.AudioRecord{
|
||||
Id: recordID,
|
||||
State: entity.StateUploaded,
|
||||
StateEnteredAt: clock.Now(),
|
||||
Source: entity.SourceApi,
|
||||
OwnerID: env.account.Id,
|
||||
OwnerID: ownerID,
|
||||
OriginalFileID: &file.Id,
|
||||
}
|
||||
require.NoError(t, env.handler.recordRepo.Create(record))
|
||||
return record
|
||||
}
|
||||
|
||||
// storedContent читает содержимое файла из хранилища.
|
||||
func storedContent(t *testing.T, env *testEnv, fileID string) []byte {
|
||||
repo := pbrepo.NewFileRepository(env.app)
|
||||
reader, err := repo.Open(fileID)
|
||||
// newTopic заводит тему в словаре названного владельца.
|
||||
//
|
||||
// Запросом к базе, а не адресом сервиса: адреса, которым заводят тему, у сервиса
|
||||
// нет — словарь пишет задача языковой модели, которой ещё нет. Проверка обязана
|
||||
// пройти тот же путь разрешения тем, каким пойдёт она.
|
||||
func newTopic(t *testing.T, env *testEnv, ownerID, name string) string {
|
||||
t.Helper()
|
||||
|
||||
id := ident.New()
|
||||
now := clock.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
_, err := env.db.Writer().ExecContext(context.Background(),
|
||||
"INSERT INTO topics (id, owner_id, name, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
||||
id, ownerID, name, now, now,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
defer reader.Close()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
// attachTopic связывает тему с записью.
|
||||
func attachTopic(t *testing.T, env *testEnv, recordID, topicID string) {
|
||||
t.Helper()
|
||||
|
||||
_, err := env.db.Writer().ExecContext(context.Background(),
|
||||
"INSERT INTO record_topics (record_id, topic_id) VALUES (?, ?)", recordID, topicID,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// storedContent читает содержимое копии из каталога данных.
|
||||
func storedContent(t *testing.T, env *testEnv, fileID string) []byte {
|
||||
t.Helper()
|
||||
|
||||
reader, err := env.files.Open(fileID)
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, reader.Close()) }()
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = buf.ReadFrom(reader)
|
||||
@@ -387,7 +532,7 @@ func TestCreateTranscribeJob_Success(t *testing.T) {
|
||||
// отправитель получит идентификатор записи, которой не будет никогда.
|
||||
require.Equal(t, 1, countJobs(t, env))
|
||||
|
||||
job, err := env.handler.recordRepo.GetByID(response.ID, env.account.Id)
|
||||
job, err := env.handler.recordRepo.GetByID(response.ID, env.account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, entity.StateUploaded, job.State)
|
||||
require.NotNil(t, job.OriginalFileID)
|
||||
@@ -529,9 +674,20 @@ func TestCreateTranscribeJob_SenderFileNameNotStored(t *testing.T) {
|
||||
require.Len(t, names, 1)
|
||||
|
||||
assert.NotContains(t, names[0], "секретное-слово",
|
||||
"имя, данное отправителем, в хранилище не попадает")
|
||||
"имя, данное отправителем, в имя файла не попадает")
|
||||
assert.True(t, strings.HasSuffix(names[0], ".mp3"),
|
||||
"расширение при этом сохраняется: %q", names[0])
|
||||
|
||||
// И в **путь** к файлу оно не попадает тоже: подкаталог назван
|
||||
// идентификатором записи, а не именем отправителя.
|
||||
var recordID, storedRecord string
|
||||
require.NoError(t, env.db.Reader().
|
||||
QueryRow("SELECT id FROM audio_records").Scan(&recordID))
|
||||
require.NoError(t, env.db.Reader().
|
||||
QueryRow("SELECT record_id FROM files").Scan(&storedRecord))
|
||||
assert.Equal(t, recordID, storedRecord, "подкаталог копии назван не записью")
|
||||
assert.NotContains(t, storedRecord, "секретное-слово",
|
||||
"имя, данное отправителем, попало в путь к файлу")
|
||||
}
|
||||
|
||||
func TestCreateTranscribeJob_MetaViewerFailure(t *testing.T) {
|
||||
@@ -659,7 +815,7 @@ func TestCreateTranscribeJob_JournalTracesRecord(t *testing.T) {
|
||||
|
||||
response := intakeItemOf(t, w)
|
||||
|
||||
job, err := env.handler.recordRepo.GetByID(response.ID, env.account.Id)
|
||||
job, err := env.handler.recordRepo.GetByID(response.ID, env.account.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, job.OriginalFileID)
|
||||
|
||||
@@ -821,7 +977,5 @@ func TestAcceptedRecordSurvivesSenderDisconnect(t *testing.T) {
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Result().StatusCode, "тело ответа: %s", w.Body.String())
|
||||
|
||||
jobs, err := env.app.FindAllRecords(migrations.RecordsCollection)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, jobs, 1, "задача заведена, несмотря на ушедшего отправителя")
|
||||
assert.Equal(t, 1, countJobs(t, env), "задача заведена, несмотря на ушедшего отправителя")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user