Files
kandev/apps/backend/internal/task/service/service_office_integration_test.go
T
2026-05-16 20:07:57 +01:00

111 lines
3.8 KiB
Go

package service
import (
"context"
"path/filepath"
"testing"
"github.com/jmoiron/sqlx"
"github.com/kandev/kandev/internal/common/logger"
"github.com/kandev/kandev/internal/db"
officesqlite "github.com/kandev/kandev/internal/office/repository/sqlite"
"github.com/kandev/kandev/internal/task/models"
"github.com/kandev/kandev/internal/task/repository"
"github.com/kandev/kandev/internal/worktree"
)
// createOfficeIntegrationService spins up a Service against a DB that has
// BOTH task and office migrations applied — mirroring production startup.
// The office migration adds CHECK constraints (notably on tasks.priority)
// and toggles foreign_keys=ON, so any code path that lands a row through
// the task service is exercised against the real schema.
func createOfficeIntegrationService(t *testing.T) *Service {
t.Helper()
tmpDir := t.TempDir()
dbConn, err := db.OpenSQLite(filepath.Join(tmpDir, "test.db"))
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
sqlxDB := sqlx.NewDb(dbConn, "sqlite3")
repo, cleanup, err := repository.Provide(sqlxDB, sqlxDB, nil)
if err != nil {
t.Fatalf("task repository: %v", err)
}
if _, err := worktree.NewSQLiteStore(sqlxDB, sqlxDB); err != nil {
t.Fatalf("worktree store: %v", err)
}
if _, err := officesqlite.NewWithDB(sqlxDB, sqlxDB, nil); err != nil {
t.Fatalf("office migrations: %v", err)
}
t.Cleanup(func() {
_ = sqlxDB.Close()
_ = cleanup()
})
log, _ := logger.NewLogger(logger.LoggingConfig{
Level: "error", Format: "json", OutputPath: "stdout",
})
return NewService(Repos{
Workspaces: repo,
Tasks: repo,
TaskRepos: repo,
Workflows: repo,
Messages: repo,
Turns: repo,
Sessions: repo,
GitSnapshots: repo,
RepoEntities: repo,
Executors: repo,
Environments: repo,
TaskEnvironments: repo,
Reviews: repo,
}, NewMockEventBus(), log, RepositoryDiscoveryConfig{})
}
// TestIntegration_CreateTask_OnboardingShape pins the regression that
// exposed the priority CHECK constraint bug end-to-end: the onboarding
// adapter calls CreateTask with no Priority field set (and an
// "onboarding" Origin that flags it as an office task). Before the
// buildTask default landed, the resulting INSERT failed against the
// CHECK constraint added by the office priority migration. This test
// runs both migrations and asserts the row lands cleanly.
func TestIntegration_CreateTask_OnboardingShape(t *testing.T) {
svc := createOfficeIntegrationService(t)
ctx := context.Background()
// The onboarding adapter (cmd/kandev/adapters.go) sends a request
// without setting Priority and with Origin = "onboarding". Replicate
// the shape exactly. Workflow is supplied directly so we don't have
// to wire the workspace office-workflow mapping for this test.
_ = svc.workspaces.CreateWorkspace(ctx, &models.Workspace{
ID: "ws-1", Name: "Workspace",
})
_ = svc.workflows.CreateWorkflow(ctx, &models.Workflow{
ID: "wf-1", WorkspaceID: "ws-1", Name: "Office",
})
task, err := svc.CreateTask(ctx, &CreateTaskRequest{
WorkspaceID: "ws-1",
WorkflowID: "wf-1",
Title: "First task from onboarding",
Description: "What the CEO would receive",
ProjectID: "proj-onboarding",
AssigneeAgentProfileID: "agent-ceo",
Origin: "onboarding",
})
if err != nil {
t.Fatalf("CreateTask should succeed against the real office schema, got %v", err)
}
if task.Priority != "medium" {
t.Errorf("expected default priority 'medium', got %q", task.Priority)
}
// Confirm the row actually landed and round-trips out of the DB.
got, err := svc.tasks.GetTask(ctx, task.ID)
if err != nil {
t.Fatalf("read back task: %v", err)
}
if got.Priority != "medium" {
t.Errorf("persisted priority should be 'medium', got %q", got.Priority)
}
}