2026-06-04 02:29:23 -07:00
|
|
|
@testable import ForgejoKit
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
import Foundation
|
|
|
|
|
import Testing
|
|
|
|
|
|
|
|
|
|
struct WorkflowServiceURLTests {
|
|
|
|
|
private func makeClient() -> ForgejoClient {
|
|
|
|
|
ForgejoClient(serverURL: "https://forgejo.example.com", username: "user", password: "pass")
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func runsListURLNoFilters() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let queryItems = [
|
|
|
|
|
URLQueryItem(name: "page", value: "1"),
|
|
|
|
|
URLQueryItem(name: "limit", value: "20"),
|
|
|
|
|
]
|
|
|
|
|
let url = try client.makeRepoURL(
|
|
|
|
|
owner: "owner", repo: "repo",
|
|
|
|
|
path: "/actions/runs", queryItems: queryItems,
|
|
|
|
|
)
|
|
|
|
|
#expect(url.path == "/api/v1/repos/owner/repo/actions/runs")
|
|
|
|
|
#expect(url.query?.contains("page=1") == true)
|
|
|
|
|
#expect(url.query?.contains("limit=20") == true)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func runsListURLAllFilters() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let queryItems = [
|
|
|
|
|
URLQueryItem(name: "page", value: "2"),
|
|
|
|
|
URLQueryItem(name: "limit", value: "50"),
|
|
|
|
|
URLQueryItem(name: "status", value: "running"),
|
|
|
|
|
URLQueryItem(name: "event", value: "push"),
|
|
|
|
|
URLQueryItem(name: "ref", value: "main"),
|
|
|
|
|
URLQueryItem(name: "workflow_id", value: "ci.yml"),
|
|
|
|
|
URLQueryItem(name: "run_number", value: "42"),
|
|
|
|
|
URLQueryItem(name: "head_sha", value: "abc123"),
|
|
|
|
|
]
|
|
|
|
|
let url = try client.makeRepoURL(
|
|
|
|
|
owner: "owner", repo: "repo",
|
|
|
|
|
path: "/actions/runs", queryItems: queryItems,
|
|
|
|
|
)
|
|
|
|
|
#expect(url.query?.contains("status=running") == true)
|
|
|
|
|
#expect(url.query?.contains("event=push") == true)
|
|
|
|
|
#expect(url.query?.contains("ref=main") == true)
|
|
|
|
|
#expect(url.query?.contains("workflow_id=ci.yml") == true)
|
|
|
|
|
#expect(url.query?.contains("run_number=42") == true)
|
|
|
|
|
#expect(url.query?.contains("head_sha=abc123") == true)
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func runDetailURL() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let url = try client.makeRepoURL(owner: "owner", repo: "repo", path: "/actions/runs/123")
|
|
|
|
|
#expect(url.absoluteString == "https://forgejo.example.com/api/v1/repos/owner/repo/actions/runs/123")
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func ownerAndRepoArePercentEncoded() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let url = try client.makeRepoURL(owner: "my org", repo: "my repo", path: "/actions/runs")
|
|
|
|
|
#expect(url.absoluteString.contains("my%20org"))
|
|
|
|
|
#expect(url.absoluteString.contains("my%20repo"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Experimental run-view URL (web routes, not /api/v1)
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func runViewURLIsUnderRepoWebRoute() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let url = try client.makeURL(path: "/owner/repo/actions/runs/12/jobs/0")
|
|
|
|
|
#expect(url.absoluteString == "https://forgejo.example.com/owner/repo/actions/runs/12/jobs/0")
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 03:06:27 -07:00
|
|
|
@Test func runViewURLEncodesOwnerAndRepo() throws {
|
feat: add Forgejo Actions runs API (#1)
New model:
- WorkflowRun — mirrors Forgejo's ActionRun schema (id, title, status,
event, indexInRepo, commitSha, prettyRef, workflowId, created, updated,
started, stopped, durationNanos, htmlUrl, triggerUser, repository).
New service:
- WorkflowService
- fetchRuns(owner:repo:status:event:ref:workflowId:runNumber:headSha:page:limit:)
backed by GET /repos/{owner}/{repo}/actions/runs.
- fetchRun(owner:repo:runId:) backed by
GET /repos/{owner}/{repo}/actions/runs/{run_id}.
Experimental:
- fetchRunView(owner:repo:runIndex:jobIndex:logCursors:) backed by
Forgejo's web-UI route POST
/{owner}/{repo}/actions/runs/{runIndex}/jobs/{jobIndex}/attempt/{N}.
This is not part of /api/v1 and may change between Forgejo releases;
it lets clients render jobs, steps, and step logs (via WorkflowRunView,
WorkflowRunViewJob, WorkflowRunViewStep, WorkflowLogCursor types).
- ForgejoClient.discoverRedirectLocation(url:) helper that resolves
Forgejo's RedirectToLatestAttempt without consuming the redirect
target — used to find the latest attempt number before POSTing.
Co-authored-by: Voislav Vasiljevski <voislav@voioo.cz>
Reviewed-on: https://codeberg.org/secana/ForgejoKit/pulls/1
Reviewed-by: secana <secana@noreply.codeberg.org>
2026-05-07 07:42:16 -07:00
|
|
|
let client = makeClient()
|
|
|
|
|
let owner = ForgejoClient.encodedPathSegment("my org")
|
|
|
|
|
let repo = ForgejoClient.encodedPathSegment("my repo")
|
|
|
|
|
let url = try client.makeURL(path: "/\(owner)/\(repo)/actions/runs/3/jobs/1")
|
|
|
|
|
#expect(url.absoluteString.contains("my%20org"))
|
|
|
|
|
#expect(url.absoluteString.contains("my%20repo"))
|
|
|
|
|
}
|
|
|
|
|
}
|