You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/10/12 05:51:30 UTC
(airavata-custos) 01/01: fix temp allocation bugs
This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch fix-temp-account-connector
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
commit a2ea84645b2826c7749d00cded116e593cedd214
Author: Yasith Jayawardana <ya...@icloud.com>
AuthorDate: Mon Oct 12 01:51:19 2026 -0400
fix temp allocation bugs
---
connectors/TempAccount/internal/handlers.go | 21 +++-
.../internal/handlers_integration_test.go | 113 +++++++++++++++++++++
2 files changed, 132 insertions(+), 2 deletions(-)
diff --git a/connectors/TempAccount/internal/handlers.go b/connectors/TempAccount/internal/handlers.go
index 1544b130a..1952ef704 100644
--- a/connectors/TempAccount/internal/handlers.go
+++ b/connectors/TempAccount/internal/handlers.go
@@ -55,6 +55,7 @@ func (h *Handlers) createTempAccount(w http.ResponseWriter, r *http.Request) {
common.WriteError(w, http.StatusBadRequest, errors.New("User type must be VIRTUAL"))
return
}
+ u.ID, u.Status = "", ""
created, err := h.coreService.CreateUser(r.Context(), &u)
@@ -138,11 +139,27 @@ func (h *Handlers) updateAllocationToTempAccount(w http.ResponseWriter, r *http.
return
}
- if m.UserID == "" || m.ComputeAllocationID == "" {
- common.WriteError(w, http.StatusBadRequest, errors.New("UserID and ComputeAllocationID must be provided"))
+ if m.ID == "" || m.UserID == "" || m.ComputeAllocationID == "" {
+ common.WriteError(w, http.StatusBadRequest, errors.New("ID, UserID and ComputeAllocationID must be provided"))
+ return
+ }
+
+ existing, err := h.coreService.GetComputeAllocationMembership(r.Context(), m.ID)
+ if err != nil {
+ common.WriteServiceError(w, err)
+ return
+ }
+ user, err := h.coreService.GetUser(r.Context(), existing.UserID)
+ if err != nil {
+ common.WriteServiceError(w, err)
+ return
+ }
+ if user.Type != models.UserTypeVirtual {
+ common.WriteError(w, http.StatusBadRequest, errors.New("User type must be VIRTUAL"))
return
}
+ m.UserID = existing.UserID
updated, err := h.coreService.UpdateComputeAllocationMembership(r.Context(), &m)
if err != nil {
diff --git a/connectors/TempAccount/internal/handlers_integration_test.go b/connectors/TempAccount/internal/handlers_integration_test.go
new file mode 100644
index 000000000..230cf925d
--- /dev/null
+++ b/connectors/TempAccount/internal/handlers_integration_test.go
@@ -0,0 +1,113 @@
+//go:build integration
+
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package internal
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/jmoiron/sqlx"
+
+ "github.com/apache/airavata-custos/internal/db"
+ "github.com/apache/airavata-custos/pkg/events"
+ "github.com/apache/airavata-custos/pkg/models"
+ "github.com/apache/airavata-custos/pkg/service"
+)
+
+func setup(t *testing.T) (*sqlx.DB, *Handlers, func(string, ...any), string) {
+ dsn := os.Getenv("CORE_TEST_DATABASE_DSN")
+ if dsn == "" {
+ t.Skip("CORE_TEST_DATABASE_DSN not set")
+ }
+ database, err := db.Open(db.Config{DSN: dsn, MaxOpenConns: 5, MaxIdleConns: 2})
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() { database.Close() })
+ if err := db.MigrateEmbedded(database); err != nil {
+ t.Fatal(err)
+ }
+ bus, err := events.New(context.Background(), database)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exec := func(q string, args ...any) {
+ if _, err := database.Exec(q, args...); err != nil {
+ t.Fatal(err)
+ }
+ }
+ orgID := uuid.NewString()
+ exec(`INSERT INTO organizations (id, originated_id, name) VALUES ($1, $2, 'Org')`, orgID, orgID)
+ return database, NewHandlers(service.New(database, bus)), exec, orgID
+}
+
+func TestUpdateAllocationKeepsMembershipOnVirtualUser(t *testing.T) {
+ for _, userType := range []string{"CLUSTER_LOCAL", "VIRTUAL"} {
+ t.Run(userType, func(t *testing.T) {
+ database, h, exec, orgID := setup(t)
+ userID, otherID, clusterID, projectID, allocID, memID := uuid.NewString(), uuid.NewString(), uuid.NewString(), uuid.NewString(), uuid.NewString(), uuid.NewString()
+ for id, typ := range map[string]string{userID: userType, otherID: "VIRTUAL"} {
+ exec(`INSERT INTO users (id, organization_id, first_name, last_name, email, type) VALUES ($1, $2, 'T', 'U', $3, $4)`, id, orgID, id, typ)
+ }
+ exec(`INSERT INTO compute_clusters (id, name) VALUES ($1, $1)`, clusterID)
+ exec(`INSERT INTO projects (id, originated_id, title, origination, project_pi_id) VALUES ($1, $1, 'P', 'TEST', $2)`, projectID, userID)
+ exec(`INSERT INTO compute_allocations (id, project_id, name, status, compute_cluster_id, start_time, end_time)
+ VALUES ($1, $2, $1, 'ACTIVE', $3, NOW(), NOW() + INTERVAL '1 year')`, allocID, projectID, clusterID)
+ exec(`INSERT INTO compute_allocation_memberships (id, compute_allocation_id, user_id, start_time, end_time, membership_status)
+ VALUES ($1, $2, $3, NOW(), NOW() + INTERVAL '1 year', 'ACTIVE')`, memID, allocID, userID)
+
+ body := `{"id":"` + memID + `","user_id":"` + otherID + `","compute_allocation_id":"` + allocID + `","membership_status":"INACTIVE"}`
+ rec := httptest.NewRecorder()
+ h.updateAllocationToTempAccount(rec, httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)))
+ var got struct {
+ UserID string `db:"user_id"`
+ Status string `db:"membership_status"`
+ }
+ if err := database.Get(&got, `SELECT user_id, membership_status FROM compute_allocation_memberships WHERE id = $1`, memID); err != nil {
+ t.Fatal(err)
+ }
+ if got.UserID != userID || userType == "CLUSTER_LOCAL" && (rec.Code != http.StatusBadRequest || got.Status != "ACTIVE") {
+ t.Errorf("got %d, user %s, status %s", rec.Code, got.UserID, got.Status)
+ }
+ })
+ }
+}
+
+func TestCreateTempAccountIgnoresClientIDAndStatus(t *testing.T) {
+ _, h, _, orgID := setup(t)
+ chosenID := uuid.NewString()
+ body := `{"id":"` + chosenID + `","status":"ACTIVE","type":"VIRTUAL","email":"` + chosenID + `@example.edu","organization_id":"` + orgID + `"}`
+ rec := httptest.NewRecorder()
+ h.createTempAccount(rec, httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body)))
+
+ var created models.User
+ if err := json.Unmarshal(rec.Body.Bytes(), &created); err != nil || rec.Code != http.StatusCreated {
+ t.Fatalf("got %d %s, want 201", rec.Code, rec.Body)
+ }
+ if created.ID == chosenID || created.Status != models.UserPending {
+ t.Errorf("created id %q status %q, want a server id and PENDING", created.ID, created.Status)
+ }
+}