test(web): per-recording delete is owner-scoped, refused while active, offered only when finished (#37), red
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
|||||||
|
package web_test
|
||||||
|
|
||||||
|
// Deleting one recording (#37). Owner-scoped like every other upload route: another user's
|
||||||
|
// id is a 404 and the row survives; an upload still being processed is refused.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func deleteReq(id string) *http.Request {
|
||||||
|
return httptest.NewRequest(http.MethodPost, "/uploads/"+id+"/delete", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadExists(t *testing.T, id string) bool {
|
||||||
|
t.Helper()
|
||||||
|
var n int
|
||||||
|
require.NoError(t, rawPool(t).QueryRow(context.Background(), `SELECT count(*) FROM uploaded_transcripts WHERE id = $1`, id).Scan(&n))
|
||||||
|
return n == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadDelete_AnOwnerRemovesAFinishedOrFailedRecordingAndLandsOnTheList(t *testing.T) {
|
||||||
|
for _, status := range []string{"done", "failed"} {
|
||||||
|
t.Run(status, func(t *testing.T) {
|
||||||
|
app, _ := newUploadApp(t, audioOK, 200<<20)
|
||||||
|
id := seedUpload(t, userID, "Roadmap sync", status, "transcription_failed")
|
||||||
|
|
||||||
|
rec := do(t, app, deleteReq(id))
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusSeeOther, rec.Code)
|
||||||
|
assert.Equal(t, "/uploads", rec.Header().Get("Location"))
|
||||||
|
assert.False(t, uploadExists(t, id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadDelete_AnotherUsersRecordingIs404AndSurvives(t *testing.T) {
|
||||||
|
app, _ := newUploadApp(t, audioOK, 200<<20)
|
||||||
|
theirs := seedUpload(t, otherUserID, "Their private meeting", "done", meetingText)
|
||||||
|
|
||||||
|
rec := do(t, app, deleteReq(theirs))
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, rec.Code)
|
||||||
|
assert.True(t, uploadExists(t, theirs), "a non-owner's delete must not remove the row")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadDelete_ARecordingStillBeingTranscribedIsRefusedWith409AndKept(t *testing.T) {
|
||||||
|
for _, status := range []string{"queued", "transcribing"} {
|
||||||
|
t.Run(status, func(t *testing.T) {
|
||||||
|
app, _ := newUploadApp(t, audioOK, 200<<20)
|
||||||
|
id := seedUpload(t, userID, "m", status, "")
|
||||||
|
|
||||||
|
rec := do(t, app, deleteReq(id))
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusConflict, rec.Code)
|
||||||
|
assert.Contains(t, body(t, rec), "still being transcribed")
|
||||||
|
assert.True(t, uploadExists(t, id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadDelete_ANonIDIsA404NotAServerError(t *testing.T) {
|
||||||
|
app, _ := newUploadApp(t, audioOK, 200<<20)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, do(t, app, deleteReq("not-a-uuid")).Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUploadPage_OffersDeleteOnlyForAFinishedOrFailedRecording(t *testing.T) {
|
||||||
|
for status, want := range map[string]bool{"done": true, "failed": true, "queued": false, "transcribing": false} {
|
||||||
|
t.Run(status, func(t *testing.T) {
|
||||||
|
app, _ := newUploadApp(t, audioOK, 200<<20)
|
||||||
|
id := seedUpload(t, userID, "m", status, "transcription_failed")
|
||||||
|
|
||||||
|
b := body(t, do(t, app, get("/uploads/"+id)))
|
||||||
|
|
||||||
|
assert.Equal(t, want, strings.Contains(b, `action="/uploads/`+id+`/delete"`))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user