package service import ( "context" "errors" "testing" "time" persistencemock "github.com/ocdp/cluster-service/internal/adapter/output/persistence/mock" "github.com/ocdp/cluster-service/internal/domain/entity" "github.com/ocdp/cluster-service/internal/domain/repository" "github.com/ocdp/cluster-service/internal/pkg/authz" ) func TestDeleteInstanceIgnoresMissingRelease(t *testing.T) { principal := &authz.Principal{UserID: "user-1", Username: "tester", Role: authz.RoleUser, WorkspaceID: entity.DefaultWorkspaceID} ctx := authz.WithPrincipal(context.Background(), principal) instanceRepo := persistencemock.NewInstanceRepositoryMock() instance := &entity.Instance{ ID: "inst-1", WorkspaceID: entity.DefaultWorkspaceID, OwnerID: "user-1", ClusterID: "cluster-1", Name: "demo", Namespace: "default", } if err := instanceRepo.Create(ctx, instance); err != nil { t.Fatalf("failed to seed instance: %v", err) } cluster := &entity.Cluster{ID: "cluster-1", Name: "cluster", Host: "https://example.com"} clusterRepo := &stubClusterRepo{cluster: cluster} svc := NewInstanceService( instanceRepo, clusterRepo, nil, &stubHelmClient{uninstallErr: entity.ErrInstanceNotFound}, nil, nil, ) if err := svc.DeleteInstance(ctx, instance.ID); err != nil { t.Fatalf("DeleteInstance returned error: %v", err) } waitForInstanceDeleted(t, ctx, instanceRepo, instance.ID) } func TestEnforceNamespaceValuesOverridesChartNamespaceKnobs(t *testing.T) { instance := &entity.Instance{ Namespace: "ocdp-u-alice", Values: map[string]interface{}{ "namespace": "default", "namespaceOverride": "default", "targetNamespace": "default", "global": map[string]interface{}{ "namespace": "default", "namespaceOverride": "default", }, "image": map[string]interface{}{ "repository": "nginx", }, }, } enforceNamespaceValues(instance) if instance.Values["namespace"] != "ocdp-u-alice" { t.Fatalf("expected top-level namespace to be enforced, got %#v", instance.Values["namespace"]) } if instance.Values["namespaceOverride"] != "ocdp-u-alice" { t.Fatalf("expected namespaceOverride to be enforced, got %#v", instance.Values["namespaceOverride"]) } if instance.Values["targetNamespace"] != "ocdp-u-alice" { t.Fatalf("expected targetNamespace to be enforced, got %#v", instance.Values["targetNamespace"]) } global, ok := instance.Values["global"].(map[string]interface{}) if !ok { t.Fatalf("expected global map, got %#v", instance.Values["global"]) } if global["namespace"] != "ocdp-u-alice" || global["namespaceOverride"] != "ocdp-u-alice" { t.Fatalf("expected global namespace keys to be enforced, got %#v", global) } } func waitForInstanceDeleted(t *testing.T, ctx context.Context, repo repository.InstanceRepository, id string) { t.Helper() deadline := time.After(2 * time.Second) ticker := time.NewTicker(10 * time.Millisecond) defer ticker.Stop() for { select { case <-deadline: _, err := repo.GetByID(ctx, id) t.Fatalf("expected instance removed, got err=%v", err) case <-ticker.C: if _, err := repo.GetByID(ctx, id); errors.Is(err, entity.ErrInstanceNotFound) { return } } } } type stubClusterRepo struct { cluster *entity.Cluster } func (s *stubClusterRepo) Create(ctx context.Context, cluster *entity.Cluster) error { s.cluster = cluster return nil } func (s *stubClusterRepo) GetByID(ctx context.Context, id string) (*entity.Cluster, error) { if s.cluster != nil && s.cluster.ID == id { return s.cluster, nil } return nil, entity.ErrClusterNotFound } func (*stubClusterRepo) GetByName(ctx context.Context, name string) (*entity.Cluster, error) { return nil, entity.ErrClusterNotFound } func (*stubClusterRepo) Update(ctx context.Context, cluster *entity.Cluster) error { return nil } func (*stubClusterRepo) Delete(ctx context.Context, id string) error { return nil } func (*stubClusterRepo) List(ctx context.Context) ([]*entity.Cluster, error) { return nil, nil } type stubHelmClient struct { uninstallErr error } func (*stubHelmClient) Install(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error { return nil } func (*stubHelmClient) Upgrade(ctx context.Context, cluster *entity.Cluster, instance *entity.Instance) error { return nil } func (s *stubHelmClient) Uninstall(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) error { return s.uninstallErr } func (*stubHelmClient) Rollback(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string, revision int) error { return nil } func (*stubHelmClient) GetStatus(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (*entity.Instance, error) { return nil, nil } func (*stubHelmClient) GetHistory(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) ([]*entity.ReleaseHistory, error) { return nil, nil } func (*stubHelmClient) List(ctx context.Context, cluster *entity.Cluster, namespace string) ([]*entity.Instance, error) { return nil, nil } func (*stubHelmClient) GetValues(ctx context.Context, cluster *entity.Cluster, releaseName, namespace string) (map[string]interface{}, error) { return nil, nil } var _ repository.ClusterRepository = (*stubClusterRepo)(nil) var _ repository.HelmClient = (*stubHelmClient)(nil)