feat: teai role

This commit is contained in:
ydc3148
2026-09-14 16:13:02 +08:00
parent 8735e35db6
commit 540718dda7
12 changed files with 416 additions and 120 deletions
+41 -23
View File
@@ -1,5 +1,5 @@
// auth_test.go 驗證認證來源選序(--token → TEAI_TOKEN → tea 組態)
// 與 tea 組態解析。
// auth_test.go 驗證認證來源選序(--token → TEAI_TOKEN → teai 組態)
// 與組態解析。
package gitea
import (
@@ -25,7 +25,7 @@ func fakeReadFile(m map[string]string) ReadFileFunc {
}
}
const sampleTeaConfig = `logins:
const sampleConfig = `logins:
- name: other.example.com
url: https://other.example.com
token: other-token
@@ -43,7 +43,7 @@ func TestResolveTokenOrdering(t *testing.T) {
SiteURL: "https://gitea.alterminal.com",
Env: fakeEnv(nil),
ReadFile: fakeReadFile(map[string]string{
"/cfg/config.yml": sampleTeaConfig,
"/cfg/config.yml": sampleConfig,
}),
ConfigPath: "/cfg/config.yml",
}
@@ -67,11 +67,11 @@ func TestResolveTokenOrdering(t *testing.T) {
}
})
t.Run("tea config used when flag and env absent", func(t *testing.T) {
t.Run("config used when flag and env absent", func(t *testing.T) {
opts := base
tok, src, err := ResolveToken(opts)
if err != nil || tok != "site-token" || src != SourceTeaConfig {
t.Fatalf("want site-token/tea-config, got %q/%v (err %v)", tok, src, err)
if err != nil || tok != "site-token" || src != SourceConfig {
t.Fatalf("want site-token/config, got %q/%v (err %v)", tok, src, err)
}
})
@@ -88,8 +88,8 @@ func TestResolveTokenOrdering(t *testing.T) {
opts := base
opts.Env = fakeEnv(map[string]string{"TEAI_TOKEN": " "})
tok, src, err := ResolveToken(opts)
if err != nil || tok != "site-token" || src != SourceTeaConfig {
t.Fatalf("want site-token/tea-config, got %q/%v (err %v)", tok, src, err)
if err != nil || tok != "site-token" || src != SourceConfig {
t.Fatalf("want site-token/config, got %q/%v (err %v)", tok, src, err)
}
})
@@ -103,36 +103,54 @@ func TestResolveTokenOrdering(t *testing.T) {
}
})
t.Run("TEA_CONFIG points config path", func(t *testing.T) {
t.Run("TEAI_CONFIG points config path", func(t *testing.T) {
opts := base
opts.ConfigPath = "" // 不用 --config,改用 TEA_CONFIG
opts.ConfigPath = "" // 不用 --config,改用 TEAI_CONFIG
opts.Env = fakeEnv(map[string]string{
"TEA_CONFIG": "/cfg/config.yml",
"TEAI_CONFIG": "/cfg/config.yml",
})
tok, src, err := ResolveToken(opts)
if err != nil || tok != "site-token" || src != SourceTeaConfig {
t.Fatalf("want site-token/tea-config via TEA_CONFIG, got %q/%v (err %v)", tok, src, err)
if err != nil || tok != "site-token" || src != SourceConfig {
t.Fatalf("want site-token/config via TEAI_CONFIG, got %q/%v (err %v)", tok, src, err)
}
})
t.Run("explicit --config beats TEA_CONFIG", func(t *testing.T) {
t.Run("explicit --config beats TEAI_CONFIG", func(t *testing.T) {
opts := base // ConfigPath = /cfg/config.yml(有 site-token)
opts.Env = fakeEnv(map[string]string{
"TEA_CONFIG": "/cfg/other.yml",
"TEAI_CONFIG": "/cfg/other.yml",
})
read := fakeReadFile(map[string]string{
"/cfg/config.yml": sampleTeaConfig,
"/cfg/config.yml": sampleConfig,
"/cfg/other.yml": "logins:\n - name: x\n url: https://gitea.alterminal.com\n token: other\n default: true\n",
})
opts.ReadFile = read
tok, src, err := ResolveToken(opts)
if err != nil || tok != "site-token" || src != SourceTeaConfig {
if err != nil || tok != "site-token" || src != SourceConfig {
t.Fatalf("want site-token from explicit config, got %q/%v (err %v)", tok, src, err)
}
})
t.Run("TEA_CONFIG is ignored", func(t *testing.T) {
opts := AuthOptions{
SiteURL: "https://gitea.alterminal.com",
Env: fakeEnv(map[string]string{
"TEA_CONFIG": "/cfg/config.yml",
}),
ReadFile: fakeReadFile(map[string]string{
"/cfg/config.yml": sampleConfig,
"/home/.config/teai/config.yml": "logins:\n - name: x\n url: https://gitea.alterminal.com\n token: teai-token\n default: true\n",
}),
Home: "/home",
}
tok, src, err := ResolveToken(opts)
if err != nil || tok != "teai-token" || src != SourceConfig {
t.Fatalf("want teai-token from teai config, got %q/%v (err %v)", tok, src, err)
}
})
}
func TestParseTeaConfigPicksMatchingLogin(t *testing.T) {
func TestParseConfigPicksMatchingLogin(t *testing.T) {
t.Run("host match preferred over default", func(t *testing.T) {
cfg := `logins:
- name: other
@@ -144,7 +162,7 @@ func TestParseTeaConfigPicksMatchingLogin(t *testing.T) {
token: right-token
default: false
`
login, err := parseTeaConfig([]byte(cfg), "https://gitea.alterminal.com")
login, err := parseConfig([]byte(cfg), "https://gitea.alterminal.com")
if err != nil {
t.Fatalf("parse error: %v", err)
}
@@ -155,7 +173,7 @@ func TestParseTeaConfigPicksMatchingLogin(t *testing.T) {
t.Run("quoted token unquoted", func(t *testing.T) {
cfg := "logins:\n - name: t\n url: https://gitea.alterminal.com\n token: \"quoted-token\"\n default: true\n"
login, err := parseTeaConfig([]byte(cfg), "https://gitea.alterminal.com")
login, err := parseConfig([]byte(cfg), "https://gitea.alterminal.com")
if err != nil {
t.Fatalf("parse error: %v", err)
}
@@ -166,14 +184,14 @@ func TestParseTeaConfigPicksMatchingLogin(t *testing.T) {
t.Run("malformed login entry errors", func(t *testing.T) {
cfg := "logins:\n - name: t\n url: https://gitea.alterminal.com\n oops\n"
if _, err := parseTeaConfig([]byte(cfg), ""); err == nil {
if _, err := parseConfig([]byte(cfg), ""); err == nil {
t.Fatal("malformed line should error")
}
})
t.Run("no logins yields empty", func(t *testing.T) {
cfg := "preferences:\n editor: false\n"
login, err := parseTeaConfig([]byte(cfg), "")
login, err := parseConfig([]byte(cfg), "")
if err != nil || login.Token != "" {
t.Fatalf("want empty login, got %+v (err %v)", login, err)
}