9 Commits

Author SHA1 Message Date
293fa65131 Merge branch 'update-with-local-changes'
Some checks failed
goreleaser / goreleaser (push) Failing after 22s
goreleaser / release-image (push) Failing after 2m57s
# Conflicts:
#	cmd/actions.go
#	cmd/actions/runs.go
#	cmd/issues/dependencies.go
#	docs/CLI.md
#	modules/api/client.go
2026-05-02 18:59:09 +02:00
5c4620d940 fix: validate order of / and # in cross-repo dependency parsing
Some checks failed
goreleaser / release-image (push) Failing after 27m28s
goreleaser / goreleaser (push) Failing after 27m29s
The previous parsing logic for cross-repo dependencies (owner/repo#123)
only checked if both "/" and "#" were present, but didn't verify that
"/" came before "#". This could cause inputs like "#123/owner/repo" to
incorrectly match the cross-repo pattern.

Now explicitly check that slashIdx < hashIdx before treating as cross-repo.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 12:59:29 +00:00
8336a0a8e0 feat(issues): add dependency management commands
Add commands to manage issue dependencies using the Gitea API:
- `tea issues dependencies <index>` - list dependencies
- `tea issues dependencies add <index> <dep>` - add a dependency
- `tea issues dependencies remove <index> <dep>` - remove a dependency

Supports cross-repo dependencies with owner/repo#index syntax.
Supports all output formats (table, json, csv, etc.).

Closes #4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 12:59:29 +00:00
3651a60024 Merge pull request '[Issue #1] Add Actions logs commands using existing Gitea 1.25 API' (#2) from issue-1-actions-logs-commands into main
Some checks failed
goreleaser / goreleaser (push) Failing after 20s
goreleaser / release-image (push) Failing after 2m21s
2025-12-30 19:14:08 +00:00
Hugo Nijhuis-Mekkelholt
6e88132305 chore: retrigger CI
Some checks failed
check-and-test / Run govulncheck (pull_request) Successful in 36s
check-and-test / check-and-test (pull_request) Failing after 1m21s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 20:07:01 +01:00
Hugo Nijhuis-Mekkelholt
38f93a4997 docs: generate documentation for actions commands
Some checks failed
check-and-test / Run govulncheck (pull_request) Successful in 2m18s
check-and-test / check-and-test (pull_request) Failing after 2m22s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 20:02:57 +01:00
Hugo Nijhuis-Mekkelholt
9fdbd4aafc fix: formatting and error message capitalization
Some checks failed
check-and-test / check-and-test (pull_request) Failing after 1m34s
check-and-test / Run govulncheck (pull_request) Successful in 1m54s
- Run go fmt on modules/api/types.go to fix struct alignment
- Use lowercase error messages to match codebase conventions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 19:57:49 +01:00
Hugo Nijhuis-Mekkelholt
7aa00e6f54 fix: address code review feedback
Some checks failed
check-and-test / check-and-test (pull_request) Failing after 1m38s
check-and-test / Run govulncheck (pull_request) Successful in 1m55s
- Fix GetRaw() error handling to check status before reading body
- Add context.Context support to all HTTP requests
- Use consistent capitalized error messages
- Update copyright year from 2024 to 2025
- Add unit tests for modules/api/client.go
- Add tests for runs, jobs, logs commands

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 19:51:52 +01:00
Hugo Nijhuis-Mekkelholt
cea501523e feat(actions): add runs, jobs, and logs commands
Some checks failed
check-and-test / Run govulncheck (pull_request) Successful in 1m43s
check-and-test / check-and-test (pull_request) Failing after 2m29s
Implement tea actions commands to view workflow runs and logs using
the Gitea 1.25 API endpoints directly. This adds:
- `tea actions runs` - list workflow runs for a repository
- `tea actions jobs <run-id>` - list jobs for a specific run
- `tea actions logs <job-id>` - display logs for a specific job

Also adds a new `modules/api` package for making raw authenticated
HTTP requests to Gitea API endpoints not yet supported by the go-sdk.

Closes #1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 19:40:19 +01:00
6 changed files with 395 additions and 273 deletions

View File

@@ -42,6 +42,7 @@ var CmdActions = cli.Command{
},
}
func runActionsDefault(_ stdctx.Context, cmd *cli.Command) error {
return cli.ShowSubcommandHelp(cmd)
func runActionsDefault(ctx stdctx.Context, cmd *cli.Command) error {
// Default to showing help
return cli.ShowCommandHelp(ctx, cmd, "actions")
}

62
cmd/actions/jobs_test.go Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"testing"
)
func TestJobsCommandFlags(t *testing.T) {
cmd := CmdActionsJobs
// Test that required flags exist
expectedFlags := []string{"output", "remote", "login", "repo"}
for _, flagName := range expectedFlags {
found := false
for _, flag := range cmd.Flags {
for _, name := range flag.Names() {
if name == flagName {
found = true
break
}
}
if found {
break
}
}
if !found {
t.Errorf("Expected flag %s not found in CmdActionsJobs", flagName)
}
}
}
func TestJobsCommandProperties(t *testing.T) {
cmd := CmdActionsJobs
if cmd.Name != "jobs" {
t.Errorf("Expected command name 'jobs', got %s", cmd.Name)
}
if len(cmd.Aliases) == 0 || cmd.Aliases[0] != "job" {
t.Errorf("Expected alias 'job' for jobs command")
}
if cmd.Usage == "" {
t.Error("Jobs command should have usage text")
}
if cmd.Description == "" {
t.Error("Jobs command should have description")
}
if cmd.ArgsUsage != "<run-id>" {
t.Errorf("Expected ArgsUsage '<run-id>', got %s", cmd.ArgsUsage)
}
if cmd.Action == nil {
t.Error("Jobs command should have an action")
}
}

62
cmd/actions/logs_test.go Normal file
View File

@@ -0,0 +1,62 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"testing"
)
func TestLogsCommandFlags(t *testing.T) {
cmd := CmdActionsLogs
// Test that required flags exist
expectedFlags := []string{"output", "remote", "login", "repo"}
for _, flagName := range expectedFlags {
found := false
for _, flag := range cmd.Flags {
for _, name := range flag.Names() {
if name == flagName {
found = true
break
}
}
if found {
break
}
}
if !found {
t.Errorf("Expected flag %s not found in CmdActionsLogs", flagName)
}
}
}
func TestLogsCommandProperties(t *testing.T) {
cmd := CmdActionsLogs
if cmd.Name != "logs" {
t.Errorf("Expected command name 'logs', got %s", cmd.Name)
}
if len(cmd.Aliases) == 0 || cmd.Aliases[0] != "log" {
t.Errorf("Expected alias 'log' for logs command")
}
if cmd.Usage == "" {
t.Error("Logs command should have usage text")
}
if cmd.Description == "" {
t.Error("Logs command should have description")
}
if cmd.ArgsUsage != "<job-id>" {
t.Errorf("Expected ArgsUsage '<job-id>', got %s", cmd.ArgsUsage)
}
if cmd.Action == nil {
t.Error("Logs command should have an action")
}
}

