Files
Agent-System-Backend/pkg/extension/project_audit_test.go
T
agent-exec-transport 97f60c1df6
governance / governance (pull_request) Successful in 15s
ci / vet-and-test (pull_request) Successful in 1m20s
agent-exec transport f461d74086dd6a80db72f83748f34b04/128fee1609ad
2026-10-22 03:41:15 +00:00

100 lines
4.4 KiB
Go

package extension
// W9 X2 读取面补完(2.29.0-experimental)的公开面契约:
// 1. 新增项全部是可选字段/新导出类型:既有 ProjectAuditHost、ProjectAuditRegistry
// 提供者与既有查询字面量照旧编译、照旧语义(不破坏旧 provider);
// 2. 新字段序列化带 omitempty:未使用的读取请求与旧消费者的线格式不变;
// 3. 正文投影与导出声明是显式契约字段,未开启时不下发。
import (
"context"
"encoding/json"
"errors"
"strings"
"testing"
core "agent-system-core"
)
// legacyAuditHost 是 2.14.0 形态的消费者:只实现既有方法,不感知新增可选字段。
type legacyAuditHost struct{}
func (legacyAuditHost) QueryProjectAudit(context.Context, ProjectAuditQuery) (ProjectAuditPage, error) {
return ProjectAuditPage{}, nil
}
// legacyAuditRegistry 是 2.14.0 形态的注册方实现。
type legacyAuditRegistry struct{}
func (legacyAuditRegistry) RegisterProjectAuditAuthorizer(ProjectAuditAuthorizer) error { return nil }
var (
_ ProjectAuditHost = legacyAuditHost{}
_ ProjectAuditRegistry = legacyAuditRegistry{}
)
func TestProjectAuditReadContractIsAdditiveAndOptional(t *testing.T) {
if SurfaceVersion < "2.29.0" {
t.Fatalf("SurfaceVersion must record the audit read completion, got %s", SurfaceVersion)
}
// 既有查询字面量(只填项目与游标)仍然可用,新增字段全部零值。
legacy := ProjectAuditQuery{ProjectID: core.ID("p1"), Limit: 10, BeforeOffset: 4, CutoffOffset: 9}
if legacy.IncludeBody || legacy.Export || legacy.LinkedResourceID != "" || legacy.LinkedRunID != "" {
t.Fatalf("optional fields defaulted to non-zero: %+v", legacy)
}
raw, err := json.Marshal(legacy)
if err != nil {
t.Fatal(err)
}
for _, unwanted := range []string{"includeBody", "export", "linkedResourceKind", "linkedResourceId", "linkedRunId"} {
if strings.Contains(string(raw), unwanted) {
t.Fatalf("omitted optional field %s reached the wire: %s", unwanted, raw)
}
}
for _, want := range []string{`"projectId":"p1"`, `"beforeOffset":4`, `"cutoffOffset":9`, `"limit":10`} {
if !strings.Contains(string(raw), want) {
t.Fatalf("existing field %s missing from the wire form: %s", want, raw)
}
}
// 显式请求时字段可见,消费端能区分“正文/导出请求”与普通分页。
full, err := json.Marshal(ProjectAuditQuery{ProjectID: core.ID("p1"), IncludeBody: true, Export: true,
LinkedResourceKind: "document", LinkedResourceID: "doc-1", LinkedRunID: "run-1"})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{`"includeBody":true`, `"export":true`, `"linkedResourceKind":"document"`,
`"linkedResourceId":"doc-1"`, `"linkedRunId":"run-1"`} {
if !strings.Contains(string(full), want) {
t.Fatalf("requested field %s missing: %s", want, full)
}
}
// 未授予正文时回执不带 resource 字段;授予后才出现对象身份(不含正文载荷)。
metadataOnly, err := json.Marshal(ProjectAuditPage{Receipts: []HostAuditReceipt{{SourceOffset: 3, CommandID: "rdm.confirm"}}})
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(metadataOnly), `"resource"`) {
t.Fatalf("metadata-only receipt carried a resource: %s", metadataOnly)
}
withBody, err := json.Marshal(ProjectAuditPage{Receipts: []HostAuditReceipt{{SourceOffset: 3, CommandID: "rdm.confirm",
Resource: &ProjectAuditReceiptResource{Kind: "document", ID: "doc-1", RunID: "run-1"}}},
Coverage: ProjectAuditCoverage{ResourceBodies: "verified-exact-link-body", Export: "unsupported"}})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{`"resource":{"kind":"document","id":"doc-1","runId":"run-1"}`, `"export":"unsupported"`} {
if !strings.Contains(string(withBody), want) {
t.Fatalf("body-permitted projection missing %s: %s", want, withBody)
}
}
// 四个审计哨兵互不相等:unsupported(缺基座能力)与 denied(策略拒绝)
// 不得互相冒充。
if !errors.Is(ErrProjectAuditUnsupported, ErrProjectAuditUnsupported) {
t.Fatal("unsupported sentinel must stay comparable with errors.Is")
}
for _, distinct := range []error{ErrProjectAuditDenied, ErrProjectAuditLinkConflict, ErrProjectAuditPending} {
if errors.Is(ErrProjectAuditUnsupported, distinct) || errors.Is(distinct, ErrProjectAuditUnsupported) {
t.Fatalf("audit sentinels must stay distinct: %v vs %v", distinct, ErrProjectAuditUnsupported)
}
}
}