go-forge/path_escape_test.go
Virgil 9cdab89c6c Fix service path segment escaping
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-23 06:47:10 +00:00

257 lines
7.6 KiB
Go

package forge
import (
"context"
"net/http"
"net/http/httptest"
"testing"
)
func TestServicePathEscaping_Good(t *testing.T) {
ctx := context.Background()
tests := []struct {
name string
method string
wantPath string
status int
headers map[string]string
body string
call func(context.Context, *Forge) error
}{
{
name: "contents raw file",
method: http.MethodGet,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/raw/docs%2Fread%20me.md",
body: "raw data",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Contents.GetRawFile(ctx, "core/team", "go forge", "docs/read me.md")
return err
},
},
{
name: "wiki delete page",
method: http.MethodDelete,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/wiki/page/Runbook%2FIntro",
call: func(ctx context.Context, f *Forge) error {
return f.Wiki.DeletePage(ctx, "core/team", "go forge", "Runbook/Intro")
},
},
{
name: "actions delete repo secret",
method: http.MethodDelete,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/actions/secrets/DEPLOY%2FKEY",
call: func(ctx context.Context, f *Forge) error {
return f.Actions.DeleteRepoSecret(ctx, "core/team", "go forge", "DEPLOY/KEY")
},
},
{
name: "actions list org secrets",
method: http.MethodGet,
wantPath: "/api/v1/orgs/ops%2Fsec/actions/secrets",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Actions.ListOrgSecrets(ctx, "ops/sec")
return err
},
},
{
name: "actions dispatch workflow",
method: http.MethodPost,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/actions/workflows/build%2Frelease.yml/dispatches",
call: func(ctx context.Context, f *Forge) error {
return f.Actions.DispatchWorkflow(ctx, "core/team", "go forge", "build/release.yml", map[string]any{"ref": "main"})
},
},
{
name: "branches delete protection",
method: http.MethodDelete,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/branch_protections/main%2Frelease",
call: func(ctx context.Context, f *Forge) error {
return f.Branches.DeleteBranchProtection(ctx, "core/team", "go forge", "main/release")
},
},
{
name: "commits list statuses",
method: http.MethodGet,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/commits/feature%2Fone/statuses",
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Commits.ListStatuses(ctx, "core/team", "go forge", "feature/one")
return err
},
},
{
name: "issues remove label",
method: http.MethodDelete,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/issues/42/labels/7",
call: func(ctx context.Context, f *Forge) error {
return f.Issues.RemoveLabel(ctx, "core/team", "go forge", 42, 7)
},
},
{
name: "labels list org labels",
method: http.MethodGet,
wantPath: "/api/v1/orgs/ops%2Fsec/labels",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Labels.ListOrgLabels(ctx, "ops/sec")
return err
},
},
{
name: "misc get license",
method: http.MethodGet,
wantPath: "/api/v1/licenses/Apache%202.0%2Fcustom",
body: "{}",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Misc.GetLicense(ctx, "Apache 2.0/custom")
return err
},
},
{
name: "misc get gitignore template",
method: http.MethodGet,
wantPath: "/api/v1/gitignore/templates/Go%2FTooling",
body: "{}",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Misc.GetGitignoreTemplate(ctx, "Go/Tooling")
return err
},
},
{
name: "notifications list repo",
method: http.MethodGet,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/notifications",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Notifications.ListRepo(ctx, "core/team", "go forge")
return err
},
},
{
name: "orgs add member",
method: http.MethodPut,
wantPath: "/api/v1/orgs/ops%2Fsec/members/alice%2Fbob",
call: func(ctx context.Context, f *Forge) error {
return f.Orgs.AddMember(ctx, "ops/sec", "alice/bob")
},
},
{
name: "orgs list user orgs",
method: http.MethodGet,
wantPath: "/api/v1/users/alice%2Fbob/orgs",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Orgs.ListUserOrgs(ctx, "alice/bob")
return err
},
},
{
name: "packages get",
method: http.MethodGet,
wantPath: "/api/v1/packages/ops%2Fsec/container%2Fv2/app%2Fservice/v1.0%2Fbeta",
body: "{}",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Packages.Get(ctx, "ops/sec", "container/v2", "app/service", "v1.0/beta")
return err
},
},
{
name: "pulls undismiss review",
method: http.MethodPost,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/pulls/42/reviews/9/undismissals",
call: func(ctx context.Context, f *Forge) error {
return f.Pulls.UndismissReview(ctx, "core/team", "go forge", 42, 9)
},
},
{
name: "releases delete by tag",
method: http.MethodDelete,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/releases/tags/v1.0%2Frelease",
call: func(ctx context.Context, f *Forge) error {
return f.Releases.DeleteByTag(ctx, "core/team", "go forge", "v1.0/release")
},
},
{
name: "repos list org repos",
method: http.MethodGet,
wantPath: "/api/v1/orgs/ops%2Fsec/repos",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Repos.ListOrgRepos(ctx, "ops/sec")
return err
},
},
{
name: "repos mirror sync",
method: http.MethodPost,
wantPath: "/api/v1/repos/core%2Fteam/go%20forge/mirror-sync",
call: func(ctx context.Context, f *Forge) error {
return f.Repos.MirrorSync(ctx, "core/team", "go forge")
},
},
{
name: "users star repo",
method: http.MethodPut,
wantPath: "/api/v1/user/starred/core%2Fteam/go%20forge",
call: func(ctx context.Context, f *Forge) error {
return f.Users.Star(ctx, "core/team", "go forge")
},
},
{
name: "teams add repo",
method: http.MethodPut,
wantPath: "/api/v1/teams/42/repos/ops%2Fsec/go%20forge",
call: func(ctx context.Context, f *Forge) error {
return f.Teams.AddRepo(ctx, 42, "ops/sec", "go forge")
},
},
{
name: "webhooks list org hooks",
method: http.MethodGet,
wantPath: "/api/v1/orgs/ops%2Fsec/hooks",
headers: map[string]string{"X-Total-Count": "0"},
body: "[]",
call: func(ctx context.Context, f *Forge) error {
_, err := f.Webhooks.ListOrgHooks(ctx, "ops/sec")
return err
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != tc.method {
t.Errorf("expected %s, got %s", tc.method, r.Method)
}
if r.URL.EscapedPath() != tc.wantPath {
t.Errorf("wrong path: %s", r.URL.EscapedPath())
}
for k, v := range tc.headers {
w.Header().Set(k, v)
}
if tc.status != 0 {
w.WriteHeader(tc.status)
}
if tc.body != "" {
if _, err := w.Write([]byte(tc.body)); err != nil {
t.Fatalf("write response: %v", err)
}
}
}))
defer srv.Close()
f := NewForge(srv.URL, "tok")
if err := tc.call(ctx, f); err != nil {
t.Fatal(err)
}
})
}
}