Files
Agent-System-Backend/internal/runtime/model_test.go
T
weichen 9a545b1e69 Add admin-issued accounts with forced first-login password change
Why: the user ruled out self-registration (2026-09-30): accounts are
created only by admins who set an initial password; first login (and
first login after any admin reset) must force a password change.

Evidence:
- migration v10 auth_accounts (users password/must_change/is_admin +
  sessions table; upgrade test walks v1<->v10 both directions).
- argon2id hash/verify with PHC encoding; store tests cover hash
  roundtrip, duplicate-owner conflict, must-change lifecycle, session
  keep/except semantics.
- e2e TestAdminAccountFlow: static login page reachable without auth,
  wrong password 401, login sets HttpOnly cookie + mustChangePassword,
  admin creates user (409 on duplicate), non-admin create 403, forced
  change clears flag and old password dies, admin reset revokes sessions
  and re-arms forced change, API-key channel untouched, 5-fail backoff
  429, logout kills session. Full go test ./... green.

Impact: middleware now accepts Bearer key OR session cookie and exempts
static + /api/auth/login (also fixes the latent case where the login
page itself could 401). Embedded no-auth mode unchanged.

Refs: proposal 2026-09-30-ui-redesign-and-auth.md section 5 (admin-issued
variant, user decision 2026-09-30)
2026-09-30 11:15:22 +00:00

48 lines
1.8 KiB
Go

package runtime
import (
"testing"
core "agent-system-core"
)
func effOf(kv map[string]any) core.EffectiveSettings { return core.EffectiveSettings(kv) }
// 三层覆盖链解析:defaults → user → project 由 settings 服务合并(server 集成测试覆盖);
// 此处验证解析器对「设置层结果 + 画像层」的最后一级覆盖与回落。
func TestResolveChatModelLayers(t *testing.T) {
eff := effOf(map[string]any{"models": map[string]any{"chat": map[string]any{
"model": "model-settings", "baseUrl": "https://s.example", "apiKey": "k0",
}}})
prof := core.AgentProfile{}
// 仅设置层。
got := ResolveChatModel(prof, eff)
if got.Model != "model-settings" || got.Base != "https://s.example" || got.Key != "k0" {
t.Fatalf("settings layer: %+v", got)
}
// 画像覆盖模型(端点沿用设置层)。
prof.Model.Chat = "model-profile"
got = ResolveChatModel(prof, eff)
if got.Model != "model-profile" || got.Base != "https://s.example" {
t.Fatalf("profile layer should override model only: %+v", got)
}
}
func TestResolveLightweightFallback(t *testing.T) {
eff := effOf(map[string]any{"models": map[string]any{
"chat": map[string]any{"model": "main-m", "baseUrl": "https://main", "apiKey": "k"},
"lightweight": map[string]any{"model": ""},
}})
got := ResolveLightweightModel(core.AgentProfile{}, eff)
if got.Model != "main-m" || got.Base != "https://main" {
t.Fatalf("lightweight should fall back to chat: %+v", got)
}
// 轻量档只配模型名:模型用轻量、端点回落主档。
eff["models"].(map[string]any)["lightweight"] = map[string]any{"model": "lite-m"}
got = ResolveLightweightModel(core.AgentProfile{Model: core.ModelRoute{Lightweight: "prof-lite"}}, eff)
if got.Model != "prof-lite" || got.Base != "https://main" {
t.Fatalf("profile lightweight + chat endpoint: %+v", got)
}
}