chore(repo): reinitialize repository
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package workflowrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"inbox/internal/app/runtimeconfig"
|
||||
"inbox/internal/base/timeutil"
|
||||
"inbox/internal/domain/workflow"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
CreateWorkflowRun(ctx context.Context, value workflow.Run) (workflow.Run, error)
|
||||
GetWorkflowRun(ctx context.Context, runID string) (workflow.Run, error)
|
||||
UpdateWorkflowRun(ctx context.Context, value workflow.Run) (workflow.Run, error)
|
||||
ListWorkflowRunsByTopic(ctx context.Context, topicID string) ([]workflow.Run, error)
|
||||
ListWorkflowRunLogs(ctx context.Context, runID string, afterSeq int) ([]workflow.RunLog, error)
|
||||
AppendWorkflowRunLog(ctx context.Context, value workflow.RunLog) (workflow.RunLog, error)
|
||||
}
|
||||
|
||||
type RuntimeResolver interface {
|
||||
ResolveRole(ctx context.Context, workspaceID, roleName string) (runtimeconfig.ResolvedRole, error)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
repo Repository
|
||||
resolver RuntimeResolver
|
||||
clock timeutil.Clock
|
||||
}
|
||||
|
||||
type Patch struct {
|
||||
Status *workflow.RunStatus
|
||||
ReplyMessageID *string
|
||||
ExitCode *int
|
||||
CompletedAt *string
|
||||
ErrorMessage *string
|
||||
CommandJSON *string
|
||||
}
|
||||
|
||||
func NewService(repo Repository, resolver RuntimeResolver, clock timeutil.Clock) *Service {
|
||||
if clock == nil {
|
||||
clock = timeutil.SystemClock{}
|
||||
}
|
||||
return &Service{
|
||||
repo: repo,
|
||||
resolver: resolver,
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Start(ctx context.Context, run workflow.Run) (workflow.Run, error) {
|
||||
resolved, err := s.resolver.ResolveRole(ctx, run.WorkspaceID, run.RoleName)
|
||||
if err != nil {
|
||||
return workflow.Run{}, fmt.Errorf("resolve runtime config for %s: %w", run.RoleName, err)
|
||||
}
|
||||
snapshot, err := resolved.SnapshotJSON()
|
||||
if err != nil {
|
||||
return workflow.Run{}, err
|
||||
}
|
||||
run.ConfigSnapshotJSON = snapshot
|
||||
if run.Status == "" {
|
||||
run.Status = workflow.RunStatusRunning
|
||||
}
|
||||
return s.repo.CreateWorkflowRun(ctx, run)
|
||||
}
|
||||
|
||||
func (s *Service) ListByTopic(ctx context.Context, topicID string) ([]workflow.Run, error) {
|
||||
return s.repo.ListWorkflowRunsByTopic(ctx, topicID)
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, runID string) (workflow.Run, error) {
|
||||
return s.repo.GetWorkflowRun(ctx, runID)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, value workflow.Run) (workflow.Run, error) {
|
||||
if value.Status != workflow.RunStatusRunning && value.CompletedAt == "" {
|
||||
value.CompletedAt = timeutil.FormatRFC3339(s.clock.Now())
|
||||
}
|
||||
return s.repo.UpdateWorkflowRun(ctx, value)
|
||||
}
|
||||
|
||||
func (s *Service) Patch(ctx context.Context, runID string, patch Patch) (workflow.Run, error) {
|
||||
current, err := s.repo.GetWorkflowRun(ctx, runID)
|
||||
if err != nil {
|
||||
return workflow.Run{}, err
|
||||
}
|
||||
if patch.Status != nil {
|
||||
current.Status = *patch.Status
|
||||
}
|
||||
if patch.ReplyMessageID != nil {
|
||||
current.ReplyMessageID = *patch.ReplyMessageID
|
||||
}
|
||||
if patch.ExitCode != nil {
|
||||
current.ExitCode = *patch.ExitCode
|
||||
}
|
||||
if patch.CompletedAt != nil {
|
||||
current.CompletedAt = *patch.CompletedAt
|
||||
}
|
||||
if patch.ErrorMessage != nil {
|
||||
current.ErrorMessage = *patch.ErrorMessage
|
||||
}
|
||||
if patch.CommandJSON != nil {
|
||||
current.CommandJSON = *patch.CommandJSON
|
||||
}
|
||||
return s.Update(ctx, current)
|
||||
}
|
||||
|
||||
func (s *Service) ListLogs(ctx context.Context, runID string, afterSeq int) ([]workflow.RunLog, error) {
|
||||
if _, err := s.repo.GetWorkflowRun(ctx, runID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.ListWorkflowRunLogs(ctx, runID, afterSeq)
|
||||
}
|
||||
|
||||
func (s *Service) AppendLog(ctx context.Context, value workflow.RunLog) (workflow.RunLog, error) {
|
||||
if _, err := s.repo.GetWorkflowRun(ctx, value.RunID); err != nil {
|
||||
return workflow.RunLog{}, err
|
||||
}
|
||||
return s.repo.AppendWorkflowRunLog(ctx, value)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package workflowrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"inbox/internal/app/runtimeconfig"
|
||||
"inbox/internal/base/timeutil"
|
||||
"inbox/internal/domain/role"
|
||||
"inbox/internal/domain/workflow"
|
||||
sqlitestore "inbox/internal/store/sqlite"
|
||||
)
|
||||
|
||||
func TestStartCreatesRunWithConfigSnapshot(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
clock := timeutil.FixedClock{Time: time.Date(2026, 3, 13, 17, 0, 0, 0, time.UTC)}
|
||||
store, err := sqlitestore.OpenInMemory(clock)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenInMemory() error = %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
workspaceRoot := t.TempDir()
|
||||
now := timeutil.FormatRFC3339(clock.Now())
|
||||
if _, err := store.DB().Exec(`
|
||||
INSERT INTO projects(id, slug, name, root_path, default_branch, status, created_at, updated_at)
|
||||
VALUES('proj_1', 'proj', 'Project', ?, 'main', 'active', ?, ?)
|
||||
`, workspaceRoot, now, now); err != nil {
|
||||
t.Fatalf("insert project: %v", err)
|
||||
}
|
||||
if _, err := store.DB().Exec(`
|
||||
INSERT INTO workspaces(id, project_id, slug, name, root_path, base_branch, worktree_branch, runtime_backend, status, created_at, updated_at)
|
||||
VALUES('ws_1', 'proj_1', 'main', 'Main', ?, 'main', 'worktree/main', 'local', 'active', ?, ?)
|
||||
`, workspaceRoot, now, now); err != nil {
|
||||
t.Fatalf("insert workspace: %v", err)
|
||||
}
|
||||
if _, err := store.UpsertRole(ctx, role.Definition{
|
||||
Name: "backend",
|
||||
Title: "Backend",
|
||||
IsEnabled: true,
|
||||
IsBuiltin: true,
|
||||
}, "seed"); err != nil {
|
||||
t.Fatalf("UpsertRole() error = %v", err)
|
||||
}
|
||||
if _, err := store.UpsertRoleConfig(ctx, role.Config{
|
||||
RoleName: "backend",
|
||||
ConfigTOML: "model = \"gpt-5.4\"",
|
||||
AuthJSON: "{\"OPENAI_API_KEY\":\"token-1\"}",
|
||||
}, "seed"); err != nil {
|
||||
t.Fatalf("UpsertRoleConfig() error = %v", err)
|
||||
}
|
||||
if _, err := store.DB().Exec(`
|
||||
INSERT INTO topics(id, workspace_id, slug, title, space, status, created_at, updated_at)
|
||||
VALUES('topic_1', 'ws_1', 'signup', 'Signup', 'workflow', 'execution', ?, ?)
|
||||
`, now, now); err != nil {
|
||||
t.Fatalf("insert topic: %v", err)
|
||||
}
|
||||
|
||||
resolver := runtimeconfig.NewService(store, store, clock)
|
||||
service := NewService(store, resolver, clock)
|
||||
run, err := service.Start(ctx, workflow.Run{
|
||||
WorkspaceID: "ws_1",
|
||||
TopicID: "topic_1",
|
||||
RoleName: "backend",
|
||||
Stage: workflow.StageExecution,
|
||||
Mode: "once",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error = %v", err)
|
||||
}
|
||||
if run.ConfigSnapshotJSON == "" || run.ConfigSnapshotJSON == "{}" {
|
||||
t.Fatalf("expected non-empty config snapshot, got %q", run.ConfigSnapshotJSON)
|
||||
}
|
||||
if run.Status != workflow.RunStatusRunning {
|
||||
t.Fatalf("expected running status, got %q", run.Status)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user