Infra ADR-0004 renamed the Gitea host. Bulk replace across go.mod and all .go import paths. Build and tests pass unchanged. Closes #20 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dt6aHEDWRjkK14Voi6HnGh
135 lines
4.2 KiB
Go
135 lines
4.2 KiB
Go
package web_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
|
)
|
|
|
|
// fakeFetcher is a web.VideoFetcher returning a fixed video (or an error),
|
|
// scoped to whatever (userID, videoID) the handler asks for.
|
|
type fakeFetcher struct {
|
|
title string
|
|
err error
|
|
calls int
|
|
}
|
|
|
|
func (f *fakeFetcher) FetchVideo(_ context.Context, userID, videoID string) (domain.Video, error) {
|
|
f.calls++
|
|
if f.err != nil {
|
|
return domain.Video{}, f.err
|
|
}
|
|
return domain.Video{
|
|
UserID: userID,
|
|
Provider: domain.ProviderYouTube,
|
|
ProviderVideoID: videoID,
|
|
Title: f.title,
|
|
URL: "https://www.youtube.com/watch?v=" + videoID,
|
|
}, nil
|
|
}
|
|
|
|
func pasteReq(rawURL string) *http.Request {
|
|
req := httptest.NewRequest(http.MethodPost, "/paste",
|
|
strings.NewReader("url="+url.QueryEscape(rawURL)))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
return req
|
|
}
|
|
|
|
func TestPasteValidURLAddsAndRequests(t *testing.T) {
|
|
ctx := context.Background()
|
|
app := newApp(t)
|
|
resetDB(t, rawPool(t))
|
|
app.Fetcher = &fakeFetcher{title: "Pasted Talk"}
|
|
p := rawPool(t)
|
|
|
|
rec := do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ"))
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
|
|
|
var (
|
|
count, requested int
|
|
title string
|
|
)
|
|
require.NoError(t, p.QueryRow(ctx,
|
|
`SELECT count(*), coalesce(max(title),'') FROM videos
|
|
WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ'`, userID).Scan(&count, &title))
|
|
require.Equal(t, 1, count, "pasted video added once, scoped to the user")
|
|
require.Equal(t, "Pasted Talk", title)
|
|
|
|
require.NoError(t, p.QueryRow(ctx,
|
|
`SELECT count(*) FROM videos
|
|
WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ' AND summarize_requested`,
|
|
userID).Scan(&requested))
|
|
require.Equal(t, 1, requested, "pasted video is queued for summarization (through the gate)")
|
|
}
|
|
|
|
func TestPasteInvalidURLRejected(t *testing.T) {
|
|
app := newApp(t)
|
|
resetDB(t, rawPool(t))
|
|
app.Fetcher = &fakeFetcher{title: "x"}
|
|
|
|
rec := do(t, app, pasteReq("definitely not a url"))
|
|
require.Equal(t, http.StatusBadRequest, rec.Code)
|
|
|
|
var count int
|
|
require.NoError(t, rawPool(t).QueryRow(context.Background(),
|
|
`SELECT count(*) FROM videos WHERE user_id=$1`, userID).Scan(&count))
|
|
require.Equal(t, 0, count, "invalid input adds nothing")
|
|
}
|
|
|
|
func TestPasteVideoNotFound(t *testing.T) {
|
|
app := newApp(t)
|
|
resetDB(t, rawPool(t))
|
|
app.Fetcher = &fakeFetcher{err: domain.ErrVideoNotFound}
|
|
|
|
rec := do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ"))
|
|
require.Equal(t, http.StatusNotFound, rec.Code)
|
|
|
|
var count int
|
|
require.NoError(t, rawPool(t).QueryRow(context.Background(),
|
|
`SELECT count(*) FROM videos WHERE user_id=$1`, userID).Scan(&count))
|
|
require.Equal(t, 0, count, "a not-found video adds nothing")
|
|
}
|
|
|
|
func TestPasteDedupNoDuplicate(t *testing.T) {
|
|
app := newApp(t)
|
|
resetDB(t, rawPool(t))
|
|
app.Fetcher = &fakeFetcher{title: "Pasted Talk"}
|
|
|
|
require.Equal(t, http.StatusOK, do(t, app, pasteReq("https://youtu.be/dQw4w9WgXcQ")).Code)
|
|
require.Equal(t, http.StatusOK, do(t, app, pasteReq("https://www.youtube.com/watch?v=dQw4w9WgXcQ")).Code)
|
|
|
|
var count int
|
|
require.NoError(t, rawPool(t).QueryRow(context.Background(),
|
|
`SELECT count(*) FROM videos WHERE user_id=$1 AND provider_video_id='dQw4w9WgXcQ'`,
|
|
userID).Scan(&count))
|
|
require.Equal(t, 1, count, "pasting the same video twice must not duplicate the row")
|
|
}
|
|
|
|
func TestListShowsPasteFormForConnectedUserWithVideos(t *testing.T) {
|
|
app := newApp(t)
|
|
resetDB(t, rawPool(t))
|
|
app.Fetcher = &fakeFetcher{title: "x"}
|
|
p := rawPool(t)
|
|
|
|
// Connected user with a non-empty feed (the case the bug missed: hasConnected
|
|
// was only computed for an empty feed).
|
|
_, err := p.Exec(context.Background(),
|
|
`INSERT INTO video_connections (user_id, provider, token_ref, status)
|
|
VALUES ($1, 'youtube', 'youtube/x/refresh_token', 'active')`, userID)
|
|
require.NoError(t, err)
|
|
seedVideo(t, p, "11111111-1111-1111-1111-111111111111", "A talk", "https://youtu.be/aaaaaaaaaaa", time.Now())
|
|
|
|
rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil))
|
|
require.Equal(t, http.StatusOK, rec.Code)
|
|
require.Contains(t, body(t, rec), `action="/paste"`,
|
|
"a connected user must see the paste box even when the feed has videos")
|
|
}
|