DB-backed repo_path_allowlist reader (deny-all until admin seeds), per-prefix admin test_commands (goose 00075), execution evidence table (goose 00074), isolated git-worktree patch executor with command allowlist/time/output caps and failure-stops evidence; controlled_patch creation/preview unlocked only with an allowlisted repo binding (projectworkflow gate + catalog flag); creator-only server endpoints for bindings/executions; merge/push/deploy stay behind a typed separate-authorization boundary. Module boundary: app->swboot composition import registered.
125 lines
4.6 KiB
Go
125 lines
4.6 KiB
Go
package swboot
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestProjectTypesCatalog(t *testing.T) {
|
|
items := ProjectTypesCatalog()
|
|
if len(items) != 4 {
|
|
t.Fatalf("expected 4 project types, got %d", len(items))
|
|
}
|
|
for _, it := range items {
|
|
if it.ProductDomain != DomainNonMedicalSoftware {
|
|
t.Fatalf("project type %s polluted domain %q", it.Key, it.ProductDomain)
|
|
}
|
|
if it.DefaultWorkflowKey != "software_bootstrap" {
|
|
t.Fatalf("expected software_bootstrap default for %s", it.Key)
|
|
}
|
|
blob := strings.ToLower(it.Name + " " + it.Description)
|
|
for _, bad := range []string{"临床", "nmpa", "cdmo"} {
|
|
if strings.Contains(blob, bad) {
|
|
t.Fatalf("medical term in project type %s", it.Key)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildCreationPreview_SelfBootstrapSolo(t *testing.T) {
|
|
res := BuildCreationPreview(CreationPreviewInput{
|
|
Name: "Agent-System 自举", ProductDomain: DomainNonMedicalSoftware,
|
|
ProjectTypeKey: ProjectTypeExistingSelfBootstrap,
|
|
ProblemStatement: "建立可信路线", TargetUsers: "内部团队", TargetOutcome: "可执行发布路线",
|
|
GovernanceProfile: GovernanceStandard, TeamMode: TeamModeSolo,
|
|
ComplianceProfileKey: "internal_control+security_baseline",
|
|
ExecutionMode: ExecutionDocsOnly,
|
|
}, time.Date(2026, 9, 16, 12, 0, 0, 0, time.UTC))
|
|
if !res.Valid {
|
|
t.Fatalf("expected valid preview, issues=%v", res.Issues)
|
|
}
|
|
if res.ApprovalPolicy != ApprovalCreatorAcknowledgement {
|
|
t.Fatalf("expected creator acknowledgement, got %s", res.ApprovalPolicy)
|
|
}
|
|
if res.RecommendedWorkflow != "software_bootstrap" {
|
|
t.Fatalf("expected software_bootstrap, got %s", res.RecommendedWorkflow)
|
|
}
|
|
if res.CreationToken == "" || res.PlanHash == "" {
|
|
t.Fatal("expected token and plan hash")
|
|
}
|
|
}
|
|
|
|
func TestBuildCreationPreview_RejectsControlledPatchAndSoloPrivacy(t *testing.T) {
|
|
res := BuildCreationPreview(CreationPreviewInput{
|
|
Name: "x", ProductDomain: DomainNonMedicalSoftware,
|
|
ProjectTypeKey: ProjectTypeGreenfield,
|
|
ProblemStatement: "p", TargetUsers: "u", TargetOutcome: "o",
|
|
TeamMode: TeamModeSolo, ComplianceProfileKey: "privacy",
|
|
ExecutionMode: ExecutionControlledPatch,
|
|
}, time.Now().UTC())
|
|
if res.Valid {
|
|
t.Fatal("expected invalid")
|
|
}
|
|
joined := strings.Join(res.Issues, ";")
|
|
if !strings.Contains(joined, "solo") {
|
|
t.Fatalf("expected solo/privacy issue, got %v", res.Issues)
|
|
}
|
|
if !strings.Contains(joined, "ERR_EXECUTION_MODE_DISABLED") {
|
|
t.Fatalf("expected controlled_patch disabled, got %v", res.Issues)
|
|
}
|
|
}
|
|
|
|
func TestRepoAllowlistStub(t *testing.T) {
|
|
al := NewRepoAllowlist([]string{"/home/weichen/projects/Agent-System"})
|
|
if err := al.CheckReadOnlyAccess("/home/weichen/projects/Agent-System/cmd"); err != nil {
|
|
t.Fatalf("expected allowlisted path ok: %v", err)
|
|
}
|
|
if err := al.CheckReadOnlyAccess("/etc/passwd"); !errors.Is(err, ErrRepoPathNotAllowlisted) {
|
|
t.Fatalf("expected not allowlisted, got %v", err)
|
|
}
|
|
if err := al.ExecuteControlledPatch("/home/weichen/projects/Agent-System", nil); !errors.Is(err, ErrPatchExecutionUnimplemented) {
|
|
t.Fatalf("expected stub rejection, got %v", err)
|
|
}
|
|
if err := ApproveMerge("feature/x"); !errors.Is(err, ErrMergeRequiresSeparateAuthorization) {
|
|
t.Fatalf("merge must stay behind separate authorization, got %v", err)
|
|
}
|
|
empty := DefaultRepoAllowlist()
|
|
if empty.IsAllowlisted("/home/weichen/projects/Agent-System") {
|
|
t.Fatal("empty allowlist must deny all")
|
|
}
|
|
}
|
|
|
|
func TestSuggestDeliveryAfterBootstrapNeverActivates(t *testing.T) {
|
|
s := SuggestDeliveryAfterBootstrap()
|
|
if s.WorkflowKey != "software_delivery" {
|
|
t.Fatalf("key=%s", s.WorkflowKey)
|
|
}
|
|
if s.AutoActivate {
|
|
t.Fatal("must not auto-activate delivery")
|
|
}
|
|
if s.Summary == "" {
|
|
t.Fatal("expected operator-facing summary")
|
|
}
|
|
}
|
|
|
|
// SWBOOT-10: controlled_patch preview is valid only with an admin-allowlisted
|
|
// repo binding; without the flag it stays the fail-closed rejection.
|
|
func TestPreview_ControlledPatchRequiresAllowlistedBinding(t *testing.T) {
|
|
mk := func(allowlisted bool, repo string) CreationPreviewInput {
|
|
return CreationPreviewInput{
|
|
Name: "受控补丁预览", ProductDomain: DomainNonMedicalSoftware,
|
|
ProjectTypeKey: "greenfield", ProblemStatement: "p", TargetUsers: "u", TargetOutcome: "o",
|
|
ExecutionMode: ExecutionControlledPatch, RepoAllowlisted: allowlisted, RepoPath: repo,
|
|
}
|
|
}
|
|
if res := BuildCreationPreview(mk(false, "/repo/x"), time.Now().UTC()); res.Valid {
|
|
t.Fatalf("controlled_patch without allowlisted binding must be invalid: %+v", res.Issues)
|
|
}
|
|
res := BuildCreationPreview(mk(true, "/allow/repo"), time.Now().UTC())
|
|
if !res.Valid {
|
|
t.Fatalf("controlled_patch with allowlisted binding must preview valid: %+v", res.Issues)
|
|
}
|
|
}
|