Files
gitea-mcp-server/mcp/operation/integration_test.go
T

583 lines
14 KiB
Go

package operation
import (
"errors"
"testing"
gitea_errors "gitea.com/gitea/gitea-mcp/pkg/errors"
)
func TestErrorScenarios_NotFound(t *testing.T) {
tests := []struct {
name string
errMsg string
ctx map[string]string
isNotFound bool
}{
{
name: "404 file not found",
errMsg: "request failed with status 404: GetContents error",
ctx: map[string]string{"operation": "GetFile", "path": "README.md"},
isNotFound: true,
},
{
name: "404 repo not found",
errMsg: "request failed with status 404: GetRepo error",
ctx: map[string]string{"operation": "GetRepo"},
isNotFound: true,
},
{
name: "404 issue not found",
errMsg: "request failed with status 404: GetIssue error",
ctx: map[string]string{"operation": "GetIssue"},
isNotFound: true,
},
{
name: "404 pull request not found",
errMsg: "request failed with status 404: GetPullRequest error",
ctx: map[string]string{"operation": "GetPullRequest"},
isNotFound: true,
},
{
name: "401 auth error not found",
errMsg: "request failed with status 401: unauthorized",
ctx: map[string]string{"operation": "GetFile"},
isNotFound: false,
},
{
name: "500 server error not found",
errMsg: "request failed with status 500: internal server error",
ctx: map[string]string{"operation": "GetFile"},
isNotFound: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
translated := gitea_errors.TranslateError(err, tt.ctx)
if gitea_errors.IsNotFound(translated) != tt.isNotFound {
t.Errorf("IsNotFound() = %v, want %v", gitea_errors.IsNotFound(translated), tt.isNotFound)
}
})
}
}
func TestErrorScenarios_AuthErrors(t *testing.T) {
tests := []struct {
name string
errMsg string
isAuthErr bool
}{
{
name: "401 unauthorized",
errMsg: "request failed with status 401: unauthorized",
isAuthErr: true,
},
{
name: "403 forbidden",
errMsg: "request failed with status 403: forbidden",
isAuthErr: true,
},
{
name: "authentication failed message",
errMsg: "authentication failed",
isAuthErr: true,
},
{
name: "permission denied message",
errMsg: "permission denied",
isAuthErr: true,
},
{
name: "token error message",
errMsg: "check your access token",
isAuthErr: true,
},
{
name: "404 not auth error",
errMsg: "request failed with status 404: not found",
isAuthErr: false,
},
{
name: "500 not auth error",
errMsg: "request failed with status 500: server error",
isAuthErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
translated := gitea_errors.TranslateError(err, nil)
if gitea_errors.IsAuthError(translated) != tt.isAuthErr {
t.Errorf("IsAuthError() = %v, want %v", gitea_errors.IsAuthError(translated), tt.isAuthErr)
}
})
}
}
func TestErrorScenarios_NetworkErrors(t *testing.T) {
tests := []struct {
name string
errMsg string
isNetworkErr bool
isTimeout bool
}{
{
name: "connection refused",
errMsg: "connection refused",
isNetworkErr: true,
isTimeout: false,
},
{
name: "no such host",
errMsg: "no such host example.com",
isNetworkErr: true,
isTimeout: false,
},
{
name: "network unreachable",
errMsg: "network unreachable",
isNetworkErr: true,
isTimeout: false,
},
{
name: "timeout error",
errMsg: "request timeout",
isNetworkErr: true,
isTimeout: true,
},
{
name: "deadline exceeded",
errMsg: "context deadline exceeded",
isNetworkErr: true,
isTimeout: true,
},
{
name: "dial tcp",
errMsg: "dial tcp: connection refused",
isNetworkErr: true,
isTimeout: false,
},
{
name: "not network error",
errMsg: "file not found",
isNetworkErr: false,
isTimeout: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
translated := gitea_errors.TranslateError(err, nil)
if gitea_errors.IsNetworkError(translated) != tt.isNetworkErr {
t.Errorf("IsNetworkError() = %v, want %v", gitea_errors.IsNetworkError(translated), tt.isNetworkErr)
}
if gitea_errors.IsTimeout(translated) != tt.isTimeout {
t.Errorf("IsTimeout() = %v, want %v", gitea_errors.IsTimeout(translated), tt.isTimeout)
}
})
}
}
func TestErrorScenarios_ActionsAPIUnavailable(t *testing.T) {
tests := []struct {
name string
errMsg string
isActionsUnavailable bool
}{
{
name: "actions 404",
errMsg: "actions endpoint returned 404",
isActionsUnavailable: true,
},
{
name: "actions 405",
errMsg: "actions endpoint returned 405",
isActionsUnavailable: true,
},
{
name: "actions not found",
errMsg: "actions workflow not found",
isActionsUnavailable: true,
},
{
name: "actions method not allowed",
errMsg: "actions method not allowed",
isActionsUnavailable: true,
},
{
name: "actions enhanced error",
errMsg: "not supported on this Gitea version",
isActionsUnavailable: true,
},
{
name: "other actions error",
errMsg: "actions completed successfully",
isActionsUnavailable: false,
},
{
name: "file not found not actions",
errMsg: "file not found",
isActionsUnavailable: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
if gitea_errors.IsActionsAPIUnavailable(err) != tt.isActionsUnavailable {
t.Errorf("IsActionsAPIUnavailable() = %v, want %v", gitea_errors.IsActionsAPIUnavailable(err), tt.isActionsUnavailable)
}
})
}
}
func TestErrorScenarios_HTTPStatusCodes(t *testing.T) {
tests := []struct {
name string
statusCode int
isUnauthorized bool
isForbidden bool
isNotFound bool
isServerError bool
}{
{
name: "HTTP 401",
statusCode: 401,
isUnauthorized: true,
isForbidden: false,
isNotFound: false,
isServerError: false,
},
{
name: "HTTP 403",
statusCode: 403,
isUnauthorized: false,
isForbidden: true,
isNotFound: false,
isServerError: false,
},
{
name: "HTTP 404",
statusCode: 404,
isUnauthorized: false,
isForbidden: false,
isNotFound: true,
isServerError: false,
},
{
name: "HTTP 500",
statusCode: 500,
isUnauthorized: false,
isForbidden: false,
isNotFound: false,
isServerError: true,
},
{
name: "HTTP 502",
statusCode: 502,
isUnauthorized: false,
isForbidden: false,
isNotFound: false,
isServerError: true,
},
{
name: "HTTP 503",
statusCode: 503,
isUnauthorized: false,
isForbidden: false,
isNotFound: false,
isServerError: true,
},
{
name: "HTTP 200",
statusCode: 200,
isUnauthorized: false,
isForbidden: false,
isNotFound: false,
isServerError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := &testHTTPError{status: tt.statusCode, message: "test error"}
if gitea_errors.IsUnauthorized(err) != tt.isUnauthorized {
t.Errorf("IsUnauthorized() = %v, want %v", gitea_errors.IsUnauthorized(err), tt.isUnauthorized)
}
if gitea_errors.IsForbidden(err) != tt.isForbidden {
t.Errorf("IsForbidden() = %v, want %v", gitea_errors.IsForbidden(err), tt.isForbidden)
}
if gitea_errors.IsNotFoundHTTP(err) != tt.isNotFound {
t.Errorf("IsNotFoundHTTP() = %v, want %v", gitea_errors.IsNotFoundHTTP(err), tt.isNotFound)
}
if gitea_errors.IsServerError(err) != tt.isServerError {
t.Errorf("IsServerError() = %v, want %v", gitea_errors.IsServerError(err), tt.isServerError)
}
})
}
}
func TestErrorScenarios_EnhancedErrorChaining(t *testing.T) {
original := errors.New("original error")
err := gitea_errors.TranslateError(original, map[string]string{
"operation": "TestOp",
"param1": "value1",
})
if !errors.Is(err, original) {
t.Error("enhanced error should wrap original")
}
var enhanced *gitea_errors.EnhancedError
if !errors.As(err, &enhanced) {
t.Fatal("should be able to extract EnhancedError")
}
if enhanced.Operation != "TestOp" {
t.Errorf("operation = %q, want %q", enhanced.Operation, "TestOp")
}
if enhanced.Context["param1"] != "value1" {
t.Errorf("context[param1] = %q, want %q", enhanced.Context["param1"], "value1")
}
formatted := enhanced.Format()
if formatted == "" {
t.Error("Format() should return non-empty string")
}
detailed := enhanced.FormatDetailed()
if detailed == "" {
t.Error("FormatDetailed() should return non-empty string")
}
}
func TestErrorScenarios_FluentAPI(t *testing.T) {
original := errors.New("test error")
enhanced := gitea_errors.TranslateError(original, nil).(*gitea_errors.EnhancedError)
result := enhanced.
WithOperation("GetFile").
WithParam("owner", "gitea").
WithParam("repo", "tea").
WithParam("path", "README.md")
if result != enhanced {
t.Error("fluent API should return same error for chaining")
}
if enhanced.Operation != "GetFile" {
t.Errorf("operation = %q, want %q", enhanced.Operation, "GetFile")
}
if enhanced.Context["owner"] != "gitea" {
t.Errorf("context[owner] = %q, want %q", enhanced.Context["owner"], "gitea")
}
if enhanced.Context["repo"] != "tea" {
t.Errorf("context[repo] = %q, want %q", enhanced.Context["repo"], "tea")
}
if enhanced.Context["path"] != "README.md" {
t.Errorf("context[path] = %q, want %q", enhanced.Context["path"], "README.md")
}
}
type testHTTPError struct {
status int
message string
}
func (e *testHTTPError) Error() string { return e.message }
func (e *testHTTPError) Status() int { return e.status }
func TestErrorScenarios_CrossToolErrorConsistency(t *testing.T) {
ctx := map[string]string{
"owner": "test-owner",
"repo": "test-repo",
"operation": "CrossToolTest",
}
testCases := []struct {
name string
errMsg string
category gitea_errors.ErrorCategory
}{
{
name: "file operation error",
errMsg: "GetContents failed",
category: gitea_errors.CategoryFile,
},
{
name: "auth operation error",
errMsg: "GetUser failed",
category: gitea_errors.CategoryAuth,
},
{
name: "repo operation error",
errMsg: "GetRepo failed",
category: gitea_errors.CategoryRepo,
},
{
name: "issue operation error",
errMsg: "GetIssue failed",
category: gitea_errors.CategoryIssue,
},
{
name: "pull request operation error",
errMsg: "GetPullRequest failed",
category: gitea_errors.CategoryPull,
},
{
name: "branch operation error",
errMsg: "GetBranch failed",
category: gitea_errors.CategoryBranch,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
translated := gitea_errors.TranslateError(err, ctx)
var enhanced *gitea_errors.EnhancedError
if !errors.As(translated, &enhanced) {
t.Fatal("expected EnhancedError")
}
if enhanced.Category != tt.category {
t.Errorf("category = %q, want %q", enhanced.Category, tt.category)
}
if enhanced.Context["owner"] != "test-owner" {
t.Errorf("context[owner] not preserved")
}
if enhanced.Context["repo"] != "test-repo" {
t.Errorf("context[repo] not preserved")
}
})
}
}
func TestErrorScenarios_NilHandling(t *testing.T) {
if gitea_errors.TranslateError(nil, nil) != nil {
t.Error("TranslateError(nil) should return nil")
}
if gitea_errors.IsNotFound(nil) {
t.Error("IsNotFound(nil) should return false")
}
if gitea_errors.IsAuthError(nil) {
t.Error("IsAuthError(nil) should return false")
}
if gitea_errors.IsNetworkError(nil) {
t.Error("IsNetworkError(nil) should return false")
}
if gitea_errors.IsTimeout(nil) {
t.Error("IsTimeout(nil) should return false")
}
if gitea_errors.IsActionsAPIUnavailable(nil) {
t.Error("IsActionsAPIUnavailable(nil) should return false")
}
if gitea_errors.IsServerError(nil) {
t.Error("IsServerError(nil) should return false")
}
if gitea_errors.Wrap(nil, "operation") != nil {
t.Error("Wrap(nil) should return nil")
}
}
func TestErrorScenarios_ServerErrors(t *testing.T) {
tests := []struct {
name string
errMsg string
isServerErr bool
}{
{
name: "500 internal server error",
errMsg: "status 500",
isServerErr: true,
},
{
name: "502 bad gateway",
errMsg: "status 502",
isServerErr: true,
},
{
name: "503 service unavailable",
errMsg: "status 503",
isServerErr: true,
},
{
name: "504 gateway timeout",
errMsg: "status 504",
isServerErr: true,
},
{
name: "404 not server error",
errMsg: "status 404",
isServerErr: false,
},
{
name: "200 not server error",
errMsg: "status 200",
isServerErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := errors.New(tt.errMsg)
if gitea_errors.IsServerError(err) != tt.isServerErr {
t.Errorf("IsServerError() = %v, want %v", gitea_errors.IsServerError(err), tt.isServerErr)
}
})
}
}
func TestErrorScenarios_ErrorWrapping(t *testing.T) {
original := errors.New("original error")
wrapped := gitea_errors.Wrap(original, "GetFile")
if !errors.Is(wrapped, original) {
t.Error("wrapped error should contain original")
}
var enhanced *gitea_errors.EnhancedError
if !errors.As(wrapped, &enhanced) {
t.Fatal("wrapped should be EnhancedError")
}
if enhanced.Operation != "GetFile" {
t.Errorf("operation = %q, want %q", enhanced.Operation, "GetFile")
}
}
func TestErrorScenarios_ErrorUnwrap(t *testing.T) {
original := errors.New("original error")
enhanced := gitea_errors.NewEnhancedError(original, "translated", gitea_errors.CategoryFile)
unwrapped := enhanced.Unwrap()
if unwrapped != original {
t.Error("Unwrap() should return original error")
}
if !errors.Is(enhanced, original) {
t.Error("errors.Is should find original through unwrapping")
}
}