122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
package openplatform
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
)
|
|
|
|
func TestPortalDecodeDetail(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
want string
|
|
}{
|
|
{name: "empty", err: io.EOF, want: "请求内容不能为空"},
|
|
{name: "syntax", err: &json.SyntaxError{Offset: 2}, want: "请求内容不是有效的 JSON"},
|
|
{name: "type", err: &json.UnmarshalTypeError{Field: "validFrom"}, want: "字段 validFrom 的数据类型不正确"},
|
|
{name: "unknown", err: errors.New(`json: unknown field "appId"`), want: `请求包含未支持的字段 "appId"`},
|
|
{name: "other", err: errors.New("unexpected"), want: "请求内容无法解析"},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if got := portalDecodeDetail(test.err); got != test.want {
|
|
t.Fatalf("portalDecodeDetail() = %q, want %q", got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStandaloneServerServesPortalDocsAndCatalog(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
staticDir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(staticDir, "index.html"), []byte(`<div id="root"></div>`), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
handler := NewStandaloneServer(db, StandaloneConfig{
|
|
StaticDir: staticDir,
|
|
SessionTTL: time.Hour,
|
|
RequestTimeout: time.Second,
|
|
Release: "test-release",
|
|
})
|
|
|
|
tests := []struct {
|
|
path string
|
|
content string
|
|
contentType string
|
|
}{
|
|
{"/", `<div id="root"></div>`, "text/html"},
|
|
{"/healthz", `"service":"open-platform-api"`, "application/json"},
|
|
{"/portal-api/catalog", `"daily_hydrogen"`, "application/json"},
|
|
{"/open-api/openapi.yaml", "openapi: 3.0.3", "application/yaml"},
|
|
}
|
|
for _, item := range tests {
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, item.path, nil))
|
|
if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), item.content) {
|
|
t.Fatalf("path=%s status=%d body=%s", item.path, recorder.Code, recorder.Body.String())
|
|
}
|
|
if value := recorder.Header().Get("Content-Type"); !strings.Contains(value, item.contentType) {
|
|
t.Fatalf("path=%s content-type=%s", item.path, value)
|
|
}
|
|
if recorder.Header().Get("X-Frame-Options") != "DENY" {
|
|
t.Fatalf("path=%s missing security headers", item.path)
|
|
}
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestStandalonePortalSessionRequiresBearerToken(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
handler := NewStandaloneServer(db, StandaloneConfig{RequestTimeout: time.Second})
|
|
recorder := httptest.NewRecorder()
|
|
handler.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/portal-api/session", nil))
|
|
if recorder.Code != http.StatusUnauthorized || !strings.Contains(recorder.Body.String(), "UNAUTHORIZED") {
|
|
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestPortalCredentialValidation(t *testing.T) {
|
|
input := PortalUserInput{
|
|
Username: "partner.dev", DisplayName: "合作伙伴开发者",
|
|
Password: "StrongPass2026", Status: "enabled",
|
|
ValidFrom: "2026-07-01T00:00:00+08:00", ValidTo: "2027-07-01T00:00:00+08:00",
|
|
}
|
|
if _, _, err := validatePortalUserInput(&input, true); err != nil {
|
|
t.Fatalf("valid portal user rejected: %v", err)
|
|
}
|
|
if err := validatePortalPassword("weak-password"); err == nil {
|
|
t.Fatal("weak password should be rejected")
|
|
}
|
|
if !validPortalRole("owner") || !validPortalRole("developer") || !validPortalRole("viewer") || validPortalRole("admin") {
|
|
t.Fatal("unexpected portal role validation")
|
|
}
|
|
raw, hash, err := newPortalSessionToken()
|
|
if err != nil || len(raw) != 64 || hash == [32]byte{} {
|
|
t.Fatalf("invalid session token raw=%d err=%v", len(raw), err)
|
|
}
|
|
}
|