package vm import ( "context" "encoding/json" "errors" "net/http" "net/http/httptest" "testing" "time" "proxui/backend/internal/auth" ) func TestListProjectVMsReturnsVisibleVMs(t *testing.T) { repository := &stubRepository{ listFound: true, vms: []VM{{ ID: "vm-1", ProjectID: "project-1", TenantID: "tenant-1", Name: "web-1", Status: "running", MembershipRole: "viewer", CreatedAt: time.Date(2026, 6, 11, 12, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 6, 11, 12, 0, 0, 0, time.UTC), }}, } handler := NewHandler(repository) req := requestWithPrincipal(http.MethodGet, "/projects/project-1/vms") req.SetPathValue("projectID", "project-1") rec := httptest.NewRecorder() handler.ListProjectVMs(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } if repository.profileID != "profile-1" { t.Fatalf("profileID = %q, want profile-1", repository.profileID) } if repository.projectID != "project-1" { t.Fatalf("projectID = %q, want project-1", repository.projectID) } var response struct { Data []VM `json:"data"` } if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil { t.Fatalf("unmarshal response: %v", err) } if len(response.Data) != 1 { t.Fatalf("len(data) = %d, want 1", len(response.Data)) } if response.Data[0].ID != "vm-1" { t.Fatalf("VM ID = %q, want vm-1", response.Data[0].ID) } } func TestListProjectVMsReturnsNotFoundForInaccessibleProject(t *testing.T) { handler := NewHandler(&stubRepository{listFound: false}) req := requestWithPrincipal(http.MethodGet, "/projects/project-1/vms") req.SetPathValue("projectID", "project-1") rec := httptest.NewRecorder() handler.ListProjectVMs(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("status = %d, want %d", rec.Code, http.StatusNotFound) } } func TestGetVMReturnsVisibleVM(t *testing.T) { repository := &stubRepository{ getFound: true, vm: VM{ ID: "vm-1", TenantID: "tenant-1", Name: "web-1", Status: "running", }, } handler := NewHandler(repository) req := requestWithPrincipal(http.MethodGet, "/vms/vm-1") req.SetPathValue("vmID", "vm-1") rec := httptest.NewRecorder() handler.GetVM(rec, req) if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } if repository.vmID != "vm-1" { t.Fatalf("vmID = %q, want vm-1", repository.vmID) } } func TestGetVMReturnsNotFoundForInvisibleVM(t *testing.T) { handler := NewHandler(&stubRepository{getFound: false}) req := requestWithPrincipal(http.MethodGet, "/vms/vm-1") req.SetPathValue("vmID", "vm-1") rec := httptest.NewRecorder() handler.GetVM(rec, req) if rec.Code != http.StatusNotFound { t.Fatalf("status = %d, want %d", rec.Code, http.StatusNotFound) } } func TestGetVMReturnsServerError(t *testing.T) { handler := NewHandler(&stubRepository{err: errors.New("db failed")}) req := requestWithPrincipal(http.MethodGet, "/vms/vm-1") req.SetPathValue("vmID", "vm-1") rec := httptest.NewRecorder() handler.GetVM(rec, req) if rec.Code != http.StatusInternalServerError { t.Fatalf("status = %d, want %d", rec.Code, http.StatusInternalServerError) } } func requestWithPrincipal(method string, target string) *http.Request { req := httptest.NewRequest(method, target, nil) return req.WithContext(auth.ContextWithPrincipal(req.Context(), auth.Principal{ Subject: "profile-1", Email: "user@example.test", Role: "authenticated", })) } type stubRepository struct { profileID string projectID string vmID string vms []VM vm VM listFound bool getFound bool err error } func (s *stubRepository) ListProjectVMs(_ context.Context, profileID string, projectID string) ([]VM, bool, error) { s.profileID = profileID s.projectID = projectID return s.vms, s.listFound, s.err } func (s *stubRepository) GetVM(_ context.Context, profileID string, vmID string) (VM, bool, error) { s.profileID = profileID s.vmID = vmID return s.vm, s.getFound, s.err } func (s *stubRepository) GetProjectInfo(_ context.Context, profileID string, projectID string) (ProjectInfo, bool, error) { return ProjectInfo{}, false, nil } func (s *stubRepository) CheckQuota(_ context.Context, _ string) (QuotaInfo, error) { return QuotaInfo{}, nil } func (s *stubRepository) ReserveNextVMID(_ context.Context, _ string) (int, error) { return 0, nil } func (s *stubRepository) InsertVM(_ context.Context, _ InsertVMRecord) (VM, error) { return VM{}, nil }