fix: генерить task_tag (UUID) при создании задачи; защита feat/' при пустом tag
All checks were successful
CI / test (push) Successful in 46s
CI / build-and-package (amd64, linux) (push) Successful in 44s
CI / build-and-package (amd64, windows) (push) Successful in 41s

This commit is contained in:
Hermes
2026-08-16 23:07:30 +05:00
parent 35d9a74ef1
commit c8cd425b29
4 changed files with 75 additions and 6 deletions

View File

@@ -49,6 +49,35 @@ func TestCreateAndGetTask(t *testing.T) {
}
}
func TestCreateTask_AutoTag(t *testing.T) {
s, ctx := setupTestDB(t)
// без TaskTag — должен сгенериться стабильный UUID (нужен для feat/<tag>)
task := &Task{ChatID: "tg://auto-tag", Title: "No tag"}
id, err := s.CreateTask(ctx, task)
if err != nil {
t.Fatalf("CreateTask: %v", err)
}
if task.TaskTag == "" {
t.Fatal("expected TaskTag to be generated when empty")
}
got, err := s.GetTask(ctx, id)
if err != nil {
t.Fatalf("GetTask: %v", err)
}
if got.TaskTag != task.TaskTag {
t.Fatalf("TaskTag = %q, want stable %q", got.TaskTag, task.TaskTag)
}
// стабильность: на одном инстансе повторный CreateTask с тем же тегом даёт другой УИД
task2 := &Task{ChatID: "tg://abc-tag2", Title: "No tag 2"}
id2, err := s.CreateTask(ctx, task2)
if err != nil {
t.Fatalf("CreateTask2: %v", err)
}
if got2, _ := s.GetTask(ctx, id2); got2.TaskTag == got.TaskTag {
t.Fatalf("TaskTag должен быть уникальным, оба = %q", got.TaskTag)
}
}
func TestGetTaskNotFound(t *testing.T) {
s, ctx := setupTestDB(t)
_, err := s.GetTask(ctx, 999)
@@ -236,4 +265,4 @@ func IsNotFound(err error) bool {
func IsInvalidStatus(err error) bool {
return errors.Is(err, ErrInvalidStatus)
}
}

View File

@@ -2,13 +2,31 @@ package storage
import (
"context"
"crypto/rand"
"database/sql"
"encoding/hex"
"fmt"
"time"
)
// newTaskTag генерирует стабильный UUID задачи (task_tag) — он же ложится в
// feature-ветку git (feat/<taskTag>). Генерится при создании и НЕ меняется.
func newTaskTag() string {
b := make([]byte, 8)
if _, err := rand.Read(b); err != nil {
// крипто-rand недоступен — fallback по времени
return hex.EncodeToString([]byte(time.Now().Format("150405.000000000")))
}
return hex.EncodeToString(b)
}
// CreateTask создаёт задачу со статусом draft. Возвращает её ID.
// chat_id и task_tag передаются извне (в диалоге — chat.Address + uuid).
// chat_id и task_tag передаются извне; если task_tag не задан — генерится уник
// UUID (стабильный на всё время жизни, нужен для feature-ветки feat/<tag>).
func (s *Storage) CreateTask(ctx context.Context, t *Task) (int64, error) {
if t.TaskTag == "" {
t.TaskTag = newTaskTag()
}
now := Now()
res, err := s.db.ExecContext(ctx, `
INSERT INTO tasks (chat_id, title, goal, repo, repos, why, ac, task_tag, status, created_at, updated_at)
@@ -161,4 +179,4 @@ func (s *Storage) DeleteTask(ctx context.Context, id int64) error {
return fmt.Errorf("%w: task %d", ErrNotFound, id)
}
return nil
}
}