diff --git a/platform/service/resources_test.go b/platform/service/resources_test.go index 6fc141d..4cfb142 100644 --- a/platform/service/resources_test.go +++ b/platform/service/resources_test.go @@ -590,6 +590,15 @@ func TestCoreServiceDeletesServerInstancesWithPasswordConfirmation(t *testing.T) if err != nil { t.Fatalf("create installing target: %v", err) } + draftTarget, err := svc.CreateServerInstanceForSession(ownerSession, domain.ServerInstance{ + ID: "server-delete-draft", + PluginID: plugin.ID, + Name: "Delete Draft Target", + State: domain.ServerInstanceStateDraft, + }) + if err != nil { + t.Fatalf("create draft target: %v", err) + } if _, err := svc.DeleteServerInstanceForSession(otherSession, ownerInstance.ID, domain.ServerDeletionRequest{Password: "secret-password"}); !errors.Is(err, ErrForbidden) { t.Fatalf("expected non-owner delete to be forbidden, got %v", err) @@ -620,6 +629,13 @@ func TestCoreServiceDeletesServerInstancesWithPasswordConfirmation(t *testing.T) if deletedAdmin.State != domain.ServerInstanceStateDeleted { t.Fatalf("expected deleted admin state, got %+v", deletedAdmin) } + deletedDraft, err := svc.DeleteServerInstanceForSession(ownerSession, draftTarget.ID, domain.ServerDeletionRequest{Password: "secret-password"}) + if err != nil { + t.Fatalf("delete draft target without run endpoint: %v", err) + } + if deletedDraft.State != domain.ServerInstanceStateDeleted || deletedDraft.RunEndpointID != "" { + t.Fatalf("expected deleted draft target without run endpoint, got %+v", deletedDraft) + } if _, err := svc.DeleteServerInstanceForSession(ownerSession, runningTarget.ID, domain.ServerDeletionRequest{Password: "secret-password"}); err == nil { t.Fatalf("expected running instance delete to fail") } else { diff --git a/platform/validator/resources.go b/platform/validator/resources.go index 2ee813b..8cebd49 100644 --- a/platform/validator/resources.go +++ b/platform/validator/resources.go @@ -986,7 +986,7 @@ func validateServerInstance(instance domain.ServerInstance, allowDeleted bool) e violations = appendRequired(violations, "id", instance.ID) violations = appendRequired(violations, "pluginId", instance.PluginID) violations = appendRequired(violations, "pluginVersion", instance.PluginVersion) - if instance.State != domain.ServerInstanceStateDraft { + if instance.State != domain.ServerInstanceStateDraft && instance.State != domain.ServerInstanceStateDeleted { violations = appendRequired(violations, "runEndpointId", instance.RunEndpointID) } violations = appendRequired(violations, "name", instance.Name) diff --git a/platform/validator/resources_test.go b/platform/validator/resources_test.go index ebd4846..3933cd7 100644 --- a/platform/validator/resources_test.go +++ b/platform/validator/resources_test.go @@ -287,6 +287,20 @@ func TestValidateServerInstanceRejectsDeletedCreateState(t *testing.T) { } } +func TestValidateStoredServerInstanceAllowsDeletedWithoutRunEndpoint(t *testing.T) { + instance := domain.ServerInstance{ + ID: "server-1", + PluginID: "server.scum", + PluginVersion: "1.0.0", + Name: "SCUM #1", + State: domain.ServerInstanceStateDeleted, + } + + if err := ValidateStoredServerInstance(instance); err != nil { + t.Fatalf("expected stored deleted instance without run endpoint to validate, got %v", err) + } +} + func TestValidateJobBoundsProgress(t *testing.T) { job := domain.Job{ ID: "job-1",