182 lines
6.5 KiB
Go
182 lines
6.5 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
ws "github.com/kandev/kandev/pkg/websocket"
|
|
)
|
|
|
|
func TestPRCommitStatsAreMarkedUnavailable(t *testing.T) {
|
|
commits, err := parsePRCommitsJSON(`[
|
|
{
|
|
"sha": "1111111111111111111111111111111111111111",
|
|
"commit": {
|
|
"message": "Remote commit",
|
|
"author": {"date": "2026-08-04T10:00:00Z"}
|
|
},
|
|
"author": {"login": "octocat"}
|
|
}
|
|
]`)
|
|
if err != nil {
|
|
t.Fatalf("parse PR commits: %v", err)
|
|
}
|
|
|
|
encoded, err := json.Marshal(commits)
|
|
if err != nil {
|
|
t.Fatalf("marshal PR commits: %v", err)
|
|
}
|
|
if !strings.Contains(string(encoded), `"stats_available":false`) {
|
|
t.Fatalf("PR commit stats availability = %s, want explicit false", encoded)
|
|
}
|
|
}
|
|
|
|
func TestParsePRCommitDetailJSONMergesPages(t *testing.T) {
|
|
detail, err := parsePRCommitDetailJSON(`[
|
|
{
|
|
"sha": "2222222222222222222222222222222222222222",
|
|
"commit": {
|
|
"message": "Remote detail\nbody",
|
|
"author": {"name": "Octo Cat", "date": "2026-08-04T11:00:00Z"}
|
|
},
|
|
"author": {"login": "octocat"},
|
|
"stats": {"additions": 7, "deletions": 3, "total": 10},
|
|
"files": [
|
|
{"filename": "one.txt", "status": "modified", "additions": 4, "deletions": 1, "patch": "@@ -1 +1 @@\n-old\n+new"},
|
|
{"filename": "duplicate.txt", "status": "added", "additions": 1, "deletions": 0}
|
|
]
|
|
},
|
|
{
|
|
"sha": "2222222222222222222222222222222222222222",
|
|
"commit": {"message": "ignored page metadata", "author": {"name": "Other", "date": "2026-08-05T11:00:00Z"}},
|
|
"author": {"login": "other"},
|
|
"stats": {"additions": 99, "deletions": 99, "total": 198},
|
|
"files": [
|
|
{"filename": "duplicate.txt", "status": "added", "additions": 1, "deletions": 0},
|
|
{"filename": "two.txt", "status": "removed", "additions": 0, "deletions": 2, "patch": "@@ -1 +0,0 @@\n-gone"}
|
|
]
|
|
}
|
|
]`)
|
|
if err != nil {
|
|
t.Fatalf("parse PR commit detail: %v", err)
|
|
}
|
|
if detail.SHA != "2222222222222222222222222222222222222222" || detail.Message != "Remote detail\nbody" {
|
|
t.Fatalf("metadata = %#v", detail)
|
|
}
|
|
if detail.AuthorLogin != "octocat" || detail.AuthorName != "Octo Cat" || detail.AuthorDate != "2026-08-04T11:00:00Z" {
|
|
t.Fatalf("author = %#v", detail)
|
|
}
|
|
if detail.Additions != 7 || detail.Deletions != 3 || detail.FilesChanged != 3 {
|
|
t.Fatalf("stats = %#v", detail)
|
|
}
|
|
if len(detail.Files) != 3 {
|
|
t.Fatalf("files = %#v, want three provider-ordered unique files", detail.Files)
|
|
}
|
|
if detail.Files[0].Filename != "one.txt" || detail.Files[1].Filename != "duplicate.txt" || detail.Files[2].Filename != "two.txt" {
|
|
t.Fatalf("file order = %#v", detail.Files)
|
|
}
|
|
if !strings.Contains(detail.Files[0].Patch, "diff --git a/one.txt b/one.txt") {
|
|
t.Fatalf("file patch = %q, want complete diff", detail.Files[0].Patch)
|
|
}
|
|
}
|
|
|
|
func TestParsePRCommitDetailJSONRejectsEmptyPayloads(t *testing.T) {
|
|
for _, payload := range []string{`null`, `[null]`, `{}`, `[{}]`} {
|
|
t.Run(payload, func(t *testing.T) {
|
|
if _, err := parsePRCommitDetailJSON(payload); err == nil {
|
|
t.Fatalf("parsePRCommitDetailJSON(%q) succeeded, want malformed payload error", payload)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetPRCommitDetailForWorkspaceUsesAuthorizedClient(t *testing.T) {
|
|
const sha = "3333333333333333333333333333333333333333"
|
|
client := NewMockClient()
|
|
client.AddRepos("test-user", []GitHubRepo{{FullName: "acme/widget", Owner: "acme", Name: "widget"}})
|
|
client.AddPRCommitDetail("acme", "widget", sha, PRCommitDetail{
|
|
SHA: sha,
|
|
Files: []PRFile{{Filename: "remote.txt", Status: "modified", Patch: "remote patch"}},
|
|
})
|
|
svc := newWorkspaceAuthenticatedTestService(t, client, nil, "workspace-1")
|
|
|
|
detail, err := svc.GetPRCommitDetailForWorkspace(context.Background(), "workspace-1", "", "acme", "widget", sha)
|
|
if err != nil {
|
|
t.Fatalf("GetPRCommitDetailForWorkspace: %v", err)
|
|
}
|
|
if detail.SHA != sha || len(detail.Files) != 1 || detail.Files[0].Filename != "remote.txt" {
|
|
t.Fatalf("detail = %#v", detail)
|
|
}
|
|
}
|
|
|
|
func TestGetPRCommitDetailForWorkspaceRejectsUnsafeSHA(t *testing.T) {
|
|
svc := &Service{workspaceAuthorizer: func(context.Context, string) error {
|
|
return errors.New("workspace lookup should not run")
|
|
}}
|
|
|
|
_, err := svc.GetPRCommitDetailForWorkspace(context.Background(), "workspace-1", "", "acme", "widget", "main")
|
|
if err == nil || !strings.Contains(err.Error(), "invalid commit SHA") {
|
|
t.Fatalf("error = %v, want invalid commit SHA before provider access", err)
|
|
}
|
|
}
|
|
|
|
func TestWSGetPRCommitDetailRoutesWorkspaceRequest(t *testing.T) {
|
|
const sha = "4444444444444444444444444444444444444444"
|
|
client := NewMockClient()
|
|
client.AddRepos("test-user", []GitHubRepo{{FullName: "acme/widget", Owner: "acme", Name: "widget"}})
|
|
client.AddPRCommitDetail("acme", "widget", sha, PRCommitDetail{SHA: sha, Message: "remote commit"})
|
|
svc := newWorkspaceAuthenticatedTestService(t, client, nil, "workspace-1")
|
|
message, err := ws.NewRequest("request-1", "github.pr_commit.get", map[string]any{
|
|
"workspace_id": "workspace-1", "owner": "acme", "repo": "widget", "sha": sha,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create request: %v", err)
|
|
}
|
|
|
|
response, err := wsGetPRCommitDetail(svc, testLogger(t))(context.Background(), message)
|
|
if err != nil {
|
|
t.Fatalf("handler error: %v", err)
|
|
}
|
|
if response.Type != ws.MessageTypeResponse {
|
|
t.Fatalf("response type = %q, want response", response.Type)
|
|
}
|
|
var body struct {
|
|
Commit PRCommitDetail `json:"commit"`
|
|
}
|
|
if err := json.Unmarshal(response.Payload, &body); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if body.Commit.SHA != sha || body.Commit.Message != "remote commit" {
|
|
t.Fatalf("response body = %#v", body)
|
|
}
|
|
}
|
|
|
|
func TestWSGetPRCommitDetailReturnsWorkspaceAuthorizationError(t *testing.T) {
|
|
svc := NewService(NewMockClient(), AuthMethodPAT, nil, nil, nil, testLogger(t))
|
|
denied := errors.New("workspace not found")
|
|
svc.SetWorkspaceAuthorizer(func(_ context.Context, workspaceID string) error {
|
|
if workspaceID != "workspace-1" {
|
|
t.Fatalf("workspaceID = %q", workspaceID)
|
|
}
|
|
return denied
|
|
})
|
|
message, err := ws.NewRequest("request-1", "github.pr_commit.get", map[string]any{
|
|
"workspace_id": "workspace-1", "owner": "acme", "repo": "widget",
|
|
"sha": "5555555555555555555555555555555555555555",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create request: %v", err)
|
|
}
|
|
|
|
response, err := wsGetPRCommitDetail(svc, testLogger(t))(context.Background(), message)
|
|
if err != nil {
|
|
t.Fatalf("handler error: %v", err)
|
|
}
|
|
if response.Type != ws.MessageTypeError || !strings.Contains(string(response.Payload), denied.Error()) {
|
|
t.Fatalf("response = %#v, want workspace authorization error", response)
|
|
}
|
|
}
|