feat: complete remaining Go backend modules
This commit is contained in:
1 parent
cea2751dc5
commit
aef5a97165
145 files changed
+18376
-199
No files matched your search
@@ -11,7 +11,10 @@ import (
|
||||
"time"
|
||||
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/application"
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/httpapi"
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/identity"
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/jobs"
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/providers"
|
||||
)
|
||||
|
||||
func TestLocalApplicationServesFoundationHealthAndReadiness(t *testing.T) {
|
||||
@@ -72,6 +75,51 @@ func TestApplicationRejectsInvalidProductionDatabaseConfiguration(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProductionLocalBackendNeverGrantsAnonymousAdministrator(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"NODE_ENV": "production",
|
||||
"ZHINIAN_DATA_BACKEND": "local",
|
||||
"ZHINIAN_AUTH_DISABLED": "true",
|
||||
})})
|
||||
if err != nil {
|
||||
t.Fatalf("New() error = %v", err)
|
||||
}
|
||||
t.Cleanup(app.Close)
|
||||
|
||||
response := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/api/settings", nil))
|
||||
if response.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("status=%d body=%s, want 401", response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationDerivesNextCompatibleMethodMatrixBeforeAuthentication(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"NODE_ENV": "production",
|
||||
"ZHINIAN_DATA_BACKEND": "local",
|
||||
})})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(app.Close)
|
||||
|
||||
for _, test := range []struct {
|
||||
method, path, allow string
|
||||
status int
|
||||
}{
|
||||
{http.MethodOptions, "/api/admin/accounts", "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT", http.StatusNoContent},
|
||||
{http.MethodOptions, "/api/health", "GET, HEAD, OPTIONS", http.StatusNoContent},
|
||||
{http.MethodHead, "/api/health", "", http.StatusOK},
|
||||
{http.MethodPost, "/api/health", "", http.StatusMethodNotAllowed},
|
||||
} {
|
||||
response := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(response, httptest.NewRequest(test.method, test.path, nil))
|
||||
if response.Code != test.status || response.Body.Len() != 0 || response.Header().Get("Allow") != test.allow {
|
||||
t.Fatalf("%s %s status=%d allow=%q body=%q", test.method, test.path, response.Code, response.Header().Get("Allow"), response.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationServesAnonymousCurrentSessionWithAuthConfigurationState(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -220,7 +268,7 @@ func TestApplicationComposesSignedCookieResolverWithAuthorizationLoader(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationUsesDatabaseAuthorizationAdapterByDefault(t *testing.T) {
|
||||
func TestLocalApplicationUsesCoherentAuthorizationAdapterByDefault(t *testing.T) {
|
||||
secret := "local-default-adapter-secret-with-enough-entropy"
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"ZHINIAN_DATA_BACKEND": "local",
|
||||
@@ -235,7 +283,7 @@ func TestApplicationUsesDatabaseAuthorizationAdapterByDefault(t *testing.T) {
|
||||
Version: 1, AuthMode: identity.AuthModeUser,
|
||||
IssuedAt: time.Now().Add(-time.Minute).Unix(), ExpiresAt: time.Now().Add(time.Hour).Unix(),
|
||||
User: identity.User{
|
||||
ID: "user-1", Subject: "user-1", DisplayName: "User", ClientID: "platform",
|
||||
ID: "demo-merchant", Subject: "demo-merchant", DisplayName: "Forged", ClientID: "platform",
|
||||
Authorities: []string{}, Scope: []string{},
|
||||
},
|
||||
}
|
||||
@@ -253,8 +301,121 @@ func TestApplicationUsesDatabaseAuthorizationAdapterByDefault(t *testing.T) {
|
||||
|
||||
app.Handler().ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusInternalServerError || response.Body.Len() != 0 {
|
||||
t.Fatalf("response = %d %q, want empty 500 from unavailable local authorization adapter", response.Code, response.Body.String())
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("response = %d %q, want authenticated local session", response.Code, response.Body.String())
|
||||
}
|
||||
var payload struct {
|
||||
Authenticated bool `json:"authenticated"`
|
||||
User struct {
|
||||
ID, DisplayName, Role, OrganizationID string
|
||||
} `json:"user"`
|
||||
}
|
||||
if err := json.NewDecoder(response.Body).Decode(&payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !payload.Authenticated || payload.User.ID != "demo-merchant" || payload.User.DisplayName != "智念用户" || payload.User.Role != "super_admin" || payload.User.OrganizationID != "org-demo" {
|
||||
t.Fatalf("payload = %#v", payload)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalApplicationBusinessModulesDoNotUseUnavailablePostgresShell(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"ZHINIAN_DATA_BACKEND": "local",
|
||||
"ZHINIAN_BILLING_REQUIRED": "0",
|
||||
})})
|
||||
if err != nil {
|
||||
t.Fatalf("New() error = %v", err)
|
||||
}
|
||||
t.Cleanup(app.Close)
|
||||
|
||||
for _, test := range []struct {
|
||||
method, path, body string
|
||||
want int
|
||||
}{
|
||||
{http.MethodGet, "/api/assets", "", http.StatusOK},
|
||||
{http.MethodGet, "/api/image-templates", "", http.StatusOK},
|
||||
{http.MethodGet, "/api/usage", "", http.StatusOK},
|
||||
{http.MethodPost, "/api/generations/image", `{"prompt":"local mock"}`, http.StatusAccepted},
|
||||
} {
|
||||
req := httptest.NewRequest(test.method, test.path, strings.NewReader(test.body))
|
||||
if test.body != "" {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
res := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(res, req)
|
||||
if res.Code != test.want {
|
||||
t.Fatalf("%s %s = %d %q, want %d", test.method, test.path, res.Code, res.Body.String(), test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalApplicationMockJobReachesSucceededWithStoredOutput(t *testing.T) {
|
||||
app, err := application.New(application.Options{
|
||||
Getenv: applicationEnv(map[string]string{
|
||||
"ZHINIAN_DATA_BACKEND": "local", "ZHINIAN_BILLING_REQUIRED": "0",
|
||||
"ZHINIAN_INTERNAL_WORKER_TOKEN": "worker-secret",
|
||||
"ZHINIAN_WORKER_POLL_INTERVAL_MS": "1",
|
||||
}),
|
||||
ProviderRegistry: jobs.ProviderRegistry{
|
||||
"volcengine-visual": providers.NewMock("local-e2e"),
|
||||
"evolink": providers.NewMock("local-e2e"),
|
||||
"bailian": providers.NewMock("local-e2e"),
|
||||
"seedance": providers.NewMock("local-e2e"),
|
||||
"mock": providers.NewMock("local-e2e"),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("New() error = %v", err)
|
||||
}
|
||||
t.Cleanup(app.Close)
|
||||
|
||||
create := httptest.NewRequest(http.MethodPost, "/api/generations/image", strings.NewReader(`{"prompt":"local output"}`))
|
||||
create.Header.Set("Content-Type", "application/json")
|
||||
created := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(created, create)
|
||||
if created.Code != http.StatusAccepted {
|
||||
t.Fatalf("create = %d %q", created.Code, created.Body.String())
|
||||
}
|
||||
var creation struct {
|
||||
Job jobs.Job `json:"job"`
|
||||
}
|
||||
if err := json.NewDecoder(created.Body).Decode(&creation); err != nil || creation.Job.ID == "" {
|
||||
t.Fatalf("creation = %#v, %v", creation, err)
|
||||
}
|
||||
|
||||
for tick := 0; tick < 2; tick++ {
|
||||
if tick > 0 {
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/internal/worker/tick", strings.NewReader(`{}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set("Authorization", "Bearer worker-secret")
|
||||
response := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(response, request)
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("tick %d = %d %q", tick, response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
got := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(got, httptest.NewRequest(http.MethodGet, "/api/generations/image/"+creation.Job.ID, nil))
|
||||
if got.Code != http.StatusOK {
|
||||
t.Fatalf("get = %d %q", got.Code, got.Body.String())
|
||||
}
|
||||
var result struct {
|
||||
Job jobs.Job `json:"job"`
|
||||
}
|
||||
if err := json.NewDecoder(got.Body).Decode(&result); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Job.Status != jobs.StatusSucceeded || len(result.Job.OutputAssetIDs) == 0 {
|
||||
t.Fatalf("job = %#v", result.Job)
|
||||
}
|
||||
|
||||
assetsResponse := httptest.NewRecorder()
|
||||
app.Handler().ServeHTTP(assetsResponse, httptest.NewRequest(http.MethodGet, "/api/assets", nil))
|
||||
if assetsResponse.Code != http.StatusOK || !strings.Contains(assetsResponse.Body.String(), result.Job.OutputAssetIDs[0]) {
|
||||
t.Fatalf("assets = %d %q", assetsResponse.Code, assetsResponse.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +510,34 @@ func TestApplicationLeavesLogoutAvailableWhenPasswordAuthenticationIsUnconfigure
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplicationOwnsEveryCheckedInHTTPRoute(t *testing.T) {
|
||||
app, err := application.New(application.Options{Getenv: applicationEnv(map[string]string{
|
||||
"ZHINIAN_DATA_BACKEND": "local",
|
||||
"NODE_ENV": "production",
|
||||
})})
|
||||
if err != nil {
|
||||
t.Fatalf("New() error = %v", err)
|
||||
}
|
||||
t.Cleanup(app.Close)
|
||||
|
||||
for _, route := range httpapi.GoRouteSurface() {
|
||||
route := route
|
||||
t.Run(route.Method+" "+route.Path, func(t *testing.T) {
|
||||
path := strings.ReplaceAll(route.Path, "{id}", "contract-id")
|
||||
path = strings.ReplaceAll(path, "{path...}", "contract/file.png")
|
||||
request := httptest.NewRequest(route.Method, "https://app.example.test"+path, strings.NewReader(`{}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
app.Handler().ServeHTTP(response, request)
|
||||
|
||||
if response.Code == http.StatusNotFound {
|
||||
t.Fatalf("route %s %s fell through to 404", route.Method, path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type applicationAuthorizationLoader struct {
|
||||
snapshot identity.AuthorizationSnapshot
|
||||
found bool
|
||||
|
||||
Reference in new issue
Block a user