162 lines
6.9 KiB
Go
162 lines
6.9 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/identity"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/prompt"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/publicapi"
|
|
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/templates"
|
|
)
|
|
|
|
type miscResolver struct {
|
|
session identity.Session
|
|
err error
|
|
}
|
|
|
|
func (resolver miscResolver) Resolve(context.Context, string) (identity.Session, error) {
|
|
return resolver.session, resolver.err
|
|
}
|
|
|
|
type miscTemplateCatalog struct{ items []templates.Template }
|
|
|
|
func (c *miscTemplateCatalog) ListTemplates(context.Context, string) ([]templates.Template, error) {
|
|
return c.items, nil
|
|
}
|
|
func (c *miscTemplateCatalog) CreateTemplate(_ context.Context, item templates.Template) (templates.Template, error) {
|
|
c.items = append(c.items, item)
|
|
return item, nil
|
|
}
|
|
func (c *miscTemplateCatalog) UpdateTemplate(context.Context, string, string, templates.Patch, time.Time) (templates.Template, bool, error) {
|
|
return templates.Template{}, false, nil
|
|
}
|
|
func (c *miscTemplateCatalog) DeleteTemplate(context.Context, string, string) (templates.Template, bool, error) {
|
|
return templates.Template{}, false, nil
|
|
}
|
|
|
|
func TestMiscTemplateAndPromptRoutes(t *testing.T) {
|
|
version := 1
|
|
session := identity.Session{Version: 1, AuthMode: identity.AuthModeUser, SessionVersion: &version, User: identity.User{ID: "u1", ClientID: "platform", Role: "user"}}
|
|
authorizer, _ := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, miscResolver{session: session})
|
|
catalog := &miscTemplateCatalog{}
|
|
service := templates.NewService(catalog, func() time.Time { return time.Date(2026, 8, 13, 0, 0, 0, 0, time.UTC) }, func() string { return "t1" })
|
|
handler, err := NewMiscHandler(MiscDependencies{Platform: authorizer, Templates: service, PromptAssembler: prompt.Assemble})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
post := httptest.NewRequest(http.MethodPost, "/api/image-templates", bytes.NewBufferString(`{"name":" N ","prompt":" P "}`))
|
|
post.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"})
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, post)
|
|
if w.Code != http.StatusCreated {
|
|
t.Fatalf("create status=%d body=%s", w.Code, w.Body.String())
|
|
}
|
|
get := httptest.NewRequest(http.MethodGet, "/api/image-templates", nil)
|
|
get.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"})
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, get)
|
|
if w.Code != 200 || !bytes.Contains(w.Body.Bytes(), []byte(`"templates"`)) {
|
|
t.Fatalf("list status=%d body=%s", w.Code, w.Body.String())
|
|
}
|
|
assemble := httptest.NewRequest(http.MethodPost, "/api/prompt/assemble", bytes.NewBufferString(`{"mode":"image","manualPrompt":"@图片1","materials":[]}`))
|
|
assemble.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"})
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, assemble)
|
|
if w.Code != 200 || !bytes.Contains(w.Body.Bytes(), []byte(`"requirements":{"image":1`)) {
|
|
t.Fatalf("prompt status=%d body=%s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
type settingsStub struct {
|
|
read any
|
|
saved map[string]any
|
|
err error
|
|
}
|
|
|
|
func (s *settingsStub) Get(context.Context) (any, error) { return s.read, s.err }
|
|
func (s *settingsStub) Save(_ context.Context, v map[string]any) (any, error) {
|
|
s.saved = v
|
|
return s.read, s.err
|
|
}
|
|
|
|
type logsStub struct {
|
|
entries any
|
|
cleared bool
|
|
err error
|
|
}
|
|
|
|
func (s *logsStub) List(context.Context, LogFilters) (any, error) { return s.entries, s.err }
|
|
func (s *logsStub) Clear(context.Context) error { s.cleared = true; return s.err }
|
|
|
|
func TestMiscSettingsLogsCapabilitiesAndOpenAPI(t *testing.T) {
|
|
session := identity.Session{AuthMode: identity.AuthModeAdmin, User: identity.User{ID: "root", ClientID: "platform", Role: "super_admin"}}
|
|
authorizer, _ := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, miscResolver{session: session})
|
|
settings := &settingsStub{read: map[string]any{"groups": []any{}}}
|
|
logs := &logsStub{entries: []any{map[string]any{"id": "l1"}}}
|
|
public := publicapi.NewAuthenticator(publicapi.Config{APIKeys: "client:key"})
|
|
handler, err := NewMiscHandler(MiscDependencies{Platform: authorizer, Settings: settings, Logs: logs, Public: public, Capabilities: func(context.Context) (any, error) { return []any{map[string]any{"id": "image.generate"}}, nil }})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, path := range []string{"/api/settings", "/api/logs"} {
|
|
request := httptest.NewRequest(http.MethodGet, path, nil)
|
|
request.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"})
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, request)
|
|
if w.Code != 200 {
|
|
t.Fatalf("%s status %d %s", path, w.Code, w.Body.String())
|
|
}
|
|
}
|
|
capRequest := httptest.NewRequest(http.MethodGet, "/api/v1/capabilities", nil)
|
|
capRequest.Header.Set("authorization", "Bearer key")
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, capRequest)
|
|
if w.Code != 200 {
|
|
t.Fatalf("capabilities %d %s", w.Code, w.Body.String())
|
|
}
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "https://app.example.test/api/v1/openapi.json", nil))
|
|
if w.Code != 200 || !bytes.Contains(w.Body.Bytes(), []byte(`"openapi":"3.1.0"`)) || !bytes.Contains(w.Body.Bytes(), []byte(`https://app.example.test`)) {
|
|
t.Fatalf("openapi %d %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestMiscOpenAPIUsesConfiguredPublicOrigin(t *testing.T) {
|
|
authorizer, _ := NewPlatformAuthorizer(AuthState{}, nil)
|
|
handler, err := NewMiscHandler(MiscDependencies{Platform: authorizer, PublicOrigin: "https://public.example.test/base/"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "http://internal:3000/api/v1/openapi.json", nil))
|
|
if response.Code != http.StatusOK || !bytes.Contains(response.Body.Bytes(), []byte(`"url":"https://public.example.test"`)) {
|
|
t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestMiscFailsClosedAndHidesInfrastructureErrors(t *testing.T) {
|
|
authorizer, _ := NewPlatformAuthorizer(AuthState{Required: true, Configured: true}, miscResolver{err: errors.New("db password secret")})
|
|
handler, _ := NewMiscHandler(MiscDependencies{Platform: authorizer, PromptAssembler: prompt.Assemble})
|
|
request := httptest.NewRequest(http.MethodPost, "/api/prompt/assemble", bytes.NewBufferString(`{"mode":"image"}`))
|
|
request.AddCookie(&http.Cookie{Name: identity.SessionCookieName, Value: "cookie"})
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, request)
|
|
if w.Code != 500 || bytes.Contains(w.Body.Bytes(), []byte("password")) {
|
|
t.Fatalf("status=%d body=%s", w.Code, w.Body.String())
|
|
}
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/api/settings", nil))
|
|
var body map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &body)
|
|
if w.Code != 401 {
|
|
t.Fatalf("status=%d body=%#v", w.Code, body)
|
|
}
|
|
}
|