Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions internal/server/job_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package server

import "github.com/Neokil/AutoPR/internal/serverstate"

// JobAction identifies the type of background job the daemon should execute.
type JobAction = serverstate.JobAction

const (
jobRun JobAction = "run"
jobAction JobAction = "action"
jobMoveToState JobAction = "move_to_state"
jobCleanup JobAction = "cleanup_ticket"
jobCleanupDone JobAction = "cleanup_done"
jobCleanupAll JobAction = "cleanup_all"
)
2 changes: 1 addition & 1 deletion internal/server/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (s *server) setJobStatus(job serverstate.JobRecord, status, errMsg string)
RepoPath: stringPtr(job.RepoPath),
TicketNumber: stringPtr(job.TicketNumber),
JobId: stringPtr(job.ID),
Action: stringPtr(job.Action),
Action: stringPtr(string(job.Action)),
Scope: stringPtr(job.Scope),
Status: stringPtr(status),
Error: stringPtr(strings.TrimSpace(errMsg)),
Expand Down
7 changes: 0 additions & 7 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ import (
)

const (
jobRun = "run"
jobAction = "action"
jobMoveToState = "move_to_state"
jobCleanup = "cleanup_ticket"
jobCleanupDone = "cleanup_done"
jobCleanupAll = "cleanup_all"

jobQueueSize = 256
httpReadHeaderTimeout = 30 * time.Second
sectionMatchLen = 3 // full match + 2 capture groups
Expand Down
8 changes: 4 additions & 4 deletions internal/server/strict_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ func (s *server) RunTicket(ctx context.Context, request api.RunTicketRequestObje
return acceptedRunTicket(s.enqueueJob(jobRun, repoID, repoRoot, request.Id, enqueueOptions{}))
}

func (s *server) enqueueJob(action, repoID, repoPath, ticket string, opts enqueueOptions) (api.ActionAcceptedResponse, int, error) {
func (s *server) enqueueJob(action JobAction, repoID, repoPath, ticket string, opts enqueueOptions) (api.ActionAcceptedResponse, int, error) {
if action == jobRun && strings.TrimSpace(ticket) != "" {
queueErr := s.ensureQueuedTicket(repoID, repoPath, ticket)
if queueErr != nil {
Expand All @@ -408,7 +408,7 @@ func (s *server) enqueueJob(action, repoID, repoPath, ticket string, opts enqueu
RepoPath: stringPtr(repoPath),
TicketNumber: stringPtr(ticket),
JobId: stringPtr(job.ID),
Action: stringPtr(action),
Action: stringPtr(string(action)),
Scope: stringPtr(opts.scope),
Status: stringPtr("queued"),
})
Expand All @@ -417,7 +417,7 @@ func (s *server) enqueueJob(action, repoID, repoPath, ticket string, opts enqueu
return api.ActionAcceptedResponse{
Status: "accepted",
JobId: job.ID,
Action: action,
Action: string(action),
RepoId: repoID,
RepoPath: repoPath,
TicketNumber: stringPtr(ticket),
Expand All @@ -430,7 +430,7 @@ func (s *server) enqueueJob(action, repoID, repoPath, ticket string, opts enqueu
RepoPath: stringPtr(repoPath),
TicketNumber: stringPtr(ticket),
JobId: stringPtr(job.ID),
Action: stringPtr(action),
Action: stringPtr(string(action)),
Scope: stringPtr(opts.scope),
Status: stringPtr("failed"),
Error: stringPtr("job queue is full"),
Expand Down
53 changes: 53 additions & 0 deletions internal/server/strict_api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package server //nolint:testpackage // needs access to unexported enqueueJob and queuedJob internals

import (
"net/http"
"testing"

"github.com/Neokil/AutoPR/internal/api"
"github.com/Neokil/AutoPR/internal/serverstate"
)

func TestEnqueueJobPreservesActionValue(t *testing.T) {
t.Parallel()

statePath := t.TempDir() + "/state.json"
store, err := serverstate.NewStore(statePath)
if err != nil {
t.Fatalf("NewStore() error = %v", err)
}

srv := &server{
meta: store,
jobs: make(chan queuedJob, 1),
subscribers: map[string]chan api.ServerEvent{},
}

resp, code, err := srv.enqueueJob(jobCleanupAll, "repo-1", "/tmp/repo", "", enqueueOptions{scope: "all"})
if err != nil {
t.Fatalf("enqueueJob() error = %v", err)
}
if code != http.StatusAccepted {
t.Fatalf("enqueueJob() code = %d, want %d", code, http.StatusAccepted)
}
if resp.Action != string(jobCleanupAll) {
t.Fatalf("resp.Action = %q, want %q", resp.Action, jobCleanupAll)
}

stored, ok := store.GetJob(resp.JobId)
if !ok {
t.Fatalf("GetJob(%q) did not find stored job", resp.JobId)
}
if stored.Action != jobCleanupAll {
t.Fatalf("stored.Action = %q, want %q", stored.Action, jobCleanupAll)
}

select {
case queued := <-srv.jobs:
if queued.record.Action != jobCleanupAll {
t.Fatalf("queued.record.Action = %q, want %q", queued.record.Action, jobCleanupAll)
}
default:
t.Fatal("expected queued job in channel")
}
}
2 changes: 1 addition & 1 deletion internal/server/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func toTicketStateResponse(state workflowstate.State) api.TicketStateResponse {
func toJobResponse(job serverstate.JobRecord) api.JobStatusResponse {
return api.JobStatusResponse{
Id: job.ID,
Action: job.Action,
Action: string(job.Action),
RepoId: job.RepoID,
RepoPath: job.RepoPath,
TicketNumber: stringPtr(job.TicketNumber),
Expand Down
4 changes: 4 additions & 0 deletions internal/serverstate/job_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package serverstate

// JobAction identifies the type of background job the daemon should execute.
type JobAction string
4 changes: 2 additions & 2 deletions internal/serverstate/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type Data struct {
// JobRecord represents a single background job and its lifecycle timestamps.
type JobRecord struct {
ID string `json:"id"`
Action string `json:"action"`
Action JobAction `json:"action"`
RepoID string `json:"repo_id"`
RepoPath string `json:"repo_path"`
TicketNumber string `json:"ticket_number,omitempty"`
Expand Down Expand Up @@ -231,7 +231,7 @@ func (s *Store) ListTickets(repoID string) []TicketRecord {
}

// NewJob creates a new queued job record and persists it.
func (s *Store) NewJob(action, repoID, repoPath, ticketNumber, scope string) (JobRecord, error) {
func (s *Store) NewJob(action JobAction, repoID, repoPath, ticketNumber, scope string) (JobRecord, error) {
s.mu.Lock()
defer s.mu.Unlock()
id, err := randomID()
Expand Down
45 changes: 45 additions & 0 deletions internal/serverstate/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package serverstate_test

import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/Neokil/AutoPR/internal/serverstate"
)

func TestNewJobPersistsAndReloadsTypedAction(t *testing.T) {
t.Parallel()

statePath := filepath.Join(t.TempDir(), "state.json")
store, err := serverstate.NewStore(statePath)
if err != nil {
t.Fatalf("NewStore() error = %v", err)
}

job, err := store.NewJob(serverstate.JobAction("cleanup_all"), "repo-1", "/tmp/repo", "SC-1", "all")
if err != nil {
t.Fatalf("NewJob() error = %v", err)
}

raw, err := os.ReadFile(statePath)
if err != nil {
t.Fatalf("ReadFile() error = %v", err)
}
if !strings.Contains(string(raw), `"action": "cleanup_all"`) {
t.Fatalf("persisted state missing action string: %s", string(raw))
}

reloaded, err := serverstate.NewStore(statePath)
if err != nil {
t.Fatalf("reloaded NewStore() error = %v", err)
}
stored, ok := reloaded.GetJob(job.ID)
if !ok {
t.Fatalf("GetJob(%q) did not find reloaded job", job.ID)
}
if stored.Action != serverstate.JobAction("cleanup_all") {
t.Fatalf("stored.Action = %q, want %q", stored.Action, serverstate.JobAction("cleanup_all"))
}
}
Loading