Implements the branch-protection OpenSpec change (#195). New operation/branchprotection/ package: - Tools: list/get/create/edit/delete_branch_protection over /repos/{owner}/{repo}/branch_protections. - list_branch_protections is output-bounded (page/limit; echoes page/limit for resumability). - create/edit round-trip status_check_contexts (comma-separated). edit uses PATCH pointer semantics (pkg/ptr.To): only caller-passed fields change; unpassed fields serialize as null (leave-unchanged), never false. - Resources: bounded collection forgejo://repo/{o}/{r}/branch_protections (EmbeddedListCap sentinel -> list_branch_protections) and single forgejo://repo/{o}/{r}/branch_protection/{rule} (rule may contain slashes). - ParseBranchProtection(s) added to operation/resource; wired into operation.go + cmd/cli.go domain grouping; README tool table updated. - httptest coverage: list, get (ok/404), create (contexts round-trip; missing branch_name -> no call), edit (only-passed-fields null-safety; contexts), delete; parser + truncation + error-mapping tests. Refines the edit scenario in the change spec to match SDK marshaling (null = leave-unchanged). tasks.md complete. Closes forgejo-mcp-uc6.
61 lines
2 KiB
Go
61 lines
2 KiB
Go
package resource
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseBranchProtections(t *testing.T) {
|
|
p, err := ParseBranchProtections("forgejo://repo/goern/forgejo-mcp/branch_protections")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if p.Owner != "goern" || p.Repo != "forgejo-mcp" {
|
|
t.Errorf("got %+v", p)
|
|
}
|
|
}
|
|
|
|
func TestParseBranchProtections_Invalid(t *testing.T) {
|
|
for _, uri := range []string{
|
|
"forgejo://repo/goern/forgejo-mcp", // missing segment
|
|
"forgejo://repo/goern/forgejo-mcp/branch_protection", // singular, no rule
|
|
"forgejo://repo/goern/forgejo-mcp/branch_protections/x", // extra segment
|
|
"https://repo/goern/forgejo-mcp/branch_protections", // wrong scheme
|
|
} {
|
|
if _, err := ParseBranchProtections(uri); !errors.Is(err, ErrInvalidParams) {
|
|
t.Errorf("ParseBranchProtections(%q): expected ErrInvalidParams, got %v", uri, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseBranchProtection(t *testing.T) {
|
|
p, err := ParseBranchProtection("forgejo://repo/goern/forgejo-mcp/branch_protection/main")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if p.Owner != "goern" || p.Repo != "forgejo-mcp" || p.Rule != "main" {
|
|
t.Errorf("got %+v", p)
|
|
}
|
|
}
|
|
|
|
func TestParseBranchProtection_GlobRuleWithSlash(t *testing.T) {
|
|
p, err := ParseBranchProtection("forgejo://repo/goern/forgejo-mcp/branch_protection/release/v1")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if p.Rule != "release/v1" {
|
|
t.Errorf("expected rule reassembled to 'release/v1', got %q", p.Rule)
|
|
}
|
|
}
|
|
|
|
func TestParseBranchProtection_Invalid(t *testing.T) {
|
|
for _, uri := range []string{
|
|
"forgejo://repo/goern/forgejo-mcp/branch_protection", // no rule
|
|
"forgejo://repo/goern/forgejo-mcp/branch_protections", // plural collection
|
|
"forgejo://owner/goern/branch_protection/main", // wrong host
|
|
} {
|
|
if _, err := ParseBranchProtection(uri); !errors.Is(err, ErrInvalidParams) {
|
|
t.Errorf("ParseBranchProtection(%q): expected ErrInvalidParams, got %v", uri, err)
|
|
}
|
|
}
|
|
}
|