Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 3 additions & 141 deletions cmd/test-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ import (
"fmt"
"log"
"net"
"strings"

eventsv1 "github.com/innabox/fulfillment-common/api/events/v1"
ffv1 "github.com/innabox/fulfillment-common/api/fulfillment/v1"
metadatav1 "github.com/innabox/fulfillment-common/api/metadata/v1"
sharedv1 "github.com/innabox/fulfillment-common/api/shared/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
Expand Down Expand Up @@ -85,140 +83,6 @@ func (s *clustersServer) List(ctx context.Context, request *ffv1.ClustersListReq
return &ffv1.ClustersListResponse{}, nil
}

// Simple mock compute instances server for testing
type computeInstancesServer struct {
ffv1.UnimplementedComputeInstancesServer
}

func (s *computeInstancesServer) Create(ctx context.Context, request *ffv1.ComputeInstancesCreateRequest) (*ffv1.ComputeInstancesCreateResponse, error) {
instance := request.GetObject()

// Set mock ID and state if not already set
if instance.Id == "" {
instance.Id = "ci-mock-12345"
}
if instance.Status == nil {
instance.Status = &ffv1.ComputeInstanceStatus{
State: ffv1.ComputeInstanceState_COMPUTE_INSTANCE_STATE_PROGRESSING,
}
}

log.Printf("Created compute instance: %s (name: %s, template: %s)",
instance.Id,
instance.GetMetadata().GetName(),
instance.GetSpec().GetTemplate())

return &ffv1.ComputeInstancesCreateResponse{Object: instance}, nil
}

func (s *computeInstancesServer) Get(ctx context.Context, request *ffv1.ComputeInstancesGetRequest) (*ffv1.ComputeInstancesGetResponse, error) {
// Return a mock instance
instance := &ffv1.ComputeInstance{
Id: request.Id,
Metadata: &sharedv1.Metadata{
Name: "mock-instance",
},
Spec: &ffv1.ComputeInstanceSpec{
Template: "small-instance",
},
Status: &ffv1.ComputeInstanceStatus{
State: ffv1.ComputeInstanceState_COMPUTE_INSTANCE_STATE_READY,
IpAddress: "192.168.1.100",
},
}

log.Printf("Retrieved compute instance: %s", request.Id)
return &ffv1.ComputeInstancesGetResponse{Object: instance}, nil
}

func (s *computeInstancesServer) List(ctx context.Context, request *ffv1.ComputeInstancesListRequest) (*ffv1.ComputeInstancesListResponse, error) {
// Return a mock list with one instance
instance := &ffv1.ComputeInstance{
Id: "ci-mock-12345",
Metadata: &sharedv1.Metadata{
Name: "mock-instance",
},
Spec: &ffv1.ComputeInstanceSpec{
Template: "small-instance",
},
Status: &ffv1.ComputeInstanceStatus{
State: ffv1.ComputeInstanceState_COMPUTE_INSTANCE_STATE_READY,
IpAddress: "192.168.1.100",
},
}

size := int32(1)
total := int32(1)
log.Printf("Listed compute instances")
return &ffv1.ComputeInstancesListResponse{
Items: []*ffv1.ComputeInstance{instance},
Size: &size,
Total: &total,
}, nil
}

// Simple mock compute instance templates server
type computeInstanceTemplatesServer struct {
ffv1.UnimplementedComputeInstanceTemplatesServer
}

func (s *computeInstanceTemplatesServer) Get(ctx context.Context, request *ffv1.ComputeInstanceTemplatesGetRequest) (*ffv1.ComputeInstanceTemplatesGetResponse, error) {
// Return a mock template
template := &ffv1.ComputeInstanceTemplate{
Id: request.Id,
Metadata: &sharedv1.Metadata{
Name: request.Id,
},
}

log.Printf("Retrieved compute instance template: %s", request.Id)
return &ffv1.ComputeInstanceTemplatesGetResponse{Object: template}, nil
}

func (s *computeInstanceTemplatesServer) List(ctx context.Context, request *ffv1.ComputeInstanceTemplatesListRequest) (*ffv1.ComputeInstanceTemplatesListResponse, error) {
// All available templates
allTemplates := []*ffv1.ComputeInstanceTemplate{
{
Id: "tpl-small-001",
Metadata: &sharedv1.Metadata{
Name: "small-instance",
},
},
{
Id: "tpl-large-001",
Metadata: &sharedv1.Metadata{
Name: "large-instance",
},
},
}

// Apply filter if provided (simple string matching for mock purposes)
filter := request.GetFilter()
var templates []*ffv1.ComputeInstanceTemplate

if filter != "" {
// Simple filter: check if filter contains the template ID or name
// This is a mock implementation - real server would parse CEL expressions
for _, tmpl := range allTemplates {
// Check if filter mentions this template's ID or name
if strings.Contains(filter, tmpl.Id) || strings.Contains(filter, tmpl.GetMetadata().GetName()) {
templates = append(templates, tmpl)
}
}
} else {
templates = allTemplates
}

size := int32(len(templates))
total := int32(len(templates))
log.Printf("Listed compute instance templates (filter: %q, matches: %d)", filter, len(templates))
return &ffv1.ComputeInstanceTemplatesListResponse{
Items: templates,
Size: &size,
Total: &total,
}, nil
}

// Dummy metadata server - required for login
type metadataServer struct {
metadatav1.UnimplementedMetadataServer
Expand All @@ -234,12 +98,12 @@ func main() {
scenarioFile := flag.String("scenario", defaultScenarioFile, "Path to event scenario YAML file")
flag.Parse()

// Load scenario from file
// Load event scenario from file
scenario, err := testing.LoadScenarioFromFile(*scenarioFile)
if err != nil {
log.Fatalf("Failed to load scenario from %s: %v", *scenarioFile, err)
log.Fatalf("Failed to load event scenario from %s: %v", *scenarioFile, err)
}
log.Printf("Loaded scenario: %s - %s", scenario.Name, scenario.Description)
log.Printf("Loaded event scenario: %s - %s", scenario.Name, scenario.Description)

listener, err := net.Listen("tcp", "127.0.0.1:"+serverPort)
if err != nil {
Expand All @@ -255,8 +119,6 @@ func main() {
eventsv1.RegisterEventsServer(grpcServer, &loggingEventsServer{EventsServerFuncs: eventsServerFuncs})

ffv1.RegisterClustersServer(grpcServer, &clustersServer{})
ffv1.RegisterComputeInstancesServer(grpcServer, &computeInstancesServer{})
ffv1.RegisterComputeInstanceTemplatesServer(grpcServer, &computeInstanceTemplatesServer{})
metadatav1.RegisterMetadataServer(grpcServer, &metadataServer{})

// Register health service
Expand Down
156 changes: 156 additions & 0 deletions internal/cmd/create/computeinstance/computeinstance_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
Copyright (c) 2025 Red Hat Inc.

Licensed 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 computeinstance

import (
"context"

ffv1 "github.com/innabox/fulfillment-common/api/fulfillment/v1"
sharedv1 "github.com/innabox/fulfillment-common/api/shared/v1"
. "github.com/onsi/ginkgo/v2/dsl/core"
. "github.com/onsi/gomega"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/innabox/fulfillment-cli/internal/testing"
)

var _ = Describe("Compute Instance E2E", func() {
var (
ctx context.Context
cancel context.CancelFunc
server *testing.Server
conn *grpc.ClientConn
instanceClient ffv1.ComputeInstancesClient
templateClient ffv1.ComputeInstanceTemplatesClient
)

BeforeEach(func() {
var err error

// Create cancellable context
ctx, cancel = context.WithCancel(context.Background())
DeferCleanup(cancel)

// Create test server
server = testing.NewServer()
DeferCleanup(server.Stop)

// Create and load compute instance scenario
scenario := &testing.ComputeInstanceScenario{
Name: "e2e-test-scenario",
Description: "E2E test scenario for compute instances",
Templates: []*testing.TemplateData{
{
ID: "tpl-test-001",
Name: "test-template",
Title: "Test Template",
Description: "A test compute instance template",
},
},
Instances: []*testing.InstanceData{
{
ID: "ci-existing-001",
Name: "existing-instance",
Template: "tpl-test-001",
State: ffv1.ComputeInstanceState_COMPUTE_INSTANCE_STATE_READY,
IPAddress: "192.168.1.100",
},
},
}

// Create and register mock compute instance server
ciServer := testing.NewMockComputeInstancesServer(scenario)
ffv1.RegisterComputeInstancesServer(server.Registrar(), ciServer)

// Create and register mock compute instance templates server
citServer := testing.NewMockComputeInstanceTemplatesServer(scenario)
ffv1.RegisterComputeInstanceTemplatesServer(server.Registrar(), citServer)

// Start the server
server.Start()

// Create client connection
conn, err = grpc.NewClient(
server.Address(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
Expect(err).ToNot(HaveOccurred())
DeferCleanup(conn.Close)

// Create clients
instanceClient = ffv1.NewComputeInstancesClient(conn)
templateClient = ffv1.NewComputeInstanceTemplatesClient(conn)
})

It("should create, list, and delete a compute instance", func() {
// Step 1: List templates to get a valid template
listTemplatesResp, err := templateClient.List(ctx, &ffv1.ComputeInstanceTemplatesListRequest{})
Expect(err).ToNot(HaveOccurred())
Expect(listTemplatesResp.Items).ToNot(BeEmpty())
template := listTemplatesResp.Items[0]

// Step 2: List existing instances (should have 1)
listResp, err := instanceClient.List(ctx, &ffv1.ComputeInstancesListRequest{})
Expect(err).ToNot(HaveOccurred())
initialCount := len(listResp.Items)
Expect(initialCount).To(Equal(1))

// Step 3: Create a new compute instance
createResp, err := instanceClient.Create(ctx, &ffv1.ComputeInstancesCreateRequest{
Object: &ffv1.ComputeInstance{
Metadata: &sharedv1.Metadata{
Name: "new-test-instance",
},
Spec: &ffv1.ComputeInstanceSpec{
Template: template.Id,
},
},
})
Expect(err).ToNot(HaveOccurred())
Expect(createResp.Object).ToNot(BeNil())
Expect(createResp.Object.Id).ToNot(BeEmpty())
createdID := createResp.Object.Id

// Step 4: List instances again (should have 2 now)
listResp, err = instanceClient.List(ctx, &ffv1.ComputeInstancesListRequest{})
Expect(err).ToNot(HaveOccurred())
Expect(len(listResp.Items)).To(Equal(initialCount + 1))

// Step 5: Get the created instance by ID
getResp, err := instanceClient.Get(ctx, &ffv1.ComputeInstancesGetRequest{
Id: createdID,
})
Expect(err).ToNot(HaveOccurred())
Expect(getResp.Object.Id).To(Equal(createdID))
Expect(getResp.Object.Spec.Template).To(Equal(template.Id))

// Step 6: Delete the created instance
_, err = instanceClient.Delete(ctx, &ffv1.ComputeInstancesDeleteRequest{
Id: createdID,
})
Expect(err).ToNot(HaveOccurred())

// Step 7: List instances again (should be back to initial count)
listResp, err = instanceClient.List(ctx, &ffv1.ComputeInstancesListRequest{})
Expect(err).ToNot(HaveOccurred())
Expect(len(listResp.Items)).To(Equal(initialCount))

// Step 8: Verify the instance was deleted (Get should fail)
_, err = instanceClient.Get(ctx, &ffv1.ComputeInstancesGetRequest{
Id: createdID,
})
Expect(err).To(HaveOccurred())
})
})
26 changes: 26 additions & 0 deletions internal/cmd/create/computeinstance/computeinstance_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright (c) 2025 Red Hat Inc.

Licensed 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 computeinstance

import (
"testing"

. "github.com/onsi/ginkgo/v2/dsl/core"
. "github.com/onsi/gomega"
)

func TestComputeInstance(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Compute Instance Suite")
}
Loading
Loading