优化兼容性

This commit is contained in:
truewhile
2026-10-11 23:44:11 +08:00
parent 3c30c64a39
commit 079b3ff063
23 changed files with 1246 additions and 142 deletions
@@ -0,0 +1,65 @@
package repository
import (
"context"
"strings"
"testing"
"github.com/truewhile/MeBox/internal/model"
)
// TestHistoryProgressUpsertQualifiesConflictWhereColumns 守住 PostgreSQL 下进度上报
// 的 upsert 语句合法性。
//
// ON CONFLICT DO UPDATE 的 WHERE 里如果不给列名加表限定,PostgreSQL 会因为这些列
// 同时出现在本次插入的行里而拒绝该语句:
//
// column reference "session_started_at_ms" is ambiguous (SQLSTATE 42702)
//
// 后果是 POST /api/history 全部 500,观看进度与续播完全存不下来。SQLite 不做这项
// 检查(旧写法在 SQLite 下一直正常),所以只能在 PostgreSQL 方言下断言。
func TestHistoryProgressUpsertQualifiesConflictWhereColumns(t *testing.T) {
repos, rec := newPostgresDryRunRepos(t)
ctx := context.Background()
if err := repos.History.UpsertProgress(ctx, &model.PlaybackHistory{
UserID: "u-1",
MediaID: "m-1",
PositionMs: 1_000,
DurationMs: 2_000,
SessionID: "session-1",
SessionStartedAtMs: 111,
Sequence: 2,
}); err != nil {
t.Fatalf("build upsert: %v", err)
}
sql := rec.all()
if strings.TrimSpace(sql) == "" {
t.Fatal("no SQL captured from UpsertProgress")
}
if !strings.Contains(sql, "ON CONFLICT") {
t.Fatalf("expected an ON CONFLICT upsert, got:\n%s", sql)
}
// 只看 ON CONFLICT 之后的部分:SET 的赋值目标和 CASE 里的 duration_ms 引用
// 属于另一套语法规则,不在这里断言。
conflict := sql[strings.Index(sql, "ON CONFLICT"):]
whereIdx := strings.Index(conflict, " WHERE ")
if whereIdx < 0 {
t.Fatalf("ON CONFLICT clause has no DO UPDATE ... WHERE guard:\n%s", sql)
}
where := conflict[whereIdx:]
for _, want := range []string{
`"playback_histories"."session_started_at_ms"`,
`"playback_histories"."sequence"`,
} {
if !strings.Contains(where, want) {
t.Fatalf("DO UPDATE WHERE is missing the table-qualified %s; it is ambiguous on PostgreSQL:\n%s", want, sql)
}
}
if strings.Contains(where, `("session_started_at_ms"`) || strings.Contains(where, ` AND "sequence"`) {
t.Fatalf("DO UPDATE WHERE still references unqualified columns:\n%s", sql)
}
}