package httpapi import ( "bytes" "context" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "inbox/internal/app/workspaceprovision" "inbox/internal/app/workspaceruntime" "inbox/internal/base/timeutil" "inbox/internal/domain/lane" "inbox/internal/domain/workspace" sqlitestore "inbox/internal/store/sqlite" ) type fakeWorkspaceProvisioner struct { provisionReq workspaceprovision.ProvisionRequest provisionWorkspace workspace.Workspace provisionRuntime workspaceruntime.Runtime } func (f *fakeWorkspaceProvisioner) Provision(_ context.Context, req workspaceprovision.ProvisionRequest) (workspace.Workspace, workspaceruntime.Runtime, error) { f.provisionReq = req return f.provisionWorkspace, f.provisionRuntime, nil } func (f *fakeWorkspaceProvisioner) Ensure(_ context.Context, _ string) (workspace.Workspace, workspaceruntime.Runtime, error) { return f.provisionWorkspace, f.provisionRuntime, nil } func (f *fakeWorkspaceProvisioner) EnsureLane(_ context.Context, _ string) (lane.Record, error) { return lane.Record{}, nil } func (f *fakeWorkspaceProvisioner) StopLane(_ context.Context, _ string) (lane.Record, error) { return lane.Record{}, nil } func TestProvisionWorkspaceRoute(t *testing.T) { clock := timeutil.FixedClock{Time: time.Date(2026, 3, 13, 12, 0, 0, 0, time.UTC)} store, err := sqlitestore.OpenInMemory(clock) if err != nil { t.Fatalf("OpenInMemory() error = %v", err) } defer store.Close() fake := &fakeWorkspaceProvisioner{ provisionWorkspace: workspace.Workspace{ ID: "ws_1", ProjectID: "proj_1", Slug: "blog", Name: "blog", RootPath: "/tmp/worktrees/blog", BaseBranch: "master", WorktreeBranch: "worktree/blog", RuntimeBackend: "host", Status: "active", ProvisionState: "ready", LastProvisionedAt: "2026-03-13T12:00:00Z", ContainerState: "running", }, } handler := NewHandlerWithOptions(store, clock, HandlerOptions{ WorkspaceProvision: fake, WorkspaceRuntime: fake, }) body, err := json.Marshal(map[string]any{ "project_dir": "/tmp/source-blog", "name": "blog", }) if err != nil { t.Fatalf("json.Marshal() error = %v", err) } req := httptest.NewRequest(http.MethodPost, "/api/v2/workspaces/provision", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } if fake.provisionReq.ProjectDir != "/tmp/source-blog" || fake.provisionReq.Name != "blog" { t.Fatalf("unexpected provision request: %#v", fake.provisionReq) } var payload struct { Workspace workspace.Workspace `json:"workspace"` } if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("json.Unmarshal() error = %v body=%s", err, rec.Body.String()) } if payload.Workspace.ID != "ws_1" { t.Fatalf("unexpected response payload: %#v", payload) } }