Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72cb25111f | ||
|
|
38f222c931 | ||
|
|
e042b6d26d | ||
|
|
c85a32e770 | ||
|
|
b246c0e688 | ||
|
|
c7896cb3cc | ||
|
|
b79fb892c8 | ||
|
|
c315e3e003 |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"brain": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://brain-mcp.d-ma.be/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer ${BRAIN_MCP_TOKEN}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitea": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://git-mcp.d-ma.be/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer ${GITEA_MCP_TOKEN}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ docs it indexes.
|
|||||||
3. `DECISIONS.md` — the ADRs. Decisions are settled here; do not re-litigate without a new ADR.
|
3. `DECISIONS.md` — the ADRs. Decisions are settled here; do not re-litigate without a new ADR.
|
||||||
4. `docs/architecture/architecture.md`, `docs/data-model.md`, `docs/use-cases/*.feature`.
|
4. `docs/architecture/architecture.md`, `docs/data-model.md`, `docs/use-cases/*.feature`.
|
||||||
5. `docs/homelab-integration.md` — the concrete endpoints/conventions you'll need.
|
5. `docs/homelab-integration.md` — the concrete endpoints/conventions you'll need.
|
||||||
|
6. `LANGUAGE.md` — the project vocabulary. Apply the caveman rubric before destructive operations.
|
||||||
|
|
||||||
## How to work in this repo
|
## How to work in this repo
|
||||||
|
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
# Vocabulary (hand-written pilot 2026-06 — will be generated by langgen in Phase 2)
|
||||||
|
|
||||||
|
| Term | Means | Never say |
|
||||||
|
|---|---|---|
|
||||||
|
| transcript | Captions-first text of a video; the source for summarizing. Shared store keyed by `(provider, video_id)`. | "subtitles file", "the audio" |
|
||||||
|
| video | Per-user record of a seen video; the unit of work. Not globally deduped. | "the shared video" |
|
||||||
|
| subscription | A watched channel on a user's connection that Tapir polls for new videos. | "feed", "follow" |
|
||||||
|
| summary | A video's produced output: summary text + highlights + takeaways + AI provenance. | "transcript" |
|
||||||
|
| highlight | A notable point pulled from a video (`Summary.Highlights`). | "takeaway" |
|
||||||
|
| takeaway | An actionable conclusion from a video (`Summary.Takeaways`). | "highlight" |
|
||||||
|
| sink | A delivery destination for a summary (`store`, `brain`). New one = new adapter. | "the database" |
|
||||||
|
| AI router | Local-first chain: local Primary → local fallback → external worst-case. | "the API" |
|
||||||
|
| BYO-AI fallback | User's own external AI key, used only when local fails; opt-in. Without it, content is never sent externally. | "the default AI" |
|
||||||
|
| brain | Persistent homelab knowledge store; in Tapir, one optional HTTP sink (`brain_ingest`), not the filesystem package. | "the database", "the store" |
|
||||||
|
| LiteLLM gateway | Local AI gateway (`koala:30401/v1`); the Primary in the AI router. | "piguard:4000", "koala:4000", "the cloud" |
|
||||||
|
| connection | A connected video account a user authorizes via OAuth; subscriptions hang off it. | "login", "session" |
|
||||||
|
|
||||||
|
## Caveman rubric
|
||||||
|
Before any HIGH/CRITICAL operation, output one line:
|
||||||
|
`caveman: me <verb> <object>, not <excluded thing>`
|
||||||
|
Valid iff: (1) only vocabulary terms + plain verbs, (2) a stranger could identify
|
||||||
|
the exact operation, (3) names one thing explicitly NOT being done.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- `caveman: me delete user connection, not the shared transcript`
|
||||||
|
- `caveman: me send transcript to BYO-AI fallback, not the LiteLLM gateway`
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/config"
|
"git.d-ma.be/mathias/tapir/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestChatModelsReuseTheChainLocalFirst: the switcher offers the ADR-022 chain in
|
// TestChatModelsReuseTheChainLocalFirst: the switcher offers the ADR-022 chain in
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// discoveryRunner runs one user's discovery pass.
|
// discoveryRunner runs one user's discovery pass.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// serialize must guarantee at most one discovery pass runs at a time, so a
|
// serialize must guarantee at most one discovery pass runs at a time, so a
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Env var names for the read-only CLI. DSN and user id are never hardcoded — the
|
// Env var names for the read-only CLI. DSN and user id are never hardcoded — the
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFormatListColumnsAndOrdering(t *testing.T) {
|
func TestFormatListColumnsAndOrdering(t *testing.T) {
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// runList prints the user's stored summaries as a table, most recent first.
|
// runList prints the user's stored summaries as a table, most recent first.
|
||||||
|
|||||||
+9
-9
@@ -23,15 +23,15 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
"git.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
"git.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/auth"
|
"git.d-ma.be/mathias/tapir/internal/auth"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/config"
|
"git.d-ma.be/mathias/tapir/internal/config"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web/oidc"
|
"git.d-ma.be/mathias/tapir/internal/web/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
+12
-12
@@ -5,18 +5,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/chat"
|
"git.d-ma.be/mathias/tapir/internal/adapters/chat"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/llm"
|
"git.d-ma.be/mathias/tapir/internal/adapters/llm"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
"git.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/summarizer"
|
"git.d-ma.be/mathias/tapir/internal/adapters/summarizer"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
"git.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/config"
|
"git.d-ma.be/mathias/tapir/internal/config"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/usecase"
|
"git.d-ma.be/mathias/tapir/internal/usecase"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// videoFetcher adapts the YouTube adapter to web.VideoFetcher for the paste flow
|
// videoFetcher adapts the YouTube adapter to web.VideoFetcher for the paste flow
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/config"
|
"git.d-ma.be/mathias/tapir/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestBuildProcessorNilOnIncompleteConfig asserts the queue-only fallback: when a
|
// TestBuildProcessorNilOnIncompleteConfig asserts the queue-only fallback: when a
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gateThreshold is the Stage-0 gate (VISION/ADR-016): usage in >= 2 distinct
|
// gateThreshold is the Stage-0 gate (VISION/ADR-016): usage in >= 2 distinct
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testSince = time.Date(2026, 6, 11, 0, 0, 0, 0, time.UTC)
|
var testSince = time.Date(2026, 6, 11, 0, 0, 0, 0, time.UTC)
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
"git.d-ma.be/mathias/tapir/internal/adapters/youtube"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/config"
|
"git.d-ma.be/mathias/tapir/internal/config"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/usecase"
|
"git.d-ma.be/mathias/tapir/internal/usecase"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// buildUserRunner constructs a runner.Runner for one user, reusing the same
|
// buildUserRunner constructs a runner.Runner for one user, reusing the same
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
func quietLog() *slog.Logger {
|
func quietLog() *slog.Logger {
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// runShow prints the full summary for one video: text, highlights, takeaways,
|
// runShow prints the full summary for one video: text, highlights, takeaways,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module gitea.d-ma.be/mathias/tapir
|
module git.d-ma.be/mathias/tapir
|
||||||
|
|
||||||
go 1.26.1
|
go 1.26.1
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Completer is the minimal LLM chat surface the Service needs. *llm.Client
|
// Completer is the minimal LLM chat surface the Service needs. *llm.Client
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileStore is a SecretStore backed by a single 0600 JSON file mapping opaque
|
// FileStore is a SecretStore backed by a single 0600 JSON file mapping opaque
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
"git.d-ma.be/mathias/tapir/internal/adapters/secrets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPutThenGet(t *testing.T) {
|
func TestPutThenGet(t *testing.T) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// seedUserRow inserts a bare users row (FK target for a connection) as the
|
// seedUserRow inserts a bare users row (FK target for a connection) as the
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package store_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -34,6 +35,30 @@ func fileMigrator(t *testing.T) *migrate.Migrate {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headVersion reports the current (HEAD) schema version so a test can restore
|
||||||
|
// to it after stepping down, without hard-coding what HEAD is. Adding a
|
||||||
|
// migration on top changes HEAD but no test that uses this needs editing.
|
||||||
|
func headVersion(t *testing.T, m *migrate.Migrate) uint {
|
||||||
|
t.Helper()
|
||||||
|
v, dirty, err := m.Version()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, dirty, "schema must not be dirty")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrateTo drives the schema to an exact version *by version number*, not by
|
||||||
|
// step count. This is the whole point of the migrate-test design: a migration
|
||||||
|
// added above the target does not shift any count here, so unrelated tests stay
|
||||||
|
// green (see issue #8). ErrNoChange (already at that version) is not a failure.
|
||||||
|
func migrateTo(t *testing.T, m *migrate.Migrate, version uint) {
|
||||||
|
t.Helper()
|
||||||
|
err := m.Migrate(version)
|
||||||
|
if errors.Is(err, migrate.ErrNoChange) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
// loginEventsExists reports whether the login_events relation is present.
|
// loginEventsExists reports whether the login_events relation is present.
|
||||||
func loginEventsExists(t *testing.T) bool {
|
func loginEventsExists(t *testing.T) bool {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -53,25 +78,15 @@ func TestMigration010LoginEventsUpDown(t *testing.T) {
|
|||||||
require.True(t, loginEventsExists(t), "login_events must exist at latest migration")
|
require.True(t, loginEventsExists(t), "login_events must exist at latest migration")
|
||||||
|
|
||||||
m := fileMigrator(t)
|
m := fileMigrator(t)
|
||||||
// 011..016 sit above 010; step them down first so 010 is exercised in isolation.
|
head := headVersion(t, m)
|
||||||
require.NoError(t, m.Steps(-1), "down 016 drops channel_caption_state, login_events intact")
|
|
||||||
require.True(t, loginEventsExists(t), "016 down leaves login_events intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 015 reshapes transcripts, login_events intact")
|
|
||||||
require.True(t, loginEventsExists(t), "015 down leaves login_events intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 014 drops channel_title, login_events intact")
|
|
||||||
require.True(t, loginEventsExists(t), "014 down leaves login_events intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 013 drops channel_errors, login_events intact")
|
|
||||||
require.True(t, loginEventsExists(t), "013 down leaves login_events intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 012 is a no-op, login_events intact")
|
|
||||||
require.True(t, loginEventsExists(t), "012 down leaves login_events intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 011 must not touch login_events")
|
|
||||||
require.True(t, loginEventsExists(t), "011 down leaves login_events intact")
|
|
||||||
|
|
||||||
require.NoError(t, m.Steps(-1), "down 010 must drop login_events")
|
migrateTo(t, m, 9) // just below 010 — everything above steps down
|
||||||
require.False(t, loginEventsExists(t), "login_events must be gone after the down migration")
|
require.False(t, loginEventsExists(t), "login_events must be gone after the down migration")
|
||||||
|
|
||||||
require.NoError(t, m.Steps(7), "up must recreate 010 then re-apply 011..016")
|
migrateTo(t, m, 10) // up 010
|
||||||
require.True(t, loginEventsExists(t), "login_events must be restored after the up migration")
|
require.True(t, loginEventsExists(t), "login_events must be restored after the up migration")
|
||||||
|
|
||||||
|
migrateTo(t, m, head) // restore to HEAD for sibling tests
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoSummarizeDefault reads the users.auto_summarize column default as text
|
// autoSummarizeDefault reads the users.auto_summarize column default as text
|
||||||
@@ -93,21 +108,15 @@ func TestMigration011AutoSummarizeDefaultUpDown(t *testing.T) {
|
|||||||
require.Equal(t, "true", autoSummarizeDefault(t), "011 sets the default to TRUE")
|
require.Equal(t, "true", autoSummarizeDefault(t), "011 sets the default to TRUE")
|
||||||
|
|
||||||
m := fileMigrator(t)
|
m := fileMigrator(t)
|
||||||
require.NoError(t, m.Steps(-1), "down 016 drops channel_caption_state")
|
head := headVersion(t, m)
|
||||||
require.NoError(t, m.Steps(-1), "down 015 reshapes transcripts")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 014 drops channel_title")
|
migrateTo(t, m, 10) // just below 011 — reverts the column default
|
||||||
require.NoError(t, m.Steps(-1), "down 013 drops channel_errors")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 012 is a no-op")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 011 reverts the column default")
|
|
||||||
require.Equal(t, "false", autoSummarizeDefault(t), "default is FALSE after the down migration")
|
require.Equal(t, "false", autoSummarizeDefault(t), "default is FALSE after the down migration")
|
||||||
|
|
||||||
require.NoError(t, m.Steps(1), "up 011 re-applies the TRUE default")
|
migrateTo(t, m, 11) // up 011 re-applies the TRUE default
|
||||||
require.Equal(t, "true", autoSummarizeDefault(t))
|
require.Equal(t, "true", autoSummarizeDefault(t))
|
||||||
require.NoError(t, m.Steps(1), "up 012 runs clean (no FORCE RLS on fresh schema)")
|
|
||||||
require.NoError(t, m.Steps(1), "up 013 creates channel_errors")
|
migrateTo(t, m, head) // restore to HEAD for sibling tests
|
||||||
require.NoError(t, m.Steps(1), "up 014 recreates channel_title")
|
|
||||||
require.NoError(t, m.Steps(1), "up 015 reshapes transcripts to shared")
|
|
||||||
require.NoError(t, m.Steps(1), "up 016 recreates channel_caption_state (HEAD)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// channelTitleExists reports whether videos.channel_title is present.
|
// channelTitleExists reports whether videos.channel_title is present.
|
||||||
@@ -127,17 +136,15 @@ func TestMigration014VideoChannelTitleUpDown(t *testing.T) {
|
|||||||
require.True(t, channelTitleExists(t), "channel_title exists at latest migration")
|
require.True(t, channelTitleExists(t), "channel_title exists at latest migration")
|
||||||
|
|
||||||
m := fileMigrator(t)
|
m := fileMigrator(t)
|
||||||
require.NoError(t, m.Steps(-1), "down 016 drops channel_caption_state, channel_title intact")
|
head := headVersion(t, m)
|
||||||
require.True(t, channelTitleExists(t), "016 down leaves channel_title intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 015 reshapes transcripts, channel_title intact")
|
migrateTo(t, m, 13) // just below 014 — drops channel_title
|
||||||
require.True(t, channelTitleExists(t), "015 down leaves channel_title intact")
|
|
||||||
require.NoError(t, m.Steps(-1), "down 014 must drop channel_title")
|
|
||||||
require.False(t, channelTitleExists(t), "channel_title must be gone after the down migration")
|
require.False(t, channelTitleExists(t), "channel_title must be gone after the down migration")
|
||||||
|
|
||||||
require.NoError(t, m.Steps(1), "up 014 must recreate channel_title")
|
migrateTo(t, m, 14) // up 014 recreates channel_title
|
||||||
require.True(t, channelTitleExists(t), "channel_title must be restored after the up migration")
|
require.True(t, channelTitleExists(t), "channel_title must be restored after the up migration")
|
||||||
require.NoError(t, m.Steps(1), "up 015 restores the shared transcripts shape")
|
|
||||||
require.NoError(t, m.Steps(1), "up 016 recreates channel_caption_state (HEAD)")
|
migrateTo(t, m, head) // restore to HEAD for sibling tests
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestMigration012FixAutoSummarizeRLS proves 012 runs cleanly and flips any
|
// TestMigration012FixAutoSummarizeRLS proves 012 runs cleanly and flips any
|
||||||
@@ -148,7 +155,11 @@ func TestMigration012FixAutoSummarizeRLS(t *testing.T) {
|
|||||||
|
|
||||||
// Round-trip: down 012, then up 012 — must be idempotent.
|
// Round-trip: down 012, then up 012 — must be idempotent.
|
||||||
m := fileMigrator(t)
|
m := fileMigrator(t)
|
||||||
require.NoError(t, m.Steps(-1), "down 012 must not error")
|
head := headVersion(t, m)
|
||||||
require.NoError(t, m.Steps(1), "up 012 must re-apply cleanly")
|
|
||||||
|
migrateTo(t, m, 11) // down 012 must not error
|
||||||
|
migrateTo(t, m, 12) // up 012 must re-apply cleanly
|
||||||
require.Equal(t, "true", autoSummarizeDefault(t), "default still TRUE after 012 re-applied")
|
require.Equal(t, "true", autoSummarizeDefault(t), "default still TRUE after 012 re-applied")
|
||||||
|
|
||||||
|
migrateTo(t, m, head) // restore to HEAD for sibling tests
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// seedVideo inserts a videos row whose id matches a summary's video_id, so the
|
// seedVideo inserts a videos row whose id matches a summary's video_id, so the
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
|
|
||||||
_ "github.com/jackc/pgx/v5/stdlib" // register the "pgx" database/sql driver for migrate
|
_ "github.com/jackc/pgx/v5/stdlib" // register the "pgx" database/sql driver for migrate
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed migrations/*.sql
|
//go:embed migrations/*.sql
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Static check: Store satisfies the Sink port.
|
// Static check: Store satisfies the Sink port.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// seedBareVideo inserts a videos row with no summary, so the all-videos read and
|
// seedBareVideo inserts a videos row with no summary, so the all-videos read and
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTranscript returns the shared, stored transcript for a video keyed by the
|
// GetTranscript returns the shared, stored transcript for a video keyed by the
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetTranscriptStatus_RoundTrip(t *testing.T) {
|
func TestSetTranscriptStatus_RoundTrip(t *testing.T) {
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Static check: Store satisfies the shared TranscriptStore port (ADR-021).
|
// Static check: Store satisfies the shared TranscriptStore port (ADR-021).
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpsertVideo persists a video's metadata and returns its durable store id (the
|
// UpsertVideo persists a video's metadata and returns its durable store id (the
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ytVideo(userID, provVideoID, title string) domain.Video {
|
func ytVideo(userID, provVideoID, title string) domain.Video {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Completer is the minimal LLM chat surface the Summarizer needs.
|
// Completer is the minimal LLM chat surface the Summarizer needs.
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSummarizerRecordsMetric verifies the summarizer→metrics wiring (ADR-030)
|
// TestSummarizerRecordsMetric verifies the summarizer→metrics wiring (ADR-030)
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultPlayerBaseURL is the InnerTube / watch-page host. Overridable via
|
// defaultPlayerBaseURL is the InnerTube / watch-page host. Overridable via
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVideoByID(t *testing.T) {
|
func TestVideoByID(t *testing.T) {
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultBaseURL is the YouTube Data API v3 root. Overridable via Config.BaseURL
|
// defaultBaseURL is the YouTube Data API v3 root. Overridable via Config.BaseURL
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- fakes ------------------------------------------------------------------
|
// --- fakes ------------------------------------------------------------------
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/auth"
|
"git.d-ma.be/mathias/tapir/internal/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeWriter is a TokenWriter capturing the persisted (ref, value).
|
// fakeWriter is a TokenWriter capturing the persisted (ref, value).
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package ports
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VideoSource is a video platform Tapir watches (YouTube, Vimeo).
|
// VideoSource is a video platform Tapir watches (YouTube, Vimeo).
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/usecase"
|
"git.d-ma.be/mathias/tapir/internal/usecase"
|
||||||
)
|
)
|
||||||
|
|
||||||
// passCandidate is a video that passed all pre-filters (seen/manual/backoff)
|
// passCandidate is a video that passed all pre-filters (seen/manual/backoff)
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/runner"
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/usecase"
|
"git.d-ma.be/mathias/tapir/internal/usecase"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testUser = "11111111-1111-1111-1111-111111111111"
|
const testUser = "11111111-1111-1111-1111-111111111111"
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/ports"
|
"git.d-ma.be/mathias/tapir/internal/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNotImplemented marks scaffold methods awaiting implementation.
|
// ErrNotImplemented marks scaffold methods awaiting implementation.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These tests pin the ADR-021 read-stored-first behaviour at the engine core:
|
// These tests pin the ADR-021 read-stored-first behaviour at the engine core:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handleAccount renders the account page: the user's display name, the
|
// handleAccount renders the account page: the user's display name, the
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeSecrets is a SecretRemover that records the refs it was asked to delete, so
|
// fakeSecrets is a SecretRemover that records the refs it was asked to delete, so
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/chat"
|
"git.d-ma.be/mathias/tapir/internal/adapters/chat"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Chatter is the per-video chat backend (ADR-027). *chat.Service satisfies it;
|
// Chatter is the per-video chat backend (ADR-027). *chat.Service satisfies it;
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/chat"
|
"git.d-ma.be/mathias/tapir/internal/adapters/chat"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// videoZ is a video id used by the isolation test for a DIFFERENT user's video.
|
// videoZ is a video id used by the isolation test for a DIFFERENT user's video.
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/auth"
|
"git.d-ma.be/mathias/tapir/internal/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connections is the narrow write port the connect flow depends on (Clean
|
// Connections is the narrow write port the connect flow depends on (Clean
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/auth"
|
"git.d-ma.be/mathias/tapir/internal/auth"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeWriter is a TokenWriter capturing the persisted (ref, value).
|
// fakeWriter is a TokenWriter capturing the persisted (ref, value).
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store is the read/write surface the web handlers depend on — a narrow port over
|
// Store is the read/write surface the web handlers depend on — a narrow port over
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dsn points at the in-process Postgres started in TestMain. Handler tests run
|
// dsn points at the in-process Postgres started in TestMain. Handler tests run
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import (
|
|||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/metrics"
|
"git.d-ma.be/mathias/tapir/internal/metrics"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is the OIDC + session configuration. cmd/tapir maps these from
|
// Config is the OIDC + session configuration. cmd/tapir maps these from
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
josev4 "github.com/go-jose/go-jose/v4"
|
josev4 "github.com/go-jose/go-jose/v4"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web/oidc"
|
"git.d-ma.be/mathias/tapir/internal/web/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// randToken returns a URL-safe 256-bit random string for session IDs, OIDC
|
// randToken returns a URL-safe 256-bit random string for session IDs, OIDC
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeFetcher is a web.VideoFetcher returning a fixed video (or an error),
|
// fakeFetcher is a web.VideoFetcher returning a fixed video (or an error),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package web
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Processor runs the core summarization use case for a single already-discovered
|
// Processor runs the core summarization use case for a single already-discovered
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeProcessor records ProcessVideo calls. With block set it parks until the
|
// fakeProcessor records ProcessVideo calls. With block set it parks until the
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func renderVideoCard(t *testing.T, r store.SummaryRow) string {
|
func renderVideoCard(t *testing.T, r store.SummaryRow) string {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// youtubeIDRe matches a canonical 11-char YouTube video id (the provider's
|
// youtubeIDRe matches a canonical 11-char YouTube video id (the provider's
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
"git.d-ma.be/mathias/tapir/internal/adapters/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Layout is the shared HTML shell. HTMX drives the progressive interactions
|
// Layout is the shared HTML shell. HTMX drives the progressive interactions
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/web"
|
"git.d-ma.be/mathias/tapir/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakeAuth is a configurable web.Auth for the landing-page tests: it reports a
|
// fakeAuth is a configurable web.Auth for the landing-page tests: it reports a
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/domain"
|
"git.d-ma.be/mathias/tapir/internal/domain"
|
||||||
"gitea.d-ma.be/mathias/tapir/internal/usecase"
|
"git.d-ma.be/mathias/tapir/internal/usecase"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- fake adapters ---------------------------------------------------------
|
// --- fake adapters ---------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user