package api import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "codespace/internal/process" "codespace/internal/service" "codespace/internal/workspace" ) func setupTestRouter(t *testing.T) http.Handler { t.Helper() root := t.TempDir() wsMgr := workspace.NewLocalManager(root) procMgr := process.NewManager("") wsSvc := service.NewWorkspaceService(wsMgr, procMgr, nil) fileSvc := service.NewFileService(wsMgr) procSvc := service.NewProcessService(wsMgr, procMgr, nil) return NewRouter(wsSvc, fileSvc, procSvc, nil) } func TestHealthz(t *testing.T) { router := setupTestRouter(t) req := httptest.NewRequest("GET", "/healthz", nil) rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } var body map[string]string if err := json.NewDecoder(rec.Body).Decode(&body); err != nil { t.Fatalf("decode: %v", err) } if body["status"] != "ok" { t.Errorf("status = %q, want ok", body["status"]) } } func TestCreateWorkspace(t *testing.T) { router := setupTestRouter(t) payload, _ := json.Marshal(map[string]string{"id": "user1"}) req := httptest.NewRequest("POST", "/api/workspaces", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusCreated, rec.Body.String()) } var resp map[string]string if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("decode: %v", err) } if resp["id"] != "user1" { t.Errorf("id = %q, want user1", resp["id"]) } } func TestWriteReadFile(t *testing.T) { router := setupTestRouter(t) // Create workspace first. payload, _ := json.Marshal(map[string]string{"id": "user1"}) req := httptest.NewRequest("POST", "/api/workspaces", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("create workspace: status=%d body=%s", rec.Code, rec.Body.String()) } // Write file. writePayload, _ := json.Marshal(map[string]string{"path": "main.go", "content": "package main\n"}) req = httptest.NewRequest("PUT", "/api/workspaces/user1/files/write", bytes.NewReader(writePayload)) req.Header.Set("Content-Type", "application/json") rec = httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("write file: status=%d want=%d body=%s", rec.Code, http.StatusNoContent, rec.Body.String()) } // Read file. req = httptest.NewRequest("GET", "/api/workspaces/user1/files/read?path=main.go", nil) rec = httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("read file: status=%d want=%d body=%s", rec.Code, http.StatusOK, rec.Body.String()) } var resp map[string]string if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("decode: %v", err) } if resp["content"] != "package main\n" { t.Errorf("content = %q, want 'package main\\n'", resp["content"]) } } func TestProcessStatus(t *testing.T) { router := setupTestRouter(t) // Create workspace. payload, _ := json.Marshal(map[string]string{"id": "user1"}) req := httptest.NewRequest("POST", "/api/workspaces", bytes.NewReader(payload)) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("create workspace: status=%d", rec.Code) } // Get process status. req = httptest.NewRequest("GET", "/api/workspaces/user1/process/status", nil) rec = httptest.NewRecorder() router.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String()) } var resp struct { WorkspaceID string `json:"workspaceId"` Running bool `json:"running"` } if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil { t.Fatalf("decode: %v", err) } if resp.WorkspaceID != "user1" { t.Errorf("workspaceId = %q, want user1", resp.WorkspaceID) } if resp.Running { t.Error("expected Running=false") } }