c7bad83496
Add WordPress as an enterprise-site CMS type alongside pbootcms, and let schedule tasks auto-publish generated articles to enterprise sites. - WordPress connection/publisher service and tests; register wordpress media platform and relax cms_type CHECK constraint - New publish_enterprise_site_targets JSONB column on schedule_tasks with array type constraint; thread targets through scheduler dispatch, prompt/KOL generation, and worker - Admin UI: enterprise-site targets in generate/schedule/publish flows, KOL package form, MediaView, and i18n strings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
530 lines
20 KiB
Go
530 lines
20 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/shared/auth"
|
|
"github.com/geo-platform/tenant-api/internal/shared/config"
|
|
"github.com/geo-platform/tenant-api/internal/shared/publicasset"
|
|
"github.com/sergi/go-diff/diffmatchpatch"
|
|
)
|
|
|
|
func TestUpdateEnterpriseSiteConnectionRequestTracksNullAndMissingFields(t *testing.T) {
|
|
var req UpdateEnterpriseSiteConnectionRequest
|
|
if err := json.Unmarshal([]byte(`{"name":"官网","default_scode":null,"secret":""}`), &req); err != nil {
|
|
t.Fatalf("unmarshal request: %v", err)
|
|
}
|
|
if !req.Name.Set || req.Name.Value == nil || *req.Name.Value != "官网" {
|
|
t.Fatalf("name patch = %#v, want set string", req.Name)
|
|
}
|
|
if !req.DefaultScode.Set || req.DefaultScode.Value != nil {
|
|
t.Fatalf("default_scode patch = %#v, want explicit null", req.DefaultScode)
|
|
}
|
|
if !req.Secret.Set || req.Secret.Value == nil || *req.Secret.Value != "" {
|
|
t.Fatalf("secret patch = %#v, want set empty string", req.Secret)
|
|
}
|
|
if req.Status.Set {
|
|
t.Fatalf("status patch = %#v, want missing", req.Status)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteUpdateRealDatabaseRepro(t *testing.T) {
|
|
dsn := os.Getenv("TEST_ENTERPRISE_SITE_UPDATE_DSN")
|
|
if dsn == "" {
|
|
t.Skip("set TEST_ENTERPRISE_SITE_UPDATE_DSN to run the real database repro")
|
|
}
|
|
ctx := auth.WithCurrentWorkspaceID(
|
|
auth.WithActor(context.Background(), auth.Actor{UserID: 1, TenantID: 1, PrimaryWorkspaceID: 1}),
|
|
1,
|
|
)
|
|
pool, err := pgxpool.New(ctx, dsn)
|
|
if err != nil {
|
|
t.Fatalf("connect database: %v", err)
|
|
}
|
|
defer pool.Close()
|
|
|
|
name := "海翔"
|
|
siteURL := "https://youjayou.com"
|
|
appID := "admin"
|
|
acode := "cn"
|
|
mcode := "2"
|
|
req := UpdateEnterpriseSiteConnectionRequest{
|
|
Name: EnterpriseSitePatchString{Set: true, Value: &name},
|
|
SiteURL: EnterpriseSitePatchString{Set: true, Value: &siteURL},
|
|
AppID: EnterpriseSitePatchString{Set: true, Value: &appID},
|
|
DefaultAcode: EnterpriseSitePatchString{Set: true, Value: &acode},
|
|
DefaultMcode: EnterpriseSitePatchString{Set: true, Value: &mcode},
|
|
DefaultScode: EnterpriseSitePatchString{Set: true, Value: nil},
|
|
}
|
|
if _, err := NewEnterpriseSiteService(pool, config.NewStaticProvider(&config.Config{})).Update(ctx, 1, req); err != nil {
|
|
t.Fatalf("update enterprise site: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateEnterpriseSiteCMSURLRequiresHTTPSForRemoteWordPress(t *testing.T) {
|
|
if err := validateEnterpriseSiteCMSURL("http://example.com", wordpressPlatformID); err == nil {
|
|
t.Fatalf("remote wordpress http url should be rejected")
|
|
}
|
|
if err := validateEnterpriseSiteCMSURL("http://127.0.0.1:6060", wordpressPlatformID); err != nil {
|
|
t.Fatalf("local wordpress http url should be allowed for development: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeEnterpriseSiteURLRejectsReservedTargets(t *testing.T) {
|
|
for _, raw := range []string{
|
|
"http://169.254.169.254",
|
|
"http://10.0.0.8",
|
|
"http://[fe80::1]",
|
|
} {
|
|
if _, err := normalizeEnterpriseSiteURL(raw); err == nil {
|
|
t.Fatalf("reserved site url %q should be rejected", raw)
|
|
}
|
|
}
|
|
if got, err := normalizeEnterpriseSiteURL("http://127.0.0.1:6060"); err != nil || got != "http://127.0.0.1:6060" {
|
|
t.Fatalf("local loopback url = %q, %v; want allowed", got, err)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteDescriptionUsesRenderedHTMLText(t *testing.T) {
|
|
got := enterpriseSiteDescription(
|
|
"<h2>2026年合肥全屋定制行业发展现状</h2><p>消费者需求升级。</p>",
|
|
"## 旧版标题\n旧版正文不应该优先进入摘要",
|
|
)
|
|
if strings.Contains(got, "##") {
|
|
t.Fatalf("description contains markdown heading marker: %q", got)
|
|
}
|
|
if strings.Contains(got, "旧版正文") {
|
|
t.Fatalf("description used markdown while html text was available: %q", got)
|
|
}
|
|
if !strings.Contains(got, "2026年合肥全屋定制行业发展现状") {
|
|
t.Fatalf("description = %q, want rendered html text", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteStripLeadingTitleHeadingHTMLRemovesDuplicateTitle(t *testing.T) {
|
|
got := enterpriseSiteStripLeadingTitleHeadingHTML(
|
|
"<h1>合肥全屋定制口碑怎么判断 看这里</h1><p>正文第一段。</p>",
|
|
"合肥全屋定制口碑怎么判断 看这里",
|
|
)
|
|
if strings.Contains(got, "<h1>") || strings.Contains(got, "合肥全屋定制口碑怎么判断 看这里") {
|
|
t.Fatalf("duplicate title heading was not removed: %q", got)
|
|
}
|
|
if !strings.Contains(got, "正文第一段") {
|
|
t.Fatalf("body content was lost: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteStripLeadingTitleHeadingHTMLKeepsDifferentHeading(t *testing.T) {
|
|
input := "<h2>行业现状</h2><p>正文第一段。</p>"
|
|
got := enterpriseSiteStripLeadingTitleHeadingHTML(input, "合肥全屋定制口碑怎么判断 看这里")
|
|
if got != input {
|
|
t.Fatalf("different leading heading changed: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteStripLeadingTitleHeadingMarkdownRemovesDuplicateTitle(t *testing.T) {
|
|
got := enterpriseSiteStripLeadingTitleHeadingMarkdown(
|
|
"# 合肥全屋定制口碑怎么判断 看这里\n\n正文第一段。",
|
|
"合肥全屋定制口碑怎么判断 看这里",
|
|
)
|
|
if strings.Contains(got, "合肥全屋定制口碑怎么判断 看这里") {
|
|
t.Fatalf("duplicate markdown title was not removed: %q", got)
|
|
}
|
|
if got != "正文第一段。" {
|
|
t.Fatalf("markdown body = %q, want body only", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteStripLeadingTitleHeadingMarkdownRemovesPlainTitle(t *testing.T) {
|
|
got := enterpriseSiteStripLeadingTitleHeadingMarkdown(
|
|
"装修预算不够?2026安徽6款高性价比家居整理\n\n近年安徽本地全屋定制需求持续上涨。",
|
|
"装修预算不够?2026安徽6款高性价比家居整理",
|
|
)
|
|
if strings.Contains(got, "装修预算不够?2026安徽6款高性价比家居整理") {
|
|
t.Fatalf("plain duplicate title was not removed: %q", got)
|
|
}
|
|
if !strings.HasPrefix(got, "近年安徽本地全屋定制需求持续上涨") {
|
|
t.Fatalf("markdown body = %q, want body only", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteDescriptionAfterTitleStripSkipsDuplicateTitle(t *testing.T) {
|
|
title := "合肥全屋定制口碑怎么判断 看这里"
|
|
html := enterpriseSiteStripLeadingTitleHeadingHTML("<h1>"+title+"</h1><p>正文第一段。</p>", title)
|
|
markdown := enterpriseSiteStripLeadingTitleHeadingMarkdown("# "+title+"\n\n正文第一段。", title)
|
|
got := enterpriseSiteDescription(html, markdown)
|
|
if strings.Contains(got, title) {
|
|
t.Fatalf("description still contains duplicate title: %q", got)
|
|
}
|
|
if !strings.Contains(got, "正文第一段") {
|
|
t.Fatalf("description = %q, want body text", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteDescriptionFallsBackToCleanMarkdown(t *testing.T) {
|
|
got := enterpriseSiteDescription("", "## 2026年合肥全屋定制行业发展现状\n- 消费者需求升级。")
|
|
if strings.Contains(got, "##") || strings.Contains(got, "- ") {
|
|
t.Fatalf("description was not cleaned: %q", got)
|
|
}
|
|
if !strings.Contains(got, "消费者需求升级") {
|
|
t.Fatalf("description = %q, want markdown text", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteNormalizeStoredContentRestoresDiffPatch(t *testing.T) {
|
|
source := "# 2026安徽全屋定制行业\n\n## 引言\n正文第一段。"
|
|
dmp := diffmatchpatch.New()
|
|
patch := dmp.PatchToText(dmp.PatchMake("", source))
|
|
|
|
got := enterpriseSiteNormalizeStoredContent(patch)
|
|
|
|
if got != source {
|
|
t.Fatalf("restored content = %q, want %q", got, source)
|
|
}
|
|
if strings.Contains(got, "@@ -") {
|
|
t.Fatalf("diff patch leaked into normalized content: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteNormalizeStoredContentRepairsMalformedEditorImage(t *testing.T) {
|
|
got := enterpriseSiteNormalizeStoredContent(
|
|
`<p class="article-editor-image article-editor-image--center" align="center" <img src="/api/public/assets/token" alt="" /></p>`,
|
|
)
|
|
if !strings.Contains(got, `align="center"><img`) {
|
|
t.Fatalf("malformed editor image was not repaired: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteHTMLContentPatchFallsBackToMarkdownRendering(t *testing.T) {
|
|
source := "# 2026安徽全屋定制行业\n\n## 引言\n正文第一段。"
|
|
dmp := diffmatchpatch.New()
|
|
htmlContent := enterpriseSiteNormalizeStoredContent(dmp.PatchToText(dmp.PatchMake("", source)))
|
|
markdownContent := ""
|
|
if htmlContent != "" && !enterpriseSiteContentLooksLikeHTML(htmlContent) {
|
|
markdownContent = htmlContent
|
|
htmlContent = ""
|
|
}
|
|
|
|
got, err := markdownToEnterpriseSiteHTML(markdownContent)
|
|
if err != nil {
|
|
t.Fatalf("render markdown: %v", err)
|
|
}
|
|
|
|
if strings.Contains(got, "@@ -") || strings.Contains(got, "%E5") {
|
|
t.Fatalf("rendered HTML still contains patch/url-encoded content: %q", got)
|
|
}
|
|
if !strings.Contains(got, "<h1>2026安徽全屋定制行业</h1>") || !strings.Contains(got, "<p>正文第一段。</p>") {
|
|
t.Fatalf("rendered HTML = %q, want UEditor-ready HTML", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteAbsoluteImageURLsRewritesRelativeAssetPaths(t *testing.T) {
|
|
got := enterpriseSiteAbsoluteImageURLs(
|
|
`<p><img src="/api/public/assets/token" data-src="/api/public/assets/token" /></p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
if strings.Count(got, "http://127.0.0.1:8080/api/public/assets/token") != 2 {
|
|
t.Fatalf("image urls were not absolutized: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteImageURLsEmbedsPublicAssetImages(t *testing.T) {
|
|
objectKey := "tenants/7/images/body.png"
|
|
token := publicasset.SignObjectKey(objectKey, "test-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "test-secret"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(
|
|
context.Background(),
|
|
7,
|
|
`<p><img src="/api/public/assets/`+token+`" data-src="/api/public/assets/`+token+`" /></p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if storage.gotKey != objectKey {
|
|
t.Fatalf("object key = %q, want %q", storage.gotKey, objectKey)
|
|
}
|
|
if strings.Count(got, `data:image/png;base64,`) != 2 {
|
|
t.Fatalf("image was not embedded in both image attrs: %s", got)
|
|
}
|
|
if strings.Contains(got, "/api/public/assets/") {
|
|
t.Fatalf("public asset url leaked into enterprise site payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteImageURLsEmbedsPublicAssetImagesWithStaleSignature(t *testing.T) {
|
|
objectKey := "tenants/7/images/body.png"
|
|
token := publicasset.SignObjectKey(objectKey, "old-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "new-secret"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(
|
|
context.Background(),
|
|
7,
|
|
`<p><img src="http://localhost:5178/api/public/assets/`+token+`" /></p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if storage.gotKey != objectKey {
|
|
t.Fatalf("object key = %q, want %q", storage.gotKey, objectKey)
|
|
}
|
|
if !strings.Contains(got, `src="data:image/png;base64,`) {
|
|
t.Fatalf("stale signed image was not embedded: %s", got)
|
|
}
|
|
if strings.Contains(got, "localhost:5178") || strings.Contains(got, "/api/public/assets/") {
|
|
t.Fatalf("saas asset url leaked into enterprise site payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteImageURLsStripsSaaSImageAttrs(t *testing.T) {
|
|
objectKey := "tenants/7/images/body.png"
|
|
token := publicasset.SignObjectKey(objectKey, "test-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "test-secret"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(
|
|
context.Background(),
|
|
7,
|
|
`<p><img src="/api/public/assets/`+token+`" alt="" data-asset-id="35" asset-id="35" /></p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if !strings.Contains(got, `src="data:image/png;base64,`) {
|
|
t.Fatalf("image was not embedded: %s", got)
|
|
}
|
|
if strings.Contains(got, "data-asset-id") || strings.Contains(got, "asset-id") {
|
|
t.Fatalf("saas image attrs leaked into enterprise site payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteImageURLsDropsUnavailableSaaSAssetImage(t *testing.T) {
|
|
objectKey := "tenants/7/images/deleted.webp"
|
|
token := publicasset.SignObjectKey(objectKey, "old-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "new-secret"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(
|
|
context.Background(),
|
|
7,
|
|
`<p>前文</p><p class="article-editor-image article-editor-image--center" align="center"><img src="http://localhost:5178/api/public/assets/`+token+`" alt="" /></p><p>后文</p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if strings.Contains(got, "localhost:5178") || strings.Contains(got, "/api/public/assets/") {
|
|
t.Fatalf("unavailable saas asset url leaked into enterprise site payload: %s", got)
|
|
}
|
|
if strings.Contains(got, "<img") || strings.Contains(got, "article-editor-image") {
|
|
t.Fatalf("unavailable image wrapper was not removed: %s", got)
|
|
}
|
|
if !strings.Contains(got, "前文") || !strings.Contains(got, "后文") {
|
|
t.Fatalf("surrounding content was lost: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteImageURLsEmbedsIncompletePublicAssetPaths(t *testing.T) {
|
|
objectKey := "tenants/7/articles/42/images/body.webp"
|
|
token := publicasset.SignObjectKey(objectKey, "test-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "test-secret"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(
|
|
context.Background(),
|
|
7,
|
|
`<p><img src="api/public/assets/`+token+`?format=png" /></p>`,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if storage.gotKey != objectKey {
|
|
t.Fatalf("object key = %q, want %q", storage.gotKey, objectKey)
|
|
}
|
|
if !strings.Contains(got, `src="data:image/png;base64,`) {
|
|
t.Fatalf("image was not embedded for incomplete asset path: %s", got)
|
|
}
|
|
if strings.Contains(got, "127.0.0.1") || strings.Contains(got, "api/public/assets") {
|
|
t.Fatalf("incomplete asset path leaked into enterprise site payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestMarkdownRenderedEnterpriseSiteImageEmbedsIncompletePublicAssetPath(t *testing.T) {
|
|
objectKey := "tenants/7/articles/42/images/body.webp"
|
|
token := publicasset.SignObjectKey(objectKey, "test-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "test-secret"},
|
|
})).WithObjectStorage(storage)
|
|
html, err := markdownToEnterpriseSiteHTML("正文\n\n")
|
|
if err != nil {
|
|
t.Fatalf("render markdown: %v", err)
|
|
}
|
|
|
|
got := svc.prepareEnterpriseSiteImageURLs(context.Background(), 7, html, "http://127.0.0.1:8080")
|
|
|
|
if storage.gotKey != objectKey {
|
|
t.Fatalf("object key = %q, want %q", storage.gotKey, objectKey)
|
|
}
|
|
if !strings.Contains(got, `src="data:image/png;base64,`) {
|
|
t.Fatalf("markdown image was not embedded for incomplete asset path: %s", got)
|
|
}
|
|
if strings.Contains(got, "127.0.0.1") || strings.Contains(got, "api/public/assets") {
|
|
t.Fatalf("markdown image asset path leaked into enterprise site payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSitePublishAssetsEmbedsIncompleteCoverAssetPath(t *testing.T) {
|
|
objectKey := "tenants/7/articles/42/images/cover.webp"
|
|
token := publicasset.SignObjectKey(objectKey, "test-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{content: testEnterpriseSitePNG()}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "test-secret"},
|
|
Server: config.ServerConfig{PublicBaseURL: "http://127.0.0.1:8080"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteCoverURL(
|
|
context.Background(),
|
|
7,
|
|
"api/public/assets/"+token+"?format=png",
|
|
nil,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if storage.gotKey != objectKey {
|
|
t.Fatalf("object key = %q, want %q", storage.gotKey, objectKey)
|
|
}
|
|
if !strings.HasPrefix(got, "data:image/png;base64,") {
|
|
t.Fatalf("cover was not embedded as data URI: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestPrepareEnterpriseSiteCoverURLDropsUnavailableSaaSAssetURL(t *testing.T) {
|
|
objectKey := "tenants/7/images/deleted.webp"
|
|
token := publicasset.SignObjectKey(objectKey, "old-secret")
|
|
storage := &enterpriseSiteImageObjectStorage{}
|
|
svc := NewEnterpriseSiteService(nil, config.NewStaticProvider(&config.Config{
|
|
JWT: config.JWTConfig{Secret: "new-secret"},
|
|
Server: config.ServerConfig{PublicBaseURL: "http://127.0.0.1:8080"},
|
|
})).WithObjectStorage(storage)
|
|
|
|
got := svc.prepareEnterpriseSiteCoverURL(
|
|
context.Background(),
|
|
7,
|
|
"http://localhost:5178/api/public/assets/"+token,
|
|
nil,
|
|
"http://127.0.0.1:8080",
|
|
)
|
|
|
|
if got != "" {
|
|
t.Fatalf("unavailable saas cover should not be sent to pbootcms, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteNormalizeLegacyPublishErrorState(t *testing.T) {
|
|
errorMessage := "图片下载失败"
|
|
item := EnterpriseSiteConnectionResponse{
|
|
Status: "error",
|
|
LastError: &errorMessage,
|
|
LatestRecord: &EnterpriseSiteLatestPublishRecord{
|
|
Status: "failed",
|
|
ErrorMessage: &errorMessage,
|
|
},
|
|
}
|
|
|
|
enterpriseSiteNormalizeLegacyPublishErrorState(&item)
|
|
|
|
if item.Status != "connected" {
|
|
t.Fatalf("status = %q, want connected", item.Status)
|
|
}
|
|
if item.LastError != nil {
|
|
t.Fatalf("last error = %q, want nil", *item.LastError)
|
|
}
|
|
}
|
|
|
|
func TestEnterpriseSiteNormalizeLegacyPublishErrorStateKeepsPingError(t *testing.T) {
|
|
lastError := "CMS request failed"
|
|
recordError := "图片下载失败"
|
|
item := EnterpriseSiteConnectionResponse{
|
|
Status: "error",
|
|
LastError: &lastError,
|
|
LatestRecord: &EnterpriseSiteLatestPublishRecord{
|
|
Status: "failed",
|
|
ErrorMessage: &recordError,
|
|
},
|
|
}
|
|
|
|
enterpriseSiteNormalizeLegacyPublishErrorState(&item)
|
|
|
|
if item.Status != "error" {
|
|
t.Fatalf("status = %q, want error", item.Status)
|
|
}
|
|
if item.LastError == nil || *item.LastError != lastError {
|
|
t.Fatalf("last error changed: %#v", item.LastError)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeEnterpriseSiteErrorTranslatesWordPressAuthFailure(t *testing.T) {
|
|
got := sanitizeEnterpriseSiteError(&wordpressRequestError{
|
|
message: "wordpress wp/v2/users/me returned http 401: rest_not_logged_in: 您目前没有登录。",
|
|
status: 401,
|
|
code: "rest_not_logged_in",
|
|
})
|
|
want := "WordPress 认证失败,请使用 WordPress 用户名和 Application Password"
|
|
if got != want {
|
|
t.Fatalf("sanitize error = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
type enterpriseSiteImageObjectStorage struct {
|
|
content []byte
|
|
gotKey string
|
|
}
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) Validate() error { return nil }
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) PutBytes(context.Context, string, []byte, string) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) GetBytes(_ context.Context, objectKey string) ([]byte, error) {
|
|
s.gotKey = objectKey
|
|
return s.content, nil
|
|
}
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) Exists(context.Context, string) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) Delete(context.Context, string) error { return nil }
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) PublicURL(string) (string, error) { return "", nil }
|
|
|
|
func (s *enterpriseSiteImageObjectStorage) DownloadURL(string) (string, error) { return "", nil }
|
|
|
|
func testEnterpriseSitePNG() []byte {
|
|
return []byte{
|
|
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
|
0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
|
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
|
|
0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4,
|
|
0x89, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41,
|
|
0x54, 0x78, 0x9c, 0x63, 0x00, 0x01, 0x00, 0x00,
|
|
0x05, 0x00, 0x01, 0x0d, 0x0a, 0x2d, 0xb4, 0x00,
|
|
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
|
|
0x42, 0x60, 0x82,
|
|
}
|
|
}
|