feat(uploads): choose the language of a recording; record it, show it, carry it to the engines (#61)
Form field (Swedish preselected, Other = auto) read before the file part and refused before anything is spooled if outside the closed set; stored on the row (migration 021, CHECK constraint, default sv for history); carried on the Job and the worker's context; shown on the status page. Feature scenarios and the coverage map are updated, and ADR-033/034 amended. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ func TestCreateUpload_StartsQueuedAndIsOwnedByTheCreator(t *testing.T) {
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
|
||||
id, err := s.CreateUpload(ctx, userA, "Teams export", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "Teams export", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, err := s.GetUpload(ctx, userA, id)
|
||||
@@ -50,7 +50,7 @@ func TestUploadLifecycle_CompletedKeepsTextSegmentsEndpointAndDuration(t *testin
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "m", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "m", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, id))
|
||||
@@ -84,7 +84,7 @@ func TestUploadLifecycle_FailedRecordsTypedKindAndNoContent(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "m", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "m", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, id))
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestGetUpload_AnotherUsersIDIsNotFound(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA, userB)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "A's meeting", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "A's meeting", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = s.GetUpload(ctx, userB, id)
|
||||
@@ -115,7 +115,7 @@ func TestUploadWrites_ByAnotherUserChangeNothing(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA, userB)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "A's meeting", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "A's meeting", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.ErrorIs(t, s.StartTranscribing(ctx, userB, id), store.ErrUploadNotFound)
|
||||
@@ -132,11 +132,11 @@ func TestListUploads_ReturnsOnlyTheCallersNewestFirstWithoutTranscriptText(t *te
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA, userB)
|
||||
ctx := context.Background()
|
||||
first, err := s.CreateUpload(ctx, userA, "first", false)
|
||||
first, err := s.CreateUpload(ctx, userA, "first", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
second, err := s.CreateUpload(ctx, userA, "second", false)
|
||||
second, err := s.CreateUpload(ctx, userA, "second", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
_, err = s.CreateUpload(ctx, userB, "B's", false)
|
||||
_, err = s.CreateUpload(ctx, userB, "B's", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, first))
|
||||
require.NoError(t, s.CompleteUpload(ctx, userA, first, domain.Transcription{Text: "long text body", Endpoint: "e"}))
|
||||
@@ -160,12 +160,12 @@ func TestFailInterruptedUploads_FailsOnlyUnfinishedRowsForEveryUser(t *testing.T
|
||||
_, err := super.Exec(ctx, `INSERT INTO user_identities (dex_subject, user_id) VALUES ($1, $2)`, subject, uid)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
queued, err := s.CreateUpload(ctx, userA, "queued", false)
|
||||
queued, err := s.CreateUpload(ctx, userA, "queued", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
running, err := s.CreateUpload(ctx, userB, "running", false)
|
||||
running, err := s.CreateUpload(ctx, userB, "running", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userB, running))
|
||||
done, err := s.CreateUpload(ctx, userA, "done", false)
|
||||
done, err := s.CreateUpload(ctx, userA, "done", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, done))
|
||||
require.NoError(t, s.CompleteUpload(ctx, userA, done, domain.Transcription{Text: "kept", Endpoint: "e"}))
|
||||
@@ -191,9 +191,9 @@ func TestCreateUpload_RecordsWhetherTheFileHadAVideoTrack(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
withVideo, err := s.CreateUpload(ctx, userA, "camera off", true)
|
||||
withVideo, err := s.CreateUpload(ctx, userA, "camera off", domain.LangSwedish, true)
|
||||
require.NoError(t, err)
|
||||
audioOnly, err := s.CreateUpload(ctx, userA, "voice only", false)
|
||||
audioOnly, err := s.CreateUpload(ctx, userA, "voice only", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotVideo, err := s.GetUpload(ctx, userA, withVideo)
|
||||
@@ -216,9 +216,9 @@ func TestUploadHadVideo_SurvivesCompletionAndFailure(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
done, err := s.CreateUpload(ctx, userA, "d", true)
|
||||
done, err := s.CreateUpload(ctx, userA, "d", domain.LangSwedish, true)
|
||||
require.NoError(t, err)
|
||||
failed, err := s.CreateUpload(ctx, userA, "f", true)
|
||||
failed, err := s.CreateUpload(ctx, userA, "f", domain.LangSwedish, true)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, done))
|
||||
require.NoError(t, s.CompleteUpload(ctx, userA, done, domain.Transcription{Text: "x", Endpoint: "e"}))
|
||||
@@ -235,7 +235,7 @@ func TestUploadLifecycle_ReconciliationRoundTripsWithItsContestedSpots(t *testin
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "m", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "m", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, id))
|
||||
want := &domain.Reconciliation{
|
||||
@@ -258,7 +258,7 @@ func TestUploadLifecycle_ASingleEngineTranscriptHasNoReconciliation(t *testing.T
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "m", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "m", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, id))
|
||||
require.NoError(t, s.CompleteUpload(ctx, userA, id, domain.Transcription{Text: "x", Endpoint: "e"}))
|
||||
@@ -273,7 +273,7 @@ func TestUploadLifecycle_ASkippedReconciliationKeepsItsReason(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
id, err := s.CreateUpload(ctx, userA, "m", false)
|
||||
id, err := s.CreateUpload(ctx, userA, "m", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.StartTranscribing(ctx, userA, id))
|
||||
rec := &domain.Reconciliation{Status: domain.ReconcileSkipped, Reason: "the model failed 3 times in a row: gateway down", Errors: 3}
|
||||
@@ -284,3 +284,55 @@ func TestUploadLifecycle_ASkippedReconciliationKeepsItsReason(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, rec, got.Reconciliation)
|
||||
}
|
||||
|
||||
func TestCreateUpload_RecordsTheLanguageTheUserChoseAndTheListCarriesIt(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
ctx := context.Background()
|
||||
swedish, err := s.CreateUpload(ctx, userA, "sv", domain.LangSwedish, false)
|
||||
require.NoError(t, err)
|
||||
auto, err := s.CreateUpload(ctx, userA, "auto", domain.LangAuto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
gotSv, err := s.GetUpload(ctx, userA, swedish)
|
||||
require.NoError(t, err)
|
||||
gotAuto, err := s.GetUpload(ctx, userA, auto)
|
||||
require.NoError(t, err)
|
||||
listed, err := s.ListUploads(ctx, userA)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, domain.LangSwedish, gotSv.Language)
|
||||
assert.Equal(t, domain.LangAuto, gotAuto.Language)
|
||||
byID := map[string]domain.Language{}
|
||||
for _, u := range listed {
|
||||
byID[u.ID] = u.Language
|
||||
}
|
||||
assert.Equal(t, map[string]domain.Language{swedish: domain.LangSwedish, auto: domain.LangAuto}, byID)
|
||||
}
|
||||
|
||||
// The closed set is also held by the table, so a bug or a second writer cannot store a
|
||||
// value the engines are never meant to receive (tapir#61).
|
||||
func TestUploadedTranscripts_TheTableRefusesALanguageOutsideTheClosedSet(t *testing.T) {
|
||||
newStore(t)
|
||||
seedUsers(t, userA)
|
||||
|
||||
_, err := rawPool(t).Exec(context.Background(),
|
||||
`INSERT INTO uploaded_transcripts (user_id, title, language) VALUES ($1, 'x', 'en')`, userA)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "language", "the CHECK constraint names the column")
|
||||
}
|
||||
|
||||
// A row from before the column existed was transcribed as Swedish; the default says so.
|
||||
func TestUploadedTranscripts_ARowInsertedWithoutALanguageIsSwedish(t *testing.T) {
|
||||
s := newStore(t)
|
||||
seedUsers(t, userA)
|
||||
var id string
|
||||
require.NoError(t, rawPool(t).QueryRow(context.Background(),
|
||||
`INSERT INTO uploaded_transcripts (user_id, title) VALUES ($1, 'old') RETURNING id`, userA).Scan(&id))
|
||||
|
||||
got, err := s.GetUpload(context.Background(), userA, id)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, domain.LangSwedish, got.Language)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user