summaryrefslogtreecommitdiff
path: root/internal/task
diff options
context:
space:
mode:
Diffstat (limited to 'internal/task')
-rw-r--r--internal/task/story.go41
-rw-r--r--internal/task/story_test.go42
-rw-r--r--internal/task/task.go1
3 files changed, 84 insertions, 0 deletions
diff --git a/internal/task/story.go b/internal/task/story.go
new file mode 100644
index 0000000..536bda1
--- /dev/null
+++ b/internal/task/story.go
@@ -0,0 +1,41 @@
+package task
+
+import "time"
+
+type StoryState string
+
+const (
+ StoryPending StoryState = "PENDING"
+ StoryInProgress StoryState = "IN_PROGRESS"
+ StoryShippable StoryState = "SHIPPABLE"
+ StoryDeployed StoryState = "DEPLOYED"
+ StoryValidating StoryState = "VALIDATING"
+ StoryReviewReady StoryState = "REVIEW_READY"
+ StoryNeedsFix StoryState = "NEEDS_FIX"
+)
+
+type Story struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ ProjectID string `json:"project_id"`
+ BranchName string `json:"branch_name"`
+ DeployConfig string `json:"deploy_config"`
+ ValidationJSON string `json:"validation_json"`
+ Status StoryState `json:"status"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+var validStoryTransitions = map[StoryState]map[StoryState]bool{
+ StoryPending: {StoryInProgress: true},
+ StoryInProgress: {StoryShippable: true, StoryNeedsFix: true},
+ StoryShippable: {StoryDeployed: true},
+ StoryDeployed: {StoryValidating: true},
+ StoryValidating: {StoryReviewReady: true, StoryNeedsFix: true},
+ StoryReviewReady: {},
+ StoryNeedsFix: {StoryInProgress: true},
+}
+
+func ValidStoryTransition(from, to StoryState) bool {
+ return validStoryTransitions[from][to]
+}
diff --git a/internal/task/story_test.go b/internal/task/story_test.go
new file mode 100644
index 0000000..38d0290
--- /dev/null
+++ b/internal/task/story_test.go
@@ -0,0 +1,42 @@
+package task
+
+import "testing"
+
+func TestValidStoryTransition_Valid(t *testing.T) {
+ cases := []struct {
+ from StoryState
+ to StoryState
+ }{
+ {StoryPending, StoryInProgress},
+ {StoryInProgress, StoryShippable},
+ {StoryInProgress, StoryNeedsFix},
+ {StoryNeedsFix, StoryInProgress},
+ {StoryShippable, StoryDeployed},
+ {StoryDeployed, StoryValidating},
+ {StoryValidating, StoryReviewReady},
+ {StoryValidating, StoryNeedsFix},
+ }
+ for _, tc := range cases {
+ if !ValidStoryTransition(tc.from, tc.to) {
+ t.Errorf("expected valid transition %s → %s", tc.from, tc.to)
+ }
+ }
+}
+
+func TestValidStoryTransition_Invalid(t *testing.T) {
+ cases := []struct {
+ from StoryState
+ to StoryState
+ }{
+ {StoryPending, StoryDeployed},
+ {StoryReviewReady, StoryPending},
+ {StoryReviewReady, StoryInProgress},
+ {StoryReviewReady, StoryShippable},
+ {StoryShippable, StoryPending},
+ }
+ for _, tc := range cases {
+ if ValidStoryTransition(tc.from, tc.to) {
+ t.Errorf("expected invalid transition %s → %s", tc.from, tc.to)
+ }
+ }
+}
diff --git a/internal/task/task.go b/internal/task/task.go
index 28d65a5..ee79668 100644
--- a/internal/task/task.go
+++ b/internal/task/task.go
@@ -81,6 +81,7 @@ type Task struct {
Priority Priority `yaml:"priority" json:"priority"`
Tags []string `yaml:"tags" json:"tags"`
DependsOn []string `yaml:"depends_on" json:"depends_on"`
+ StoryID string `yaml:"-" json:"story_id,omitempty"`
State State `yaml:"-" json:"state"`
RejectionComment string `yaml:"-" json:"rejection_comment,omitempty"`
QuestionJSON string `yaml:"-" json:"question,omitempty"`