8febe12d7d
真实本机 PG/ES/MinIO:v071 目录镜像 10 模式、结构引用/游标 15 模式、 8 表×2 外部范围身份矩阵、13 模式自然键(含软删除与合法同 parent 正例)、 备份期间三注入、收尾故障与授权撤销两个真实治理 PG 用例。 需隔离服务的用例在 test-scope.json 显式声明:普通 CI 跳过不算通过。
103 lines
3.9 KiB
Go
103 lines
3.9 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"knowledge-governance/internal/governance"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEmptyTargetOverviewAndEnqueueAgree(t *testing.T) {
|
|
for _, failed := range []bool{false, true} {
|
|
root := libraryFixtureRoot(t)
|
|
target := sealSharedVersions(t, root, 1, 4)[0]
|
|
digest, err := librarySyncParamsDigest("internal", target)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now().UTC()
|
|
store := &fakeLibraryStore{versions: []governance.LibraryVersion{{ID: "seal", Scope: "internal", Kind: governance.LibraryVersionTestSealed, SnapshotID: target, CreatedAt: now}}}
|
|
store.enqueueEchoRequestedID = true
|
|
store.grant = &governance.LibraryReleaseAuthorization{ID: "grant", Scope: "internal", SnapshotID: target, ParamsDigest: digest, AuthorizedBy: "admin", ExpiresAt: now.Add(time.Hour), CreatedAt: now}
|
|
config := Config{Store: store, LibraryArchiveRoot: root, ProductionReleaseRequestsEnabled: true, ProductionBackupRestoreVerified: true, LibraryProductionDatabaseURL: "postgres://unused.invalid/test", LibraryProductionInternalKB: "kb", LibraryCapacityBudgetBytes: 1 << 40, LibraryScopeProbe: func(context.Context, string) (bool, map[string]int64, error) {
|
|
if failed {
|
|
return false, nil, errors.New("offline")
|
|
}
|
|
return true, nil, nil
|
|
}}
|
|
sourceID := bindSourceFixture(t, &config, store, &target)
|
|
rec := httptest.NewRecorder()
|
|
libraryOverviewHandler(rec, httptest.NewRequest(http.MethodGet, "/api/v1/library/overview?scope=internal", nil), config)
|
|
var overview libraryOverview
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &overview); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if overview.Gate.SyncAllowed == failed {
|
|
t.Fatalf("failed=%v gate=%+v", failed, overview.Gate)
|
|
}
|
|
rec = httptest.NewRecorder()
|
|
librarySyncEnqueue(rec, httptest.NewRequest(http.MethodPost, "/api/v1/library/syncs", strings.NewReader(sourceRequestBody(t, `{"scope":"internal","target_snapshot_id":"`+target+`"}`, sourceID))), config)
|
|
if failed {
|
|
if rec.Code != http.StatusServiceUnavailable || store.createN != 0 {
|
|
t.Fatalf("unreachable target enqueued: %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
} else if rec.Code != http.StatusAccepted || store.createN != 1 {
|
|
t.Fatalf("empty target rejected: %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLibraryOverviewProbesUnknownTarget(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
empty bool
|
|
err error
|
|
want string
|
|
}{
|
|
{name: "empty", empty: true},
|
|
{name: "nonempty", want: "目标非空"},
|
|
{name: "unreachable", err: errors.New("connection unavailable"), want: "无法读取目标整库状态"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
calls := 0
|
|
config := Config{Store: &fakeLibraryStore{}, LibraryArchiveRoot: t.TempDir(), LibraryScopeProbe: func(ctx context.Context, scope string) (bool, map[string]int64, error) {
|
|
calls++
|
|
if scope != "internal" {
|
|
t.Fatalf("scope=%s", scope)
|
|
}
|
|
return tc.empty, nil, tc.err
|
|
}}
|
|
recorder := httptest.NewRecorder()
|
|
libraryOverviewHandler(recorder, httptest.NewRequest(http.MethodGet, "/api/v1/library/overview?scope=internal", nil), config)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
var body libraryOverview
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("target probe calls=%d, want 1", calls)
|
|
}
|
|
if body.Production.EmptyBaseline != tc.empty {
|
|
t.Fatalf("production=%+v", body.Production)
|
|
}
|
|
reasons := strings.Join(body.Gate.Reasons, "|")
|
|
if tc.want != "" && !strings.Contains(reasons, tc.want) {
|
|
t.Fatalf("reasons=%s", reasons)
|
|
}
|
|
if (tc.empty || tc.err != nil) && strings.Contains(reasons, "目标非空") {
|
|
t.Fatalf("not proven nonempty: %s", reasons)
|
|
}
|
|
if body.Gate.SyncAllowed {
|
|
t.Fatal("empty target must not bypass authorization and write gates")
|
|
}
|
|
})
|
|
}
|
|
}
|