# B8 預設模型(簡單版:寫死常數 + 打包 + 不簽 token) 模型庫「預設模型」原本實際是空的(舊 seed 是 demo 假資料、production 不啟用)。 補 7 個公用預設模型(kl520×4 + kl720×3,來源 local-tool models.json): - metadata 寫死成 Go 常數(presets.go),不進 DB → 公用、不可刪、無 seed 重複 - 7 個 .nef 打包進 image(assets/preset-models/,~61MB;Dockerfile COPY) - download 走 visionA 自己(簡單版不簽 token,preset 公用本不需授權): download handler 三分支 preset→visionA URL / converted→FAA / uploaded→501 - 新 GET /preset-models/*filepath 靜態 serve(無 auth、Content-Disposition attachment 用 mime.FormatMediaType、path-traversal 防禦) - list/get 含 preset(對所有人可見) # B5 詳細頁下載按鈕 + preset 可下載 - model-detail-client.tsx 補下載按鈕(行為對齊列表卡片) - isModelDownloadable 加 preset(source==="preset" → true) - normalizeModelSummary 對 target_chip toLowerCase(修 preset 大寫 KL520 在小寫晶片篩選下被隱藏的 bug;收斂所有來源大小寫) # 測試 backend:presets/preset 下載三分支/靜態 serve/path-traversal/條件對稱 全綠 frontend:詳細頁下載鈕 + preset 可下載 + target_chip 大小寫篩選回歸 38 PASS Reviewer 兩輪通過(Major-1 + Minor-1/2 修畢、複審 0 問題) # backlog(未做的優化) preset 下載改 HMAC presigned / preset .nef 改放 FAA 減 image / metadata 後台可管理 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
77 lines
2.6 KiB
Go
77 lines
2.6 KiB
Go
package model
|
||
|
||
import (
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// TestPresetModels_Count 驗證恰好 7 個系統預設模型。
|
||
func TestPresetModels_Count(t *testing.T) {
|
||
got := PresetModels()
|
||
require.Len(t, got, 7)
|
||
}
|
||
|
||
// TestPresetModels_AllSourcePresetNoOwner 驗證每筆 preset 都是公用(Source=preset、無 owner、有 StorageKey、無 FAAObjectKey)。
|
||
func TestPresetModels_AllSourcePresetNoOwner(t *testing.T) {
|
||
for _, m := range PresetModels() {
|
||
assert.Equal(t, SourcePreset, m.Source, "preset %s source", m.ID)
|
||
assert.Empty(t, m.OwnerUserID, "preset %s should have no owner", m.ID)
|
||
assert.Empty(t, m.FAAObjectKey, "preset %s should not have FAAObjectKey", m.ID)
|
||
assert.Equal(t, "models/preset/"+m.ID+".nef", m.StorageKey, "preset %s storage key", m.ID)
|
||
assert.NotEmpty(t, m.Name, "preset %s name", m.ID)
|
||
assert.NotEmpty(t, m.TargetChip, "preset %s target chip", m.ID)
|
||
assert.Greater(t, m.FileSize, int64(0), "preset %s file size", m.ID)
|
||
}
|
||
}
|
||
|
||
// TestPresetModels_ExpectedIDs 驗證 7 個 id 與 models.json 對齊(順序固定)。
|
||
func TestPresetModels_ExpectedIDs(t *testing.T) {
|
||
want := []string{
|
||
"kl520-yolov5-detection",
|
||
"kl520-fcos-detection",
|
||
"kl520-ssd-face-detection",
|
||
"kl520-tiny-yolov3",
|
||
"kl720-yolov5-detection",
|
||
"kl720-resnet18-classification",
|
||
"kl720-fcos-detection",
|
||
}
|
||
got := PresetModels()
|
||
require.Len(t, got, len(want))
|
||
for i, id := range want {
|
||
assert.Equal(t, id, got[i].ID, "preset[%d] id", i)
|
||
}
|
||
}
|
||
|
||
// TestPresetByID_FoundAndNotFound 驗證查找。
|
||
func TestPresetByID_FoundAndNotFound(t *testing.T) {
|
||
m, ok := PresetByID("kl720-resnet18-classification")
|
||
require.True(t, ok)
|
||
assert.Equal(t, "ImageNet Classification ResNet18 (KL720)", m.Name)
|
||
assert.Equal(t, "KL720", m.TargetChip)
|
||
assert.Equal(t, []int{1, 3, 224, 224}, m.InputShape)
|
||
|
||
_, ok = PresetByID("not-a-preset")
|
||
assert.False(t, ok)
|
||
|
||
assert.True(t, IsPresetID("kl520-yolov5-detection"))
|
||
assert.False(t, IsPresetID("random-uuid"))
|
||
}
|
||
|
||
// TestPresetModels_ReturnsDeepCopy 驗證回傳是深拷貝——改動回傳值不影響後續呼叫(保護共用常數)。
|
||
func TestPresetModels_ReturnsDeepCopy(t *testing.T) {
|
||
a, ok := PresetByID("kl520-yolov5-detection")
|
||
require.True(t, ok)
|
||
origLen := len(a.Classes)
|
||
a.Name = "MUTATED"
|
||
a.Classes = append(a.Classes, "injected")
|
||
a.InputShape[0] = 999
|
||
|
||
b, ok := PresetByID("kl520-yolov5-detection")
|
||
require.True(t, ok)
|
||
assert.NotEqual(t, "MUTATED", b.Name, "回傳值修改不應污染共用常數")
|
||
assert.Len(t, b.Classes, origLen, "Classes slice 應為獨立拷貝")
|
||
assert.Equal(t, 1, b.InputShape[0], "InputShape slice 應為獨立拷貝")
|
||
}
|