58
cmd/actions/runs_test.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"testing"
)
func TestRunsCommandFlags(t *testing.T) {
cmd := CmdActionsRuns
// Test that required flags exist
expectedFlags := []string{"page", "limit", "output", "remote", "login", "repo"}
for _, flagName := range expectedFlags {
found := false
for _, flag := range cmd.Flags {
for _, name := range flag.Names() {
if name == flagName {
found = true
break
}
}
if found {
break
}
}
if !found {
t.Errorf("Expected flag %s not found in CmdActionsRuns", flagName)
}
}
}
func TestRunsCommandProperties(t *testing.T) {
cmd := CmdActionsRuns
if cmd.Name != "runs" {
t.Errorf("Expected command name 'runs', got %s", cmd.Name)
}
if len(cmd.Aliases) == 0 || cmd.Aliases[0] != "run" {
t.Errorf("Expected alias 'run' for runs command")
}
if cmd.Usage == "" {
t.Error("Runs command should have usage text")
}
if cmd.Description == "" {
t.Error("Runs command should have description")
}
if cmd.Action == nil {
t.Error("Runs command should have an action")
}
}

View File

@@ -274,7 +274,7 @@ Manage and checkout pull requests
**--comments**: Whether to display comments (will prompt if not provided & run interactively)
**--fields, -f**="": Comma-separated list of fields to print. Available values:
index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments,ci
index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments
(default: "index,title,state,author,milestone,updated,labels")
**--limit, --lm**="": specify limit of items per page (default: 30)
@@ -296,7 +296,7 @@ Manage and checkout pull requests
List pull requests of the repository
**--fields, -f**="": Comma-separated list of fields to print. Available values:
index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments,ci
index,state,author,author-id,url,title,body,mergeable,base,base-commit,head,diff,patch,created,updated,deadline,assignees,milestone,labels,comments
(default: "index,title,state,author,milestone,updated,labels")
**--limit, --lm**="": specify limit of items per page (default: 30)
@@ -345,8 +345,6 @@ Deletes local & remote feature-branches for a closed pull request
Create a pull-request
**--agit**: Create an agit flow pull request
**--allow-maintainer-edits, --edits**: Enable maintainers to push to the base branch of created pull
**--assignees, -a**="": Comma-separated list of usernames to assign
@@ -373,8 +371,6 @@ Create a pull-request
**--title, -t**="":
**--topic**="": Topic name for agit flow pull request
### close
Change state of one or more pull requests to 'closed'
@@ -399,36 +395,6 @@ Change state of one or more pull requests to 'open'
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
### edit, e
Edit one or more pull requests
**--add-assignees, -a**="": Comma-separated list of usernames to assign
**--add-labels, -L**="": Comma-separated list of labels to assign. Takes precedence over --remove-labels
**--add-reviewers, -r**="": Comma-separated list of usernames to request review from
**--deadline, -D**="": Deadline timestamp to assign
**--description, -d**="":
**--login, -l**="": Use a different Gitea Login. Optional
**--milestone, -m**="": Milestone to assign
**--referenced-version, -v**="": commit-hash or tag name to assign
**--remote, -R**="": Discover Gitea login from remote. Optional
**--remove-labels**="": Comma-separated list of labels to remove
**--remove-reviewers**="": Comma-separated list of usernames to remove from reviewers
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
**--title, -t**="":
### review
Interactively review a pull request
@@ -483,46 +449,6 @@ Merge a pull request
**--title, -t**="": Merge commit title
### review-comments, rc
List review comments on a pull request
**--fields, -f**="": Comma-separated list of fields to print. Available values:
id,body,reviewer,path,line,resolver,created,updated,url
(default: "id,path,line,body,reviewer,resolver")
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
### resolve
Resolve a review comment on a pull request
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
### unresolve
Unresolve a review comment on a pull request
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
## labels, label
Manage issue labels
@@ -923,16 +849,12 @@ Operate on tracked times of a repository's issues & pulls
**--from, -f**="": Show only times tracked after this date
**--limit, --lm**="": specify limit of items per page (default: 30)
**--login, -l**="": Use a different Gitea Login. Optional
**--mine, -m**: Show all times tracked by you across all repositories (overrides command arguments)
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--page, -p**="": specify page (default: 1)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
@@ -987,16 +909,12 @@ List tracked times on issues & pulls
**--from, -f**="": Show only times tracked after this date
**--limit, --lm**="": specify limit of items per page (default: 30)
**--login, -l**="": Use a different Gitea Login. Optional
**--mine, -m**: Show all times tracked by you across all repositories (overrides command arguments)
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--page, -p**="": specify page (default: 1)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
@@ -1065,7 +983,7 @@ Delete users Organizations
## repos, repo
Manage repositories
Show repository details
**--fields, -f**="": Comma-separated list of fields to print. Available values:
description,forks,id,name,owner,stars,ssh,updated,url,permission,type
@@ -1077,8 +995,6 @@ Manage repositories
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--owner, -O**="": List repos of a specific owner (org or user)
**--page, -p**="": specify page (default: 1)
**--starred, -s**: List your starred repos instead
@@ -1101,8 +1017,6 @@ List repositories you have access to
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--owner, -O**="": List repos of a specific owner (org or user)
**--page, -p**="": specify page (default: 1)
**--starred, -s**: List your starred repos instead
@@ -1273,32 +1187,6 @@ Delete an existing repository
**--owner, -O**="": owner of the repo
### edit, e
Edit repository properties
**--archived**="": Set archived [true/false]
**--default-branch**="": Set default branch
**--description, --desc**="": New description of the repository
**--login, -l**="": Use a different Gitea Login. Optional
**--name**="": New name of the repository
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--private**="": Set private [true/false]
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
**--template**="": Set template [true/false]
**--website**="": New website URL of the repository
## branches, branch, b
Consult branches
@@ -1381,26 +1269,6 @@ Unprotect branches
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
### rename, rn
Rename a branch
**--fields, -f**="": Comma-separated list of fields to print. Available values:
name,protected,user-can-merge,user-can-push,protection
(default: "name,protected,user-can-merge,user-can-push")
**--limit, --lm**="": specify limit of items per page (default: 30)
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--page, -p**="": specify page (default: 1)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
## actions, action
Manage repository actions
@@ -1511,18 +1379,8 @@ Delete an action variable
### runs, run
Manage workflow runs
#### list, ls
List workflow runs
**--actor**="": Filter by actor username (who triggered the run)
**--branch**="": Filter by branch name
**--event**="": Filter by event type (push, pull_request, etc.)
**--limit, --lm**="": specify limit of items per page (default: 30)
**--login, -l**="": Use a different Gitea Login. Optional
@@ -1535,17 +1393,9 @@ List workflow runs
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
**--since**="": Show runs started after this time (e.g., '24h', '2024-01-01')
### jobs, job
**--status**="": Filter by status (success, failure, pending, queued, in_progress, skipped, canceled)
**--until**="": Show runs started before this time (e.g., '2024-01-01')
#### view, show, get
View workflow run details
**--jobs**: show jobs table
List jobs for a workflow run
**--login, -l**="": Use a different Gitea Login. Optional
@@ -1555,99 +1405,9 @@ View workflow run details
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### delete, remove, rm, cancel
### logs, log
Delete or cancel a workflow run
**--confirm, -y**: confirm deletion without prompting
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### logs, log
View workflow run logs
**--follow, -f**: follow log output (like tail -f), requires job to be in progress
**--job**="": specific job ID to view logs for (if omitted, shows all jobs)
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
### workflows, workflow
Manage repository workflows
#### list, ls
List repository workflows
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### view, show, get
View workflow details
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### dispatch, trigger, run
Dispatch a workflow run
**--follow, -f**: follow log output after dispatching
**--input, -i**="": workflow input in key=value format (can be specified multiple times)
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--ref, -r**="": branch or tag to dispatch on (default: current branch)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### enable
Enable a workflow
**--login, -l**="": Use a different Gitea Login. Optional
**--output, -o**="": Output format. (simple, table, csv, tsv, yaml, json)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
#### disable
Disable a workflow
**--confirm, -y**: confirm disable without prompting
Display logs for a job
**--login, -l**="": Use a different Gitea Login. Optional
@@ -1984,27 +1744,3 @@ List Users
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional
## api
Make an authenticated API request
**--Field, -F**="": Add a typed field to the request body (key=value, @file, or @- for stdin)
**--data, -d**="": Raw JSON request body (use @file to read from file, @- for stdin)
**--field, -f**="": Add a string field to the request body (key=value)
**--header, -H**="": Add a custom header (key:value)
**--include, -i**: Include HTTP status and response headers in output (written to stderr)
**--login, -l**="": Use a different Gitea Login. Optional
**--method, -X**="": HTTP method (GET, POST, PUT, PATCH, DELETE) (default: "GET")
**--output, -o**="": Write response body to file instead of stdout (use '-' for stdout)
**--remote, -R**="": Discover Gitea login from remote. Optional
**--repo, -r**="": Override local repository path or gitea repository slug to interact with. Optional

