jim800121chen cabbdde495 feat(visionA-backend): DB 接入後續 — OIDC/pairing FK 收尾 + B4 metadata + nginx healthz + 補測試
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>
2026-06-21 06:36:35 +08:00

111 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build dbtest
// seed_db_test.go — seedDemoData 的 DB-backeddbBacked=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_UpsertsUserAndSeedsAllDB-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 已 upsertowner 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. deviceowner = 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. modelowner = 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 tokenowner = 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_GeneratesUUIDIDsDB-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")
}