package store import ( "context" "testing" "gcy_hpc_server/internal/model" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) func newAppTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Fatalf("open sqlite: %v", err) } if err := db.AutoMigrate(&model.Application{}); err != nil { t.Fatalf("auto migrate: %v", err) } return db } func TestApplicationStore_Create_Success(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) id, err := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "gromacs", Description: "Molecular dynamics simulator", Category: "simulation", ScriptTemplate: "#!/bin/bash\nmodule load gromacs", Parameters: []byte(`[{"name":"ntasks","type":"number","required":true}]`), Scope: "system", }) if err != nil { t.Fatalf("Create() error = %v", err) } if id <= 0 { t.Errorf("Create() id = %d, want positive", id) } } func TestApplicationStore_GetByID_Success(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) id, _ := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "lammps", ScriptTemplate: "#!/bin/bash\nmodule load lammps", }) app, err := s.GetByID(context.Background(), id) if err != nil { t.Fatalf("GetByID() error = %v", err) } if app == nil { t.Fatal("GetByID() returned nil, expected application") } if app.Name != "lammps" { t.Errorf("Name = %q, want %q", app.Name, "lammps") } if app.ID != id { t.Errorf("ID = %d, want %d", app.ID, id) } } func TestApplicationStore_GetByID_NotFound(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) app, err := s.GetByID(context.Background(), 99999) if err != nil { t.Fatalf("GetByID() error = %v", err) } if app != nil { t.Error("GetByID() expected nil for not-found, got non-nil") } } func TestApplicationStore_List_Pagination(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) for i := 0; i < 5; i++ { s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "app-" + string(rune('A'+i)), ScriptTemplate: "#!/bin/bash\necho " + string(rune('A'+i)), }) } apps, total, err := s.List(context.Background(), 1, 3) if err != nil { t.Fatalf("List() error = %v", err) } if total != 5 { t.Errorf("total = %d, want 5", total) } if len(apps) != 3 { t.Errorf("len(apps) = %d, want 3", len(apps)) } apps2, total2, err := s.List(context.Background(), 2, 3) if err != nil { t.Fatalf("List() page 2 error = %v", err) } if total2 != 5 { t.Errorf("total2 = %d, want 5", total2) } if len(apps2) != 2 { t.Errorf("len(apps2) = %d, want 2", len(apps2)) } } func TestApplicationStore_Update_Success(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) id, _ := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "orig", ScriptTemplate: "#!/bin/bash\necho original", }) newName := "updated" newDesc := "updated description" err := s.Update(context.Background(), id, &model.UpdateApplicationRequest{ Name: &newName, Description: &newDesc, }) if err != nil { t.Fatalf("Update() error = %v", err) } app, _ := s.GetByID(context.Background(), id) if app.Name != "updated" { t.Errorf("Name = %q, want %q", app.Name, "updated") } if app.Description != "updated description" { t.Errorf("Description = %q, want %q", app.Description, "updated description") } } func TestApplicationStore_Update_NotFound(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) name := "nope" err := s.Update(context.Background(), 99999, &model.UpdateApplicationRequest{ Name: &name, }) if err == nil { t.Fatal("Update() expected error for not-found, got nil") } } func TestApplicationStore_Delete_Success(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) id, _ := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "to-delete", ScriptTemplate: "#!/bin/bash\necho bye", }) err := s.Delete(context.Background(), id) if err != nil { t.Fatalf("Delete() error = %v", err) } app, _ := s.GetByID(context.Background(), id) if app != nil { t.Error("GetByID() after delete returned non-nil") } } func TestApplicationStore_Delete_Idempotent(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) err := s.Delete(context.Background(), 99999) if err != nil { t.Fatalf("Delete() non-existent error = %v, want nil (idempotent)", err) } } func TestApplicationStore_Create_DuplicateName(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) _, err := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "dup-app", ScriptTemplate: "#!/bin/bash\necho 1", }) if err != nil { t.Fatalf("first Create() error = %v", err) } _, err = s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "dup-app", ScriptTemplate: "#!/bin/bash\necho 2", }) if err == nil { t.Fatal("expected error for duplicate name, got nil") } } func TestApplicationStore_Create_EmptyParameters(t *testing.T) { db := newAppTestDB(t) s := NewApplicationStore(db) id, err := s.Create(context.Background(), &model.CreateApplicationRequest{ Name: "no-params", ScriptTemplate: "#!/bin/bash\necho hello", }) if err != nil { t.Fatalf("Create() error = %v", err) } app, _ := s.GetByID(context.Background(), id) if string(app.Parameters) != "[]" { t.Errorf("Parameters = %q, want []", string(app.Parameters)) } }