DB 接入塊 0-5 上主幹後的收尾工作,讓 DB-on 模式可真人使用 + 補齊功能與測試。 OIDC / pairing FK 修復(接 DB 上線必要): - 新建 internal/user package(User + Store + InMemory + Postgres);OIDC callback 驗證 id_token 成功後 fail-closed upsert users(sub 直接當 users.id,MC sub 為 UUID) - pairing exchange 雲端自建 device(不動 local-tool)+ 同 tx 綁 session token; 自建 device 空 serial 寫 NULL(避免撞 partial unique) - device.SaveTx / session.CreateTx 新增 tx-aware 版本 B4 model metadata: - 轉檔 result 的 analysis_info(input_shape/classes/framework)串進 model: converter_client → flow → adapter → model.Model → PG → ModelResponse DTO - input_shape 優先用陣列、後備四維組 NCHW、缺一不亂組;全 optional 防禦性 - 前端詳細頁顯示(另 repo);轉檔端串接交接檔 b4-converter-handoff.md nginx healthz(部署層): - 新增 /healthz/deep 轉發 backend(ping PG+Redis、down 回 503)給 LB - 修掉 default_server return 444 短路 bug(docker healthcheck 長期 unhealthy 真因) storage error 統一映射(不洩漏 storage 後端細節)。 測試:補 internal/api(storage/errors handler)、cmd/api-server(seed/adapter)、 internal/db(redis)、relay/session 弱處,含 testcontainers integration。 DB 接入相關 package 真環境覆蓋達 88-94%。全程 Reviewer 審查 + 130 真 PG/Redis dbtest 綠。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111 lines
4.6 KiB
Go
111 lines
4.6 KiB
Go
//go:build dbtest
|
||
|
||
// seed_db_test.go — seedDemoData 的 DB-backed(dbBacked=true)整合測試。
|
||
//
|
||
// Owner: testing agent(補測任務 — cmd/api-server 可測部分,DB 分支)
|
||
//
|
||
// build tag `dbtest`:只在 `go test -tags=dbtest` 時編譯/執行(需 Docker)。
|
||
// 本機無 Docker,由 Orchestrator 在 130 跑:
|
||
//
|
||
// DOCKER_HOST=tcp://192.168.0.130:2375 TESTCONTAINERS_RYUK_DISABLED=true \
|
||
// go test -tags=dbtest ./cmd/api-server/ -run TestSeedDemoData_DBBacked -count=1
|
||
//
|
||
// 對齊 seed.go docstring 的 DB-backed 契約:
|
||
// - 先 upsert demoSeedUserID 對應 users 列(滿足 models/devices/pairing_tokens 的 owner FK)。
|
||
// - model/device id 改用合法 UUID(非雛形字串),owner 統一為 demoSeedUserID。
|
||
// - 全部落 Postgres(用真 PG repository,非 in-memory)。
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
|
||
"visiona-backend/internal/auth"
|
||
"visiona-backend/internal/db/testsupport"
|
||
"visiona-backend/internal/device"
|
||
"visiona-backend/internal/model"
|
||
"visiona-backend/internal/user"
|
||
)
|
||
|
||
// TestSeedDemoData_DBBacked_UpsertsUserAndSeedsAll:DB-backed seed 應
|
||
// 1. upsert demoSeedUserID 進 users(滿足 FK)
|
||
// 2. 用 demoSeedUserID 當 owner 建 device + model + pairing token(全落 PG)
|
||
func TestSeedDemoData_DBBacked_UpsertsUserAndSeedsAll(t *testing.T) {
|
||
tdb := testsupport.SetupTestDB(t)
|
||
tdb.Truncate(t, "pairing_tokens", "session_tokens", "models", "devices", "users")
|
||
|
||
ctx := context.Background()
|
||
devRepo := device.NewPostgresRepository(tdb.Pool)
|
||
mdlRepo := model.NewPostgresRepository(tdb.Pool)
|
||
pairings := auth.NewPostgresPairingStore(tdb.Pool)
|
||
userStore := user.NewPostgresStore(tdb.Pool)
|
||
|
||
// userID 傳入雛形 StaticUserID(非 UUID)—— DB-backed 分支應忽略它、改用 demoSeedUserID。
|
||
require.NoError(t, seedDemoData(
|
||
devRepo, mdlRepo, pairings, userStore,
|
||
"demo-user", true /* dbBacked */, discardLog(),
|
||
))
|
||
|
||
// 1. demo user 已 upsert(owner FK 前提)
|
||
u, err := userStore.Get(ctx, demoSeedUserID)
|
||
require.NoError(t, err, "DB-backed seed 應 upsert demoSeedUserID")
|
||
assert.Equal(t, "demo@visiona.local", u.Email)
|
||
|
||
// 2. device:owner = demoSeedUserID(非傳入的 demo-user)
|
||
devs, err := devRepo.List(ctx, demoSeedUserID)
|
||
require.NoError(t, err)
|
||
require.Len(t, devs, 1, "DB-backed seed 應建 1 個 device 掛在 demoSeedUserID")
|
||
assert.Equal(t, demoSeedUserID, devs[0].OwnerUserID)
|
||
assert.Equal(t, "kl520", devs[0].DeviceType)
|
||
// 註:device 確實掛在 demoSeedUserID(上方斷言已證),且 owner_user_id 為 UUID 欄位,
|
||
// 傳入的非-UUID "demo-user" 在型別上不可能成為任何 device 的 owner,故無需另查(查了反而會被 PG reject)。
|
||
|
||
// 3. model:owner = demoSeedUserID
|
||
mdls, err := mdlRepo.List(ctx, model.ListFilter{OwnerUserID: demoSeedUserID})
|
||
require.NoError(t, err)
|
||
require.Len(t, mdls, 1, "DB-backed seed 應建 1 個 model 掛在 demoSeedUserID")
|
||
assert.Equal(t, demoSeedUserID, mdls[0].OwnerUserID)
|
||
assert.Equal(t, model.SourceUploaded, mdls[0].Source)
|
||
|
||
// 4. pairing token:owner = demoSeedUserID
|
||
tokens, err := pairings.List(ctx, demoSeedUserID)
|
||
require.NoError(t, err)
|
||
require.Len(t, tokens, 1, "DB-backed seed 應建 1 個 pairing token 掛在 demoSeedUserID")
|
||
assert.Equal(t, demoSeedUserID, tokens[0].UserID)
|
||
}
|
||
|
||
// TestSeedDemoData_DBBacked_GeneratesUUIDIDs:DB-backed 分支的 model/device id 必須是合法 UUID
|
||
// (schema 的 id 欄位為 UUID + FK;雛形的 "demo-model-xxx" 字串會違反型別)。
|
||
func TestSeedDemoData_DBBacked_GeneratesUUIDIDs(t *testing.T) {
|
||
tdb := testsupport.SetupTestDB(t)
|
||
tdb.Truncate(t, "pairing_tokens", "session_tokens", "models", "devices", "users")
|
||
|
||
ctx := context.Background()
|
||
devRepo := device.NewPostgresRepository(tdb.Pool)
|
||
mdlRepo := model.NewPostgresRepository(tdb.Pool)
|
||
pairings := auth.NewPostgresPairingStore(tdb.Pool)
|
||
userStore := user.NewPostgresStore(tdb.Pool)
|
||
|
||
require.NoError(t, seedDemoData(
|
||
devRepo, mdlRepo, pairings, userStore,
|
||
"demo-user", true, discardLog(),
|
||
))
|
||
|
||
devs, err := devRepo.List(ctx, demoSeedUserID)
|
||
require.NoError(t, err)
|
||
require.Len(t, devs, 1)
|
||
assert.Regexp(t,
|
||
`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`,
|
||
devs[0].ID, "DB-backed device id 應為合法 UUID")
|
||
|
||
mdls, err := mdlRepo.List(ctx, model.ListFilter{OwnerUserID: demoSeedUserID})
|
||
require.NoError(t, err)
|
||
require.Len(t, mdls, 1)
|
||
assert.Regexp(t,
|
||
`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`,
|
||
mdls[0].ID, "DB-backed model id 應為合法 UUID")
|
||
}
|