203
modules/api/client_test.go Normal file
View File

@@ -0,0 +1,203 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package api
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"code.gitea.io/tea/modules/config"
)
func TestNewClient(t *testing.T) {
login := &config.Login{
URL: "https://gitea.example.com",
Token: "test-token",
Insecure: false,
}
client := NewClient(login)
if client == nil {
t.Fatal("NewClient returned nil")
}
if client.login != login {
t.Error("Client login not set correctly")
}
if client.httpClient == nil {
t.Error("Client httpClient not set")
}
}
func TestNewClientInsecure(t *testing.T) {
login := &config.Login{
URL: "https://gitea.example.com",
Token: "test-token",
Insecure: true,
}
client := NewClient(login)
if client == nil {
t.Fatal("NewClient returned nil")
}
// Verify that insecure transport is configured
if client.httpClient.Transport == nil {
t.Error("Expected custom transport for insecure client")
}
}
func TestGet(t *testing.T) {
// Create a test server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verify request
if r.Method != "GET" {
t.Errorf("Expected GET request, got %s", r.Method)
}
if r.Header.Get("Authorization") != "token test-token" {
t.Errorf("Expected authorization header, got %s", r.Header.Get("Authorization"))
}
if r.Header.Get("Accept") != "application/json" {
t.Errorf("Expected accept header, got %s", r.Header.Get("Accept"))
}
// Return test response
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}))
defer server.Close()
login := &config.Login{
URL: server.URL,
Token: "test-token",
}
client := NewClient(login)
var result map[string]string
_, err := client.Get(context.Background(), "/test", &result)
if err != nil {
t.Fatalf("Get returned error: %v", err)
}
if result["status"] != "ok" {
t.Errorf("Expected status 'ok', got %s", result["status"])
}
}
func TestGetError(t *testing.T) {
// Create a test server that returns an error
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("not found"))
}))
defer server.Close()
login := &config.Login{
URL: server.URL,
Token: "test-token",
}
client := NewClient(login)
var result map[string]string
_, err := client.Get(context.Background(), "/test", &result)
if err == nil {
t.Fatal("Expected error for 404 response")
}
expectedError := "API request failed with status 404"
if err.Error()[:len(expectedError)] != expectedError {
t.Errorf("Expected error starting with '%s', got '%s'", expectedError, err.Error())
}
}
func TestGetRaw(t *testing.T) {
expectedBody := "raw log content here"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("Expected GET request, got %s", r.Method)
}
if r.Header.Get("Authorization") != "token test-token" {
t.Errorf("Expected authorization header, got %s", r.Header.Get("Authorization"))
}
w.Write([]byte(expectedBody))
}))
defer server.Close()
login := &config.Login{
URL: server.URL,
Token: "test-token",
}
client := NewClient(login)
body, err := client.GetRaw(context.Background(), "/logs")
if err != nil {
t.Fatalf("GetRaw returned error: %v", err)
}
if string(body) != expectedBody {
t.Errorf("Expected body '%s', got '%s'", expectedBody, string(body))
}
}
func TestGetRawError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal error"))
}))
defer server.Close()
login := &config.Login{
URL: server.URL,
Token: "test-token",
}
client := NewClient(login)
_, err := client.GetRaw(context.Background(), "/logs")
if err == nil {
t.Fatal("Expected error for 500 response")
}
expectedError := "API request failed with status 500"
if err.Error()[:len(expectedError)] != expectedError {
t.Errorf("Expected error starting with '%s', got '%s'", expectedError, err.Error())
}
}
func TestGetWithContextCancellation(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// This should not be reached if context is cancelled
w.Write([]byte("ok"))
}))
defer server.Close()
login := &config.Login{
URL: server.URL,
Token: "test-token",
}
client := NewClient(login)
// Create a cancelled context
ctx, cancel := context.WithCancel(context.Background())
cancel()
var result map[string]string
_, err := client.Get(ctx, "/test", &result)
if err == nil {
t.Fatal("Expected error for cancelled context")
}
}