diff --git a/README.md b/README.md index 82fa91a..beab6dd 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,12 @@ make coverage-check # enforce >= 80% coverage under ./internal make lint # go vet + golangci-lint ``` -The BDD suite lives in `test/bdd` and uses [Ginkgo](https://onsi.github.io/ginkgo/) -and [Gomega](https://onsi.github.io/gomega/) to exercise the public volume -lifecycle: create/export, active-session refusal, credential rotation, and -delete. GoMock is not required yet because the core interfaces are covered by -focused in-package test doubles. +All unit test packages now run through [Ginkgo](https://onsi.github.io/ginkgo/) +suites; the existing case functions execute through Ginkgo's `GinkgoTB` adapter. +The BDD-style suite in `test/bdd` uses [Gomega](https://onsi.github.io/gomega/) +assertions to exercise the public volume lifecycle: create/export, +active-session refusal, credential rotation, and delete. GoMock is not required +yet because the core interfaces are covered by focused in-package test doubles. `make tools` installs the optional protobuf, CRD, and lint tools. Add `$(go env GOPATH)/bin` to `PATH` before running `make lint` or `make proto`. diff --git a/internal/agent/admin_drift_test.go b/internal/agent/admin_drift_test.go index 85b274e..a9a06e1 100644 --- a/internal/agent/admin_drift_test.go +++ b/internal/agent/admin_drift_test.go @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc/status" ) -func addDriftExport(t *testing.T, store *state.Store, owner string) { +func addDriftExport(t testing.TB, store *state.Store, owner string) { t.Helper() now := time.Date(2026, 11, 6, 12, 0, 0, 0, time.UTC) if err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -29,9 +29,9 @@ func addDriftExport(t *testing.T, store *state.Store, owner string) { } } -func TestResolveDriftMarkActions(t *testing.T) { +func bddTestResolveDriftMarkActions(t testing.TB) { owner := "spiffe://kouja/caller/one" - newService := func(t *testing.T, present bool, targetPresent bool) (*AdminService, *state.Store) { + newService := func(t testing.TB, present bool, targetPresent bool) (*AdminService, *state.Store) { t.Helper() store := newAgentStore(t) addAgentVolume(t, store, owner, "vol1", "resource-1", "drift") @@ -51,7 +51,7 @@ func TestResolveDriftMarkActions(t *testing.T) { return NewAdminService(store, backend, WithTargetExporters(map[string]target.Exporter{"iscsi": exporter})), store } - t.Run("mark available", func(t *testing.T) { + bddSubtest(t, "mark available", func(t testing.TB) { service, store := newService(t, true, false) response, err := service.ResolveDrift(adminContext(), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_AVAILABLE, @@ -68,7 +68,7 @@ func TestResolveDriftMarkActions(t *testing.T) { } }) - t.Run("mark exported", func(t *testing.T) { + bddSubtest(t, "mark exported", func(t testing.TB) { service, store := newService(t, true, true) response, err := service.ResolveDrift(adminContext(), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_EXPORTED, @@ -93,7 +93,7 @@ func TestResolveDriftMarkActions(t *testing.T) { } }) - t.Run("mark failed", func(t *testing.T) { + bddSubtest(t, "mark failed", func(t testing.TB) { service, store := newService(t, true, false) if _, err := service.ResolveDrift(adminContext(), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_FAILED, @@ -106,7 +106,7 @@ func TestResolveDriftMarkActions(t *testing.T) { } }) - t.Run("mark deleted", func(t *testing.T) { + bddSubtest(t, "mark deleted", func(t testing.TB) { service, store := newService(t, false, false) if _, err := service.ResolveDrift(adminContext(), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_DELETED, @@ -124,7 +124,7 @@ func TestResolveDriftMarkActions(t *testing.T) { } }) - t.Run("mark available rejects present target", func(t *testing.T) { + bddSubtest(t, "mark available rejects present target", func(t testing.TB) { service, _ := newService(t, true, true) if _, err := service.ResolveDrift(adminContext(), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_AVAILABLE, @@ -133,7 +133,7 @@ func TestResolveDriftMarkActions(t *testing.T) { } }) - t.Run("non admin rejected", func(t *testing.T) { + bddSubtest(t, "non admin rejected", func(t testing.TB) { service, _ := newService(t, true, false) if _, err := service.ResolveDrift(callerContext(owner), &koujav1.ResolveDriftRequest{ Owner: owner, VolumeId: "vol1", Action: koujav1.ResolveAction_RESOLVE_ACTION_MARK_FAILED, @@ -143,7 +143,7 @@ func TestResolveDriftMarkActions(t *testing.T) { }) } -func TestResolveDriftRecreateExport(t *testing.T) { +func bddTestResolveDriftRecreateExport(t testing.TB) { owner := "spiffe://kouja/caller/one" store := newAgentStore(t) addAgentVolume(t, store, owner, "vol1", "resource-1", "drift") diff --git a/internal/agent/admin_service_test.go b/internal/agent/admin_service_test.go index 74a12b3..50c3299 100644 --- a/internal/agent/admin_service_test.go +++ b/internal/agent/admin_service_test.go @@ -21,7 +21,7 @@ func adminContext() context.Context { }) } -func TestAdminRegisterAndDeregisterVG(t *testing.T) { +func bddTestAdminRegisterAndDeregisterVG(t testing.TB) { store := newAgentStore(t) backend := &fakeLVM{capacity: lvm.Capacity{ VGName: "vg1", VGUUID: "vg-uuid", VGState: "active", TotalBytes: 10, @@ -50,7 +50,7 @@ func TestAdminRegisterAndDeregisterVG(t *testing.T) { } } -func TestAdminGrantsAndRevokesNodeCredential(t *testing.T) { +func bddTestAdminGrantsAndRevokesNodeCredential(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/controller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "exported") @@ -87,7 +87,7 @@ func TestAdminGrantsAndRevokesNodeCredential(t *testing.T) { } } -func TestAdminRejectsNonAdminAndDeregisterWithVolume(t *testing.T) { +func bddTestAdminRejectsNonAdminAndDeregisterWithVolume(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") @@ -101,7 +101,7 @@ func TestAdminRejectsNonAdminAndDeregisterWithVolume(t *testing.T) { } } -func TestAdminValidationBranches(t *testing.T) { +func bddTestAdminValidationBranches(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/controller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") diff --git a/internal/agent/admission_test.go b/internal/agent/admission_test.go index 96c85e2..10453ce 100644 --- a/internal/agent/admission_test.go +++ b/internal/agent/admission_test.go @@ -10,7 +10,7 @@ import ( "google.golang.org/grpc/status" ) -func TestAdmissionConcurrencyAndPerCaller(t *testing.T) { +func bddTestAdmissionConcurrencyAndPerCaller(t testing.TB) { admission := NewAdmission(2, 1, 10) releaseOne, err := admission.AcquireOperation(context.Background(), "caller-a") if err != nil { @@ -36,7 +36,7 @@ func TestAdmissionConcurrencyAndPerCaller(t *testing.T) { release() } -func TestAdmissionCapacityRate(t *testing.T) { +func bddTestAdmissionCapacityRate(t testing.TB) { admission := NewAdmission(2, 2, 1) base := time.Date(2026, 11, 6, 12, 0, 0, 0, time.UTC) admission.now = func() time.Time { return base } diff --git a/internal/agent/ginkgo_suite_test.go b/internal/agent/ginkgo_suite_test.go new file mode 100644 index 0000000..d31025e --- /dev/null +++ b/internal/agent/ginkgo_suite_test.go @@ -0,0 +1,123 @@ +package agent + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "agent Suite") +} + +func bddSubtest(t testing.TB, name string, fn func(testing.TB)) { + By(name) + fn(t) +} + +var _ = Describe("unit tests", func() { + It("TestResolveDriftMarkActions", func() { + bddTestResolveDriftMarkActions(GinkgoTB()) + }) + It("TestResolveDriftRecreateExport", func() { + bddTestResolveDriftRecreateExport(GinkgoTB()) + }) + It("TestAdminRegisterAndDeregisterVG", func() { + bddTestAdminRegisterAndDeregisterVG(GinkgoTB()) + }) + It("TestAdminGrantsAndRevokesNodeCredential", func() { + bddTestAdminGrantsAndRevokesNodeCredential(GinkgoTB()) + }) + It("TestAdminRejectsNonAdminAndDeregisterWithVolume", func() { + bddTestAdminRejectsNonAdminAndDeregisterWithVolume(GinkgoTB()) + }) + It("TestAdminValidationBranches", func() { + bddTestAdminValidationBranches(GinkgoTB()) + }) + It("TestAdmissionConcurrencyAndPerCaller", func() { + bddTestAdmissionConcurrencyAndPerCaller(GinkgoTB()) + }) + It("TestAdmissionCapacityRate", func() { + bddTestAdmissionCapacityRate(GinkgoTB()) + }) + It("TestAgentProtoMappings", func() { + bddTestAgentProtoMappings(GinkgoTB()) + }) + It("TestCheckDependenciesWithFakeTools", func() { + bddTestCheckDependenciesWithFakeTools(GinkgoTB()) + }) + It("TestAgentAdmissionAndIdentityErrors", func() { + bddTestAgentAdmissionAndIdentityErrors(GinkgoTB()) + }) + It("TestInfoAndListBranches", func() { + bddTestInfoAndListBranches(GinkgoTB()) + }) + It("TestCapacityAdmissionAndReadyBranches", func() { + bddTestCapacityAdmissionAndReadyBranches(GinkgoTB()) + }) + It("TestVolumeServiceIdentityFailures", func() { + bddTestVolumeServiceIdentityFailures(GinkgoTB()) + }) + It("TestCapacityUnavailableBranch", func() { + bddTestCapacityUnavailableBranch(GinkgoTB()) + }) + It("TestRequestIDAndNilErrors", func() { + bddTestRequestIDAndNilErrors(GinkgoTB()) + }) + It("TestReconcileWritesObservationsAndDrift", func() { + bddTestReconcileWritesObservationsAndDrift(GinkgoTB()) + }) + It("TestServerServesGRPCAndHTTPOnOneTLSListener", func() { + bddTestServerServesGRPCAndHTTPOnOneTLSListener(GinkgoTB()) + }) + It("TestNewServerLifecycleAndErrors", func() { + bddTestNewServerLifecycleAndErrors(GinkgoTB()) + }) + It("TestDependencyChecksAndReconcileLoop", func() { + bddTestDependencyChecksAndReconcileLoop(GinkgoTB()) + }) + It("TestUnaryInterceptorPropagatesIdentityAndRequestID", func() { + bddTestUnaryInterceptorPropagatesIdentityAndRequestID(GinkgoTB()) + }) + It("TestUnaryInterceptorRejectsMissingOrInvalidCertificate", func() { + bddTestUnaryInterceptorRejectsMissingOrInvalidCertificate(GinkgoTB()) + }) + It("TestInitializeVGsSeedsOnlyOnce", func() { + bddTestInitializeVGsSeedsOnlyOnce(GinkgoTB()) + }) + It("TestVerifyVGsMarksUnavailableAndVolumesDrift", func() { + bddTestVerifyVGsMarksUnavailableAndVolumesDrift(GinkgoTB()) + }) + It("TestVerifyVGsRejectsUUIDMismatch", func() { + bddTestVerifyVGsRejectsUUIDMismatch(GinkgoTB()) + }) + It("TestCreateVolumeBuildsCanonicalIntent", func() { + bddTestCreateVolumeBuildsCanonicalIntent(GinkgoTB()) + }) + It("TestCreateVolumeUnexportedDoesNotRequireProtocol", func() { + bddTestCreateVolumeUnexportedDoesNotRequireProtocol(GinkgoTB()) + }) + It("TestGetVolumeOwnerScopingAndMapping", func() { + bddTestGetVolumeOwnerScopingAndMapping(GinkgoTB()) + }) + It("TestListVolumesPaginationAndCapacity", func() { + bddTestListVolumesPaginationAndCapacity(GinkgoTB()) + }) + It("TestOperationErrorMapsToStatus", func() { + bddTestOperationErrorMapsToStatus(GinkgoTB()) + }) + It("TestGetCredentialRevealsSecret", func() { + bddTestGetCredentialRevealsSecret(GinkgoTB()) + }) + It("TestVolumeServiceMutationRPCsAndCredentialValue", func() { + bddTestVolumeServiceMutationRPCsAndCredentialValue(GinkgoTB()) + }) + It("TestVolumeServiceHelpersAndStatusErrors", func() { + bddTestVolumeServiceHelpersAndStatusErrors(GinkgoTB()) + }) + It("TestVolumeServiceRPCErrorBranches", func() { + bddTestVolumeServiceRPCErrorBranches(GinkgoTB()) + }) +}) diff --git a/internal/agent/misc_test.go b/internal/agent/misc_test.go index 8c2609d..c621a20 100644 --- a/internal/agent/misc_test.go +++ b/internal/agent/misc_test.go @@ -14,7 +14,7 @@ import ( "github.com/voidstarit/kouja/internal/state" ) -func TestAgentProtoMappings(t *testing.T) { +func bddTestAgentProtoMappings(t testing.TB) { observation := toProtoObservation(state.Observation{ LVPresent: true, ObservedSizeBytes: 10, DevicePath: "/dev/x", ObservedAt: time.Now(), Source: "lvm", }, state.ExportObservation{ @@ -62,7 +62,7 @@ func TestAgentProtoMappings(t *testing.T) { } } -func TestCheckDependenciesWithFakeTools(t *testing.T) { +func bddTestCheckDependenciesWithFakeTools(t testing.TB) { dir := t.TempDir() for _, name := range []string{"lvs", "vgs", "pvs", "lvcreate", "lvremove", "lvchange", "udevadm", "findmnt", "blockdev", "systemctl"} { if err := os.WriteFile(filepath.Join(dir, name), []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil { //nolint:gosec // executable test fixture @@ -81,7 +81,7 @@ func TestCheckDependenciesWithFakeTools(t *testing.T) { } } -func TestAgentAdmissionAndIdentityErrors(t *testing.T) { +func bddTestAgentAdmissionAndIdentityErrors(t testing.TB) { manager := &fakeCredentialManager{err: errors.New("missing")} service := NewVolumeService(newAgentStore(t), &fakeLVM{}, &fakeRunner{}, manager, ServiceOptions{}) if _, err := service.GetInfo(context.Background(), &koujav1.GetInfoRequest{}); err != nil { @@ -99,7 +99,7 @@ func TestAgentAdmissionAndIdentityErrors(t *testing.T) { _ = WithDriftRetrier(nil) } -func TestInfoAndListBranches(t *testing.T) { +func bddTestInfoAndListBranches(t testing.TB) { store := newAgentStore(t) ready := false service := NewVolumeService(store, &fakeLVM{}, &fakeRunner{}, &fakeCredentialManager{}, ServiceOptions{ @@ -120,7 +120,7 @@ func TestInfoAndListBranches(t *testing.T) { } } -func TestCapacityAdmissionAndReadyBranches(t *testing.T) { +func bddTestCapacityAdmissionAndReadyBranches(t testing.TB) { store := newAgentStore(t) admission := NewAdmission(1, 1, 1) service := NewVolumeService(store, &fakeLVM{}, &fakeRunner{}, &fakeCredentialManager{}, ServiceOptions{ @@ -138,7 +138,7 @@ func TestCapacityAdmissionAndReadyBranches(t *testing.T) { } } -func TestVolumeServiceIdentityFailures(t *testing.T) { +func bddTestVolumeServiceIdentityFailures(t testing.TB) { store := newAgentStore(t) service := NewVolumeService(store, &fakeLVM{}, &fakeRunner{}, &fakeCredentialManager{}, ServiceOptions{}) if _, err := service.GetCredential(context.Background(), &koujav1.GetCredentialRequest{VolumeId: "vol"}); err == nil { @@ -152,7 +152,7 @@ func TestVolumeServiceIdentityFailures(t *testing.T) { } } -func TestCapacityUnavailableBranch(t *testing.T) { +func bddTestCapacityUnavailableBranch(t testing.TB) { store := newAgentStore(t) addAgentVolume(t, store, "owner", "vol1", "resource-1", "available") service := NewVolumeService(store, &fakeLVM{capacityErr: errors.New("lvm offline")}, &fakeRunner{}, &fakeCredentialManager{}, ServiceOptions{}) @@ -162,7 +162,7 @@ func TestCapacityUnavailableBranch(t *testing.T) { } } -func TestRequestIDAndNilErrors(t *testing.T) { +func bddTestRequestIDAndNilErrors(t testing.TB) { if statusError(nil) != nil { t.Fatal("nil error did not map to nil") } diff --git a/internal/agent/reconcile_test.go b/internal/agent/reconcile_test.go index 0d62b55..a7b5ead 100644 --- a/internal/agent/reconcile_test.go +++ b/internal/agent/reconcile_test.go @@ -45,7 +45,7 @@ func (exporter *reconcileTarget) Restore(context.Context, target.ExportSpec, tar } func (exporter *reconcileTarget) Delete(context.Context, target.ExportSpec, bool) error { return nil } -func TestReconcileWritesObservationsAndDrift(t *testing.T) { +func bddTestReconcileWritesObservationsAndDrift(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "exported") diff --git a/internal/agent/server_integration_test.go b/internal/agent/server_integration_test.go index 113552d..8323cdc 100644 --- a/internal/agent/server_integration_test.go +++ b/internal/agent/server_integration_test.go @@ -26,7 +26,7 @@ import ( "google.golang.org/grpc/credentials" ) -func writeTestCertificates(t *testing.T) (caFile, serverCert, serverKey, clientCert, clientKey string) { +func writeTestCertificates(t testing.TB) (caFile, serverCert, serverKey, clientCert, clientKey string) { t.Helper() dir := t.TempDir() caKey, err := rsa.GenerateKey(rand.Reader, 2048) @@ -59,7 +59,7 @@ func writeTestCertificates(t *testing.T) (caFile, serverCert, serverKey, clientC return caFile, serverCertFile, serverKeyFile, clientCertFile, clientKeyFile } -func issueCertificate(t *testing.T, dir string, ca *x509.Certificate, caKey *rsa.PrivateKey, identity string, server bool) (keyFile, certFile string) { +func issueCertificate(t testing.TB, dir string, ca *x509.Certificate, caKey *rsa.PrivateKey, identity string, server bool) (keyFile, certFile string) { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -99,7 +99,7 @@ func issueCertificate(t *testing.T, dir string, ca *x509.Certificate, caKey *rsa return keyFile, certFile } -func writePEM(t *testing.T, path, blockType string, data []byte) { +func writePEM(t testing.TB, path, blockType string, data []byte) { t.Helper() file, err := os.Create(path) //nolint:gosec // temporary test certificate path if err != nil { @@ -111,7 +111,7 @@ func writePEM(t *testing.T, path, blockType string, data []byte) { } } -func TestServerServesGRPCAndHTTPOnOneTLSListener(t *testing.T) { +func bddTestServerServesGRPCAndHTTPOnOneTLSListener(t testing.TB) { caFile, serverCert, serverKey, clientCert, clientKey := writeTestCertificates(t) cfg := &config.Config{} cfg.Listen.Address = "127.0.0.1:0" diff --git a/internal/agent/server_more_test.go b/internal/agent/server_more_test.go index a125368..062eb61 100644 --- a/internal/agent/server_more_test.go +++ b/internal/agent/server_more_test.go @@ -18,7 +18,7 @@ import ( "github.com/voidstarit/kouja/internal/observability" ) -func agentTLSFiles(t *testing.T) (string, string, string) { +func agentTLSFiles(t testing.TB) (string, string, string) { t.Helper() dir := t.TempDir() caKey, err := rsa.GenerateKey(rand.Reader, 2048) @@ -60,7 +60,7 @@ func agentTLSFiles(t *testing.T) (string, string, string) { return certPath, keyPath, caPath } -func TestNewServerLifecycleAndErrors(t *testing.T) { +func bddTestNewServerLifecycleAndErrors(t testing.TB) { cert, key, ca := agentTLSFiles(t) cfg := config.Default() cfg.Listen.Address = "127.0.0.1:0" @@ -101,7 +101,7 @@ func TestNewServerLifecycleAndErrors(t *testing.T) { } } -func TestDependencyChecksAndReconcileLoop(t *testing.T) { +func bddTestDependencyChecksAndReconcileLoop(t testing.TB) { previous := os.Getenv("PATH") t.Setenv("PATH", filepath.Join(t.TempDir(), "empty")) cfg := config.Default() diff --git a/internal/agent/server_test.go b/internal/agent/server_test.go index a49223e..d7edcfa 100644 --- a/internal/agent/server_test.go +++ b/internal/agent/server_test.go @@ -19,7 +19,7 @@ import ( "google.golang.org/grpc/status" ) -func testCertificate(t *testing.T, san string) *x509.Certificate { +func testCertificate(t testing.TB, san string) *x509.Certificate { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -48,7 +48,7 @@ func testCertificate(t *testing.T, san string) *x509.Certificate { return certificate } -func TestUnaryInterceptorPropagatesIdentityAndRequestID(t *testing.T) { +func bddTestUnaryInterceptorPropagatesIdentityAndRequestID(t testing.TB) { certificate := testCertificate(t, "spiffe://kouja/caller/test") ctx := peer.NewContext(context.Background(), &peer.Peer{ AuthInfo: credentials.TLSInfo{State: tls.ConnectionState{PeerCertificates: []*x509.Certificate{certificate}}}, @@ -72,7 +72,7 @@ func TestUnaryInterceptorPropagatesIdentityAndRequestID(t *testing.T) { } } -func TestUnaryInterceptorRejectsMissingOrInvalidCertificate(t *testing.T) { +func bddTestUnaryInterceptorRejectsMissingOrInvalidCertificate(t testing.TB) { if _, err := UnaryInterceptor(context.Background(), nil, nil, func(context.Context, any) (any, error) { return nil, nil }); status.Code(err) != codes.Unauthenticated { diff --git a/internal/agent/startup_test.go b/internal/agent/startup_test.go index 70ce45e..620430a 100644 --- a/internal/agent/startup_test.go +++ b/internal/agent/startup_test.go @@ -37,7 +37,7 @@ func (backend *startupBackend) Eligibility(_ context.Context, vg string) error { return nil } -func TestInitializeVGsSeedsOnlyOnce(t *testing.T) { +func bddTestInitializeVGsSeedsOnlyOnce(t testing.TB) { store := newAgentStore(t) backend := &startupBackend{ fakeLVM: &fakeLVM{}, @@ -75,7 +75,7 @@ func TestInitializeVGsSeedsOnlyOnce(t *testing.T) { } } -func TestVerifyVGsMarksUnavailableAndVolumesDrift(t *testing.T) { +func bddTestVerifyVGsMarksUnavailableAndVolumesDrift(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") @@ -129,7 +129,7 @@ func TestVerifyVGsMarksUnavailableAndVolumesDrift(t *testing.T) { } } -func TestVerifyVGsRejectsUUIDMismatch(t *testing.T) { +func bddTestVerifyVGsRejectsUUIDMismatch(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") @@ -156,7 +156,7 @@ func TestVerifyVGsRejectsUUIDMismatch(t *testing.T) { } } -func fetchStoredVolume(t *testing.T, store *state.Store, owner, volumeID string) (state.Volume, error) { +func fetchStoredVolume(t testing.TB, store *state.Store, owner, volumeID string) (state.Volume, error) { t.Helper() var volume state.Volume err := store.Transact(context.Background(), func(tx state.Tx) error { diff --git a/internal/agent/volume_service_test.go b/internal/agent/volume_service_test.go index d28b44d..618f359 100644 --- a/internal/agent/volume_service_test.go +++ b/internal/agent/volume_service_test.go @@ -86,7 +86,7 @@ func (manager *fakeCredentialManager) Install(context.Context, target.ExportSpec } func (manager *fakeCredentialManager) Revoke(context.Context, target.ExportSpec) error { return nil } -func newAgentStore(t *testing.T) *state.Store { +func newAgentStore(t testing.TB) *state.Store { t.Helper() store, err := state.Open(context.Background(), filepath.Join(t.TempDir(), "state.db")) if err != nil { @@ -96,7 +96,7 @@ func newAgentStore(t *testing.T) *state.Store { return store } -func addAgentVolume(t *testing.T, store *state.Store, owner, volumeID, resourceUUID, stateValue string) { +func addAgentVolume(t testing.TB, store *state.Store, owner, volumeID, resourceUUID, stateValue string) { t.Helper() now := time.Date(2026, 11, 6, 12, 0, 0, 0, time.UTC) err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -119,7 +119,7 @@ func callerContext(owner string) context.Context { return WithIdentity(context.Background(), auth.Identity{Owner: owner, Role: auth.RoleCaller}) } -func TestCreateVolumeBuildsCanonicalIntent(t *testing.T) { +func bddTestCreateVolumeBuildsCanonicalIntent(t testing.TB) { store := newAgentStore(t) runner := &fakeRunner{result: operations.Result{OperationID: "op-1"}} service := NewVolumeService(store, &fakeLVM{}, runner, &fakeCredentialManager{}, ServiceOptions{ @@ -148,7 +148,7 @@ func TestCreateVolumeBuildsCanonicalIntent(t *testing.T) { } } -func TestCreateVolumeUnexportedDoesNotRequireProtocol(t *testing.T) { +func bddTestCreateVolumeUnexportedDoesNotRequireProtocol(t testing.TB) { store := newAgentStore(t) runner := &fakeRunner{} service := NewVolumeService(store, &fakeLVM{}, runner, &fakeCredentialManager{}, ServiceOptions{ @@ -170,7 +170,7 @@ func TestCreateVolumeUnexportedDoesNotRequireProtocol(t *testing.T) { } } -func TestGetVolumeOwnerScopingAndMapping(t *testing.T) { +func bddTestGetVolumeOwnerScopingAndMapping(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") @@ -188,7 +188,7 @@ func TestGetVolumeOwnerScopingAndMapping(t *testing.T) { } } -func TestListVolumesPaginationAndCapacity(t *testing.T) { +func bddTestListVolumesPaginationAndCapacity(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" now := time.Date(2026, 11, 6, 12, 0, 0, 0, time.UTC) @@ -229,7 +229,7 @@ func TestListVolumesPaginationAndCapacity(t *testing.T) { } } -func TestOperationErrorMapsToStatus(t *testing.T) { +func bddTestOperationErrorMapsToStatus(t testing.TB) { store := newAgentStore(t) runner := &fakeRunner{err: &operations.OperationError{ Code: "SESSION_ACTIVE", Retryable: false, OperationID: "op-1", Err: operations.ErrSessionActive, @@ -244,7 +244,7 @@ func TestOperationErrorMapsToStatus(t *testing.T) { } } -func TestGetCredentialRevealsSecret(t *testing.T) { +func bddTestGetCredentialRevealsSecret(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") @@ -259,7 +259,7 @@ func TestGetCredentialRevealsSecret(t *testing.T) { } } -func TestVolumeServiceMutationRPCsAndCredentialValue(t *testing.T) { +func bddTestVolumeServiceMutationRPCsAndCredentialValue(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "exported") @@ -323,7 +323,7 @@ func TestVolumeServiceMutationRPCsAndCredentialValue(t *testing.T) { } } -func TestVolumeServiceHelpersAndStatusErrors(t *testing.T) { +func bddTestVolumeServiceHelpersAndStatusErrors(t testing.TB) { store := newAgentStore(t) service := NewVolumeService(store, &fakeLVM{}, &fakeRunner{}, &fakeCredentialManager{}, ServiceOptions{}) release, err := service.admit(context.Background(), auth.Identity{Owner: "owner"}) @@ -401,7 +401,7 @@ func TestVolumeServiceHelpersAndStatusErrors(t *testing.T) { } } -func TestVolumeServiceRPCErrorBranches(t *testing.T) { +func bddTestVolumeServiceRPCErrorBranches(t testing.TB) { store := newAgentStore(t) owner := "spiffe://kouja/caller/one" addAgentVolume(t, store, owner, "vol1", "resource-1", "available") diff --git a/internal/auth/ginkgo_suite_test.go b/internal/auth/ginkgo_suite_test.go new file mode 100644 index 0000000..206d6c1 --- /dev/null +++ b/internal/auth/ginkgo_suite_test.go @@ -0,0 +1,19 @@ +package auth + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "auth Suite") +} + +var _ = Describe("unit tests", func() { + It("TestParseRolesAndErrors", func() { + bddTestParseRolesAndErrors(GinkgoTB()) + }) +}) diff --git a/internal/auth/types_test.go b/internal/auth/types_test.go index 4eb750e..98d2299 100644 --- a/internal/auth/types_test.go +++ b/internal/auth/types_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func uri(t *testing.T, value string) *url.URL { +func uri(t testing.TB, value string) *url.URL { t.Helper() parsed, err := url.Parse(value) if err != nil { @@ -15,7 +15,7 @@ func uri(t *testing.T, value string) *url.URL { return parsed } -func TestParseRolesAndErrors(t *testing.T) { +func bddTestParseRolesAndErrors(t testing.TB) { for _, test := range []struct { san string role Role diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go index 4ec7136..91448b8 100644 --- a/internal/backup/backup_test.go +++ b/internal/backup/backup_test.go @@ -13,7 +13,7 @@ import ( "github.com/voidstarit/kouja/internal/state" ) -func openBackupStore(t *testing.T) *state.Store { +func openBackupStore(t testing.TB) *state.Store { t.Helper() store, err := state.Open(context.Background(), filepath.Join(t.TempDir(), "state.db")) if err != nil { @@ -27,7 +27,7 @@ func openBackupStore(t *testing.T) *state.Store { return store } -func TestRunCreatesCompleteBundle(t *testing.T) { +func bddTestRunCreatesCompleteBundle(t testing.TB) { store := openBackupStore(t) dir := t.TempDir() hostIDPath := filepath.Join(dir, "host-id") @@ -94,7 +94,7 @@ func TestRunCreatesCompleteBundle(t *testing.T) { } } -func TestRunWithoutKeyAndMissingHostID(t *testing.T) { +func bddTestRunWithoutKeyAndMissingHostID(t testing.TB) { store := openBackupStore(t) dir := t.TempDir() hostIDPath := filepath.Join(dir, "host-id") @@ -146,7 +146,7 @@ func TestRunWithoutKeyAndMissingHostID(t *testing.T) { } } -func TestRunRejectsInvalidTarget(t *testing.T) { +func bddTestRunRejectsInvalidTarget(t testing.TB) { store := openBackupStore(t) hostIDPath := filepath.Join(t.TempDir(), "host-id") if err := os.WriteFile(hostIDPath, []byte(uuid.NewString()), 0o600); err != nil { @@ -165,7 +165,7 @@ func TestRunRejectsInvalidTarget(t *testing.T) { } } -func TestRotateRetainsShortDailyAndWeekly(t *testing.T) { +func bddTestRotateRetainsShortDailyAndWeekly(t testing.TB) { store := openBackupStore(t) dir := t.TempDir() hostIDPath := filepath.Join(dir, "host-id") @@ -214,7 +214,7 @@ func TestRotateRetainsShortDailyAndWeekly(t *testing.T) { } } -func TestBackupValidationAndCopyErrors(t *testing.T) { +func bddTestBackupValidationAndCopyErrors(t testing.TB) { store := openBackupStore(t) if _, err := New(nil, Options{}); err == nil { t.Fatal("nil store did not fail") @@ -251,7 +251,7 @@ func TestBackupValidationAndCopyErrors(t *testing.T) { } } -func TestBackupDefaultsAndRunFailures(t *testing.T) { +func bddTestBackupDefaultsAndRunFailures(t testing.TB) { store := openBackupStore(t) dir := t.TempDir() hostID := filepath.Join(dir, "host-id") diff --git a/internal/backup/ginkgo_suite_test.go b/internal/backup/ginkgo_suite_test.go new file mode 100644 index 0000000..1e6ab55 --- /dev/null +++ b/internal/backup/ginkgo_suite_test.go @@ -0,0 +1,34 @@ +package backup + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "backup Suite") +} + +var _ = Describe("unit tests", func() { + It("TestRunCreatesCompleteBundle", func() { + bddTestRunCreatesCompleteBundle(GinkgoTB()) + }) + It("TestRunWithoutKeyAndMissingHostID", func() { + bddTestRunWithoutKeyAndMissingHostID(GinkgoTB()) + }) + It("TestRunRejectsInvalidTarget", func() { + bddTestRunRejectsInvalidTarget(GinkgoTB()) + }) + It("TestRotateRetainsShortDailyAndWeekly", func() { + bddTestRotateRetainsShortDailyAndWeekly(GinkgoTB()) + }) + It("TestBackupValidationAndCopyErrors", func() { + bddTestBackupValidationAndCopyErrors(GinkgoTB()) + }) + It("TestBackupDefaultsAndRunFailures", func() { + bddTestBackupDefaultsAndRunFailures(GinkgoTB()) + }) +}) diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 063bf3d..085db1c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -27,7 +27,7 @@ targets: port: 3260 ` -func TestParseAppliesDefaultsAndValidates(t *testing.T) { +func bddTestParseAppliesDefaultsAndValidates(t testing.TB) { cfg, err := Parse([]byte(validConfig)) if err != nil { t.Fatalf("Parse: %v", err) @@ -44,7 +44,7 @@ func TestParseAppliesDefaultsAndValidates(t *testing.T) { } } -func TestLoadFromFile(t *testing.T) { +func bddTestLoadFromFile(t testing.TB) { path := filepath.Join(t.TempDir(), "config.yaml") if err := os.WriteFile(path, []byte(validConfig), 0o600); err != nil { t.Fatal(err) @@ -64,14 +64,14 @@ func TestLoadFromFile(t *testing.T) { } } -func TestUnknownFieldRejected(t *testing.T) { +func bddTestUnknownFieldRejected(t testing.TB) { _, err := Parse([]byte(validConfig + "\nunknown_field: true\n")) if err == nil { t.Fatal("unknown field was accepted") } } -func TestValidationFailures(t *testing.T) { +func bddTestValidationFailures(t testing.TB) { tests := []struct { name string yaml string @@ -145,7 +145,7 @@ targets: }, } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + bddSubtest(t, test.name, func(t testing.TB) { if _, err := Parse([]byte(test.yaml)); err == nil { t.Fatal("invalid configuration was accepted") } @@ -153,7 +153,7 @@ targets: } } -func TestConfigValidationBranches(t *testing.T) { +func bddTestConfigValidationBranches(t testing.TB) { cfg := Default() cfg.Listen.TLS.MinVersion = "TLS1.2" if err := cfg.Validate(); err == nil { diff --git a/internal/config/ginkgo_suite_test.go b/internal/config/ginkgo_suite_test.go new file mode 100644 index 0000000..120a220 --- /dev/null +++ b/internal/config/ginkgo_suite_test.go @@ -0,0 +1,36 @@ +package config + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "config Suite") +} + +func bddSubtest(t testing.TB, name string, fn func(testing.TB)) { + By(name) + fn(t) +} + +var _ = Describe("unit tests", func() { + It("TestParseAppliesDefaultsAndValidates", func() { + bddTestParseAppliesDefaultsAndValidates(GinkgoTB()) + }) + It("TestLoadFromFile", func() { + bddTestLoadFromFile(GinkgoTB()) + }) + It("TestUnknownFieldRejected", func() { + bddTestUnknownFieldRejected(GinkgoTB()) + }) + It("TestValidationFailures", func() { + bddTestValidationFailures(GinkgoTB()) + }) + It("TestConfigValidationBranches", func() { + bddTestConfigValidationBranches(GinkgoTB()) + }) +}) diff --git a/internal/credential/ginkgo_suite_test.go b/internal/credential/ginkgo_suite_test.go new file mode 100644 index 0000000..e4fb6dd --- /dev/null +++ b/internal/credential/ginkgo_suite_test.go @@ -0,0 +1,40 @@ +package credential + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "credential Suite") +} + +var _ = Describe("unit tests", func() { + It("TestGenerateRevealAndKeyFile", func() { + bddTestGenerateRevealAndKeyFile(GinkgoTB()) + }) + It("TestGetAuthorizationAndGrants", func() { + bddTestGetAuthorizationAndGrants(GinkgoTB()) + }) + It("TestRotateReplacesActiveCredential", func() { + bddTestRotateReplacesActiveCredential(GinkgoTB()) + }) + It("TestInstallRevokeAndNVMeGeneration", func() { + bddTestInstallRevokeAndNVMeGeneration(GinkgoTB()) + }) + It("TestCredentialErrorPaths", func() { + bddTestCredentialErrorPaths(GinkgoTB()) + }) + It("TestCredentialFileOpenErrors", func() { + bddTestCredentialFileOpenErrors(GinkgoTB()) + }) + It("TestCredentialKeyDirectoryErrors", func() { + bddTestCredentialKeyDirectoryErrors(GinkgoTB()) + }) + It("TestRotateWithoutCredential", func() { + bddTestRotateWithoutCredential(GinkgoTB()) + }) +}) diff --git a/internal/credential/manager_test.go b/internal/credential/manager_test.go index 95f603e..81e59ec 100644 --- a/internal/credential/manager_test.go +++ b/internal/credential/manager_test.go @@ -13,7 +13,7 @@ import ( "github.com/voidstarit/kouja/internal/target" ) -func openCredentialStore(t *testing.T) *state.Store { +func openCredentialStore(t testing.TB) *state.Store { t.Helper() store, err := state.Open(context.Background(), filepath.Join(t.TempDir(), "state.db")) if err != nil { @@ -23,7 +23,7 @@ func openCredentialStore(t *testing.T) *state.Store { return store } -func putCredentialVolume(t *testing.T, store *state.Store, owner, volumeID, resourceUUID string) { +func putCredentialVolume(t testing.TB, store *state.Store, owner, volumeID, resourceUUID string) { t.Helper() now := time.Date(2026, 11, 6, 12, 0, 0, 0, time.UTC) err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -42,7 +42,7 @@ func putCredentialVolume(t *testing.T, store *state.Store, owner, volumeID, reso } } -func TestGenerateRevealAndKeyFile(t *testing.T) { +func bddTestGenerateRevealAndKeyFile(t testing.TB) { store := openCredentialStore(t) keyPath := filepath.Join(t.TempDir(), "secret.key") manager, err := Open(store, keyPath) @@ -85,7 +85,7 @@ func TestGenerateRevealAndKeyFile(t *testing.T) { } } -func TestGetAuthorizationAndGrants(t *testing.T) { +func bddTestGetAuthorizationAndGrants(t testing.TB) { store := openCredentialStore(t) owner := "spiffe://kouja/caller/owner" putCredentialVolume(t, store, owner, "volume", "resource-1") @@ -138,7 +138,7 @@ func TestGetAuthorizationAndGrants(t *testing.T) { } } -func TestRotateReplacesActiveCredential(t *testing.T) { +func bddTestRotateReplacesActiveCredential(t testing.TB) { store := openCredentialStore(t) owner := "spiffe://kouja/caller/owner" putCredentialVolume(t, store, owner, "volume", "resource-1") @@ -181,7 +181,7 @@ func TestRotateReplacesActiveCredential(t *testing.T) { } } -func TestInstallRevokeAndNVMeGeneration(t *testing.T) { +func bddTestInstallRevokeAndNVMeGeneration(t testing.TB) { store := openCredentialStore(t) owner := "spiffe://kouja/caller/owner" putCredentialVolume(t, store, owner, "volume", "resource-1") @@ -227,7 +227,7 @@ func targetSpec(owner, volumeID string) target.ExportSpec { } } -func TestCredentialErrorPaths(t *testing.T) { +func bddTestCredentialErrorPaths(t testing.TB) { store := openCredentialStore(t) if _, err := Open(nil, filepath.Join(t.TempDir(), "key")); err == nil { t.Fatal("nil store Open did not fail") @@ -268,7 +268,7 @@ func TestCredentialErrorPaths(t *testing.T) { } } -func TestCredentialFileOpenErrors(t *testing.T) { +func bddTestCredentialFileOpenErrors(t testing.TB) { store := openCredentialStore(t) directory := t.TempDir() if _, err := Open(store, directory); err == nil { @@ -283,7 +283,7 @@ func TestCredentialFileOpenErrors(t *testing.T) { } } -func TestCredentialKeyDirectoryErrors(t *testing.T) { +func bddTestCredentialKeyDirectoryErrors(t testing.TB) { store := openCredentialStore(t) blocker := filepath.Join(t.TempDir(), "blocker") if err := os.WriteFile(blocker, []byte("x"), 0o600); err != nil { @@ -294,7 +294,7 @@ func TestCredentialKeyDirectoryErrors(t *testing.T) { } } -func TestRotateWithoutCredential(t *testing.T) { +func bddTestRotateWithoutCredential(t testing.TB) { store := openCredentialStore(t) owner := "spiffe://kouja/caller/owner" putCredentialVolume(t, store, owner, "volume", "resource-1") diff --git a/internal/csi/attachments_test.go b/internal/csi/attachments_test.go index bcc5382..fdeebba 100644 --- a/internal/csi/attachments_test.go +++ b/internal/csi/attachments_test.go @@ -10,7 +10,7 @@ import ( "k8s.io/client-go/kubernetes/fake" ) -func TestKubernetesAttachmentLookup(t *testing.T) { +func bddTestKubernetesAttachmentLookup(t testing.TB) { const ( handle = "v1:host-1:resource-1" nodeID = "node-1" @@ -56,7 +56,7 @@ func TestKubernetesAttachmentLookup(t *testing.T) { } } -func TestNodeFailurePolicyAndNodeUnavailable(t *testing.T) { +func bddTestNodeFailurePolicyAndNodeUnavailable(t testing.TB) { const ( volumeHandle = "v1:host-1:resource-1" className = "force-class" diff --git a/internal/csi/client_test.go b/internal/csi/client_test.go index cbfb4af..6480054 100644 --- a/internal/csi/client_test.go +++ b/internal/csi/client_test.go @@ -13,7 +13,7 @@ import ( "time" ) -func writeTestTLS(t *testing.T) (certFile, keyFile, caFile string) { +func writeTestTLS(t testing.TB) (certFile, keyFile, caFile string) { t.Helper() directory := t.TempDir() caKey, err := rsa.GenerateKey(rand.Reader, 2048) @@ -60,7 +60,7 @@ func writeTestTLS(t *testing.T) (certFile, keyFile, caFile string) { return } -func TestNewKubernetesAttachmentLookup(t *testing.T) { +func bddTestNewKubernetesAttachmentLookup(t testing.TB) { kubeconfig := filepath.Join(t.TempDir(), "config") content := `apiVersion: v1 kind: Config @@ -93,7 +93,7 @@ users: } } -func TestLoadTLSConfigAndControllerLifecycle(t *testing.T) { +func bddTestLoadTLSConfigAndControllerLifecycle(t testing.TB) { certFile, keyFile, caFile := writeTestTLS(t) tlsConfig, err := LoadTLSConfig(certFile, keyFile, caFile) if err != nil { diff --git a/internal/csi/controller_test.go b/internal/csi/controller_test.go index 71d8505..49acaa6 100644 --- a/internal/csi/controller_test.go +++ b/internal/csi/controller_test.go @@ -265,7 +265,7 @@ func newTestController() (*Controller, *fakeVolumeClient, *fakeAdminClient, *fak return controller, volume, admin, policy, placement } -func TestControllerCreateDeleteAndPlacement(t *testing.T) { +func bddTestControllerCreateDeleteAndPlacement(t testing.TB) { controller, volume, _, _, placement := newTestController() request := &csipb.CreateVolumeRequest{ Name: "vol-1", @@ -297,7 +297,7 @@ func TestControllerCreateDeleteAndPlacement(t *testing.T) { } } -func TestControllerPublishGrantsAndUnpublishRevokes(t *testing.T) { +func bddTestControllerPublishGrantsAndUnpublishRevokes(t testing.TB) { controller, volume, admin, policy, _ := newTestController() volume.volumes = []*koujav1.Volume{{ Owner: "spiffe://kouja/controller/test", VolumeId: "vol-1", ResourceUuid: "resource-1", @@ -334,7 +334,7 @@ func TestControllerPublishGrantsAndUnpublishRevokes(t *testing.T) { } } -func TestControllerExpandStatusCapabilitiesAndIdentity(t *testing.T) { +func bddTestControllerExpandStatusCapabilitiesAndIdentity(t testing.TB) { controller, volume, _, _, _ := newTestController() volume.volumes = []*koujav1.Volume{{VolumeId: "vol-1", ResourceUuid: "resource-1", ActualSizeBytes: 2 << 20}} expanded, err := controller.ControllerExpandVolume(context.Background(), &csipb.ControllerExpandVolumeRequest{ @@ -384,7 +384,7 @@ func TestControllerExpandStatusCapabilitiesAndIdentity(t *testing.T) { } } -func TestControllerErrorsAndDuplicatePlacement(t *testing.T) { +func bddTestControllerErrorsAndDuplicatePlacement(t testing.TB) { controller, volume, _, _, placement := newTestController() if _, err := controller.CreateVolume(context.Background(), &csipb.CreateVolumeRequest{}); status.Code(err) != codes.InvalidArgument { t.Fatalf("empty create = %v", err) @@ -415,7 +415,7 @@ func TestControllerErrorsAndDuplicatePlacement(t *testing.T) { } } -func TestControllerErrorPaths(t *testing.T) { +func bddTestControllerErrorPaths(t testing.TB) { controller, volume, admin, policy, placement := newTestController() ctx := context.Background() validCreate := &csipb.CreateVolumeRequest{ diff --git a/internal/csi/ginkgo_suite_test.go b/internal/csi/ginkgo_suite_test.go new file mode 100644 index 0000000..9f9bf89 --- /dev/null +++ b/internal/csi/ginkgo_suite_test.go @@ -0,0 +1,76 @@ +package csi + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "csi Suite") +} + +var _ = Describe("unit tests", func() { + It("TestKubernetesAttachmentLookup", func() { + bddTestKubernetesAttachmentLookup(GinkgoTB()) + }) + It("TestNodeFailurePolicyAndNodeUnavailable", func() { + bddTestNodeFailurePolicyAndNodeUnavailable(GinkgoTB()) + }) + It("TestNewKubernetesAttachmentLookup", func() { + bddTestNewKubernetesAttachmentLookup(GinkgoTB()) + }) + It("TestLoadTLSConfigAndControllerLifecycle", func() { + bddTestLoadTLSConfigAndControllerLifecycle(GinkgoTB()) + }) + It("TestControllerCreateDeleteAndPlacement", func() { + bddTestControllerCreateDeleteAndPlacement(GinkgoTB()) + }) + It("TestControllerPublishGrantsAndUnpublishRevokes", func() { + bddTestControllerPublishGrantsAndUnpublishRevokes(GinkgoTB()) + }) + It("TestControllerExpandStatusCapabilitiesAndIdentity", func() { + bddTestControllerExpandStatusCapabilitiesAndIdentity(GinkgoTB()) + }) + It("TestControllerErrorsAndDuplicatePlacement", func() { + bddTestControllerErrorsAndDuplicatePlacement(GinkgoTB()) + }) + It("TestControllerErrorPaths", func() { + bddTestControllerErrorPaths(GinkgoTB()) + }) + It("TestVolumeHandleRoundTrip", func() { + bddTestVolumeHandleRoundTrip(GinkgoTB()) + }) + It("TestVolumeHandleRejectsInvalidValues", func() { + bddTestVolumeHandleRejectsInvalidValues(GinkgoTB()) + }) + It("TestProtocolValue", func() { + bddTestProtocolValue(GinkgoTB()) + }) + It("TestResolveSRV", func() { + bddTestResolveSRV(GinkgoTB()) + }) + It("TestNodeLifecycleAndExpand", func() { + bddTestNodeLifecycleAndExpand(GinkgoTB()) + }) + It("TestNodeISCSIPublishAndFailures", func() { + bddTestNodeISCSIPublishAndFailures(GinkgoTB()) + }) + It("TestNodeMiscellaneousPaths", func() { + bddTestNodeMiscellaneousPaths(GinkgoTB()) + }) + It("TestNodeCredentialAndHelpers", func() { + bddTestNodeCredentialAndHelpers(GinkgoTB()) + }) + It("TestNodeFilesystemAndWaitEdges", func() { + bddTestNodeFilesystemAndWaitEdges(GinkgoTB()) + }) + It("TestNodeSetAgentAndNoop", func() { + bddTestNodeSetAgentAndNoop(GinkgoTB()) + }) + It("TestKubernetesPlacementLifecycle", func() { + bddTestKubernetesPlacementLifecycle(GinkgoTB()) + }) +}) diff --git a/internal/csi/handle_test.go b/internal/csi/handle_test.go index 1d2cad2..5355804 100644 --- a/internal/csi/handle_test.go +++ b/internal/csi/handle_test.go @@ -2,7 +2,7 @@ package csi import "testing" -func TestVolumeHandleRoundTrip(t *testing.T) { +func bddTestVolumeHandleRoundTrip(t testing.TB) { handle := FormatVolumeHandle("host-1", "resource-1") if handle != "v1:host-1:resource-1" { t.Fatalf("handle = %q", handle) @@ -16,7 +16,7 @@ func TestVolumeHandleRoundTrip(t *testing.T) { } } -func TestVolumeHandleRejectsInvalidValues(t *testing.T) { +func bddTestVolumeHandleRejectsInvalidValues(t testing.TB) { for _, value := range []string{"", "v1:host", "v2:host:resource", "v1::resource", "v1:host:", "v1:host:resource:extra"} { if _, err := ParseVolumeHandle(value); err == nil { t.Fatalf("ParseVolumeHandle(%q) succeeded", value) @@ -24,7 +24,7 @@ func TestVolumeHandleRejectsInvalidValues(t *testing.T) { } } -func TestProtocolValue(t *testing.T) { +func bddTestProtocolValue(t testing.TB) { tests := map[string]string{ "iscsi": "PROTOCOL_ISCSI", "nvme": "PROTOCOL_NVME_TCP", diff --git a/internal/csi/inventory_test.go b/internal/csi/inventory_test.go index 16127de..19a8ed1 100644 --- a/internal/csi/inventory_test.go +++ b/internal/csi/inventory_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -func TestResolveSRV(t *testing.T) { +func bddTestResolveSRV(t testing.TB) { previous := lookupSRV t.Cleanup(func() { lookupSRV = previous }) lookupSRV = func(service, proto, name string) (string, []*net.SRV, error) { diff --git a/internal/csi/node_test.go b/internal/csi/node_test.go index c8fac53..8abbd1f 100644 --- a/internal/csi/node_test.go +++ b/internal/csi/node_test.go @@ -10,7 +10,7 @@ import ( csipb "github.com/container-storage-interface/spec/lib/go/csi" ) -func fakeCommands(t *testing.T) { +func fakeCommands(t testing.TB) { t.Helper() directory := t.TempDir() scripts := map[string]string{ @@ -46,7 +46,7 @@ exit 0 t.Setenv("PATH", directory+":"+os.Getenv("PATH")) } -func TestNodeLifecycleAndExpand(t *testing.T) { +func bddTestNodeLifecycleAndExpand(t testing.TB) { fakeCommands(t) node := NewNode("node-1", "nqn.test", "ext4") target := filepath.Join(t.TempDir(), "target") @@ -94,7 +94,7 @@ func TestNodeLifecycleAndExpand(t *testing.T) { } } -func TestNodeISCSIPublishAndFailures(t *testing.T) { +func bddTestNodeISCSIPublishAndFailures(t testing.TB) { fakeCommands(t) previous := iscsiInitiatorPath iscsiInitiatorPath = filepath.Join(t.TempDir(), "initiatorname.iscsi") @@ -131,7 +131,7 @@ func TestNodeISCSIPublishAndFailures(t *testing.T) { } } -func TestNodeMiscellaneousPaths(t *testing.T) { +func bddTestNodeMiscellaneousPaths(t testing.TB) { fakeCommands(t) node := NewNode("node-1", "nqn.test", "ext4") if err := ensureDeviceNode("definitely-missing-kouja-device"); err == nil { @@ -176,7 +176,7 @@ func TestNodeMiscellaneousPaths(t *testing.T) { } -func TestNodeCredentialAndHelpers(t *testing.T) { +func bddTestNodeCredentialAndHelpers(t testing.TB) { fakeCommands(t) node := NewNode("node-1", "nqn.test", "ext4") node.agentEndpoint = "agent-1:9443" @@ -215,7 +215,7 @@ func TestNodeCredentialAndHelpers(t *testing.T) { } } -func TestNodeFilesystemAndWaitEdges(t *testing.T) { +func bddTestNodeFilesystemAndWaitEdges(t testing.TB) { fakeCommands(t) node := NewNode("node-1", "nqn.test", "ext4") if err := ensureFilesystem(context.Background(), "/dev/null", "xfs"); err != nil { @@ -241,7 +241,7 @@ func TestNodeFilesystemAndWaitEdges(t *testing.T) { } } -func TestNodeSetAgentAndNoop(t *testing.T) { +func bddTestNodeSetAgentAndNoop(t testing.TB) { node := NewNode("node-1", "nqn.test", "") if node.FsType != "ext4" { t.Fatalf("default fs type = %q", node.FsType) diff --git a/internal/csi/placement_test.go b/internal/csi/placement_test.go index 4eb9358..0a959f1 100644 --- a/internal/csi/placement_test.go +++ b/internal/csi/placement_test.go @@ -11,7 +11,7 @@ import ( "k8s.io/client-go/dynamic/fake" ) -func TestKubernetesPlacementLifecycle(t *testing.T) { +func bddTestKubernetesPlacementLifecycle(t testing.TB) { client := fake.NewSimpleDynamicClientWithCustomListKinds( runtime.NewScheme(), map[schema.GroupVersionResource]string{placementResource: "KoujaVolumePlacementList"}, diff --git a/internal/hostid/ginkgo_suite_test.go b/internal/hostid/ginkgo_suite_test.go new file mode 100644 index 0000000..cf33f63 --- /dev/null +++ b/internal/hostid/ginkgo_suite_test.go @@ -0,0 +1,43 @@ +package hostid + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "hostid Suite") +} + +var _ = Describe("unit tests", func() { + It("TestEnsureCreatesPersistsAndRepairsFile", func() { + bddTestEnsureCreatesPersistsAndRepairsFile(GinkgoTB()) + }) + It("TestMismatchAndMissingFileWithVolumesFailClosed", func() { + bddTestMismatchAndMissingFileWithVolumesFailClosed(GinkgoTB()) + }) + It("TestMismatchWithoutVolumesRepairsSetting", func() { + bddTestMismatchWithoutVolumesRepairsSetting(GinkgoTB()) + }) + It("TestInvalidFileAndBootID", func() { + bddTestInvalidFileAndBootID(GinkgoTB()) + }) + It("TestHostIDErrorPaths", func() { + bddTestHostIDErrorPaths(GinkgoTB()) + }) + It("TestHostIDReadBootAndStoreErrors", func() { + bddTestHostIDReadBootAndStoreErrors(GinkgoTB()) + }) + It("TestHostIDEnsureReadErrors", func() { + bddTestHostIDEnsureReadErrors(GinkgoTB()) + }) + It("TestHostIDWriteAndReadFailureBranches", func() { + bddTestHostIDWriteAndReadFailureBranches(GinkgoTB()) + }) + It("TestHostIDInvalidStoredSetting", func() { + bddTestHostIDInvalidStoredSetting(GinkgoTB()) + }) +}) diff --git a/internal/hostid/hostid_test.go b/internal/hostid/hostid_test.go index 7f34e0e..8e2830c 100644 --- a/internal/hostid/hostid_test.go +++ b/internal/hostid/hostid_test.go @@ -12,7 +12,7 @@ import ( "github.com/voidstarit/kouja/internal/state" ) -func openState(t *testing.T) *state.Store { +func openState(t testing.TB) *state.Store { t.Helper() store, err := state.Open(context.Background(), filepath.Join(t.TempDir(), "state.db")) if err != nil { @@ -26,7 +26,7 @@ func openState(t *testing.T) *state.Store { return store } -func addVolume(t *testing.T, store *state.Store) { +func addVolume(t testing.TB, store *state.Store) { t.Helper() now := time.Date(2026, 11, 6, 0, 0, 0, 0, time.UTC) err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -51,7 +51,7 @@ func addVolume(t *testing.T, store *state.Store) { } } -func TestEnsureCreatesPersistsAndRepairsFile(t *testing.T) { +func bddTestEnsureCreatesPersistsAndRepairsFile(t testing.TB) { store := openState(t) path := filepath.Join(t.TempDir(), "host-id") manager := New(path, store) @@ -97,7 +97,7 @@ func TestEnsureCreatesPersistsAndRepairsFile(t *testing.T) { } } -func TestMismatchAndMissingFileWithVolumesFailClosed(t *testing.T) { +func bddTestMismatchAndMissingFileWithVolumesFailClosed(t testing.TB) { store := openState(t) addVolume(t, store) path := filepath.Join(t.TempDir(), "host-id") @@ -115,7 +115,7 @@ func TestMismatchAndMissingFileWithVolumesFailClosed(t *testing.T) { } } -func TestMismatchWithoutVolumesRepairsSetting(t *testing.T) { +func bddTestMismatchWithoutVolumesRepairsSetting(t testing.TB) { store := openState(t) path := filepath.Join(t.TempDir(), "host-id") first := uuid.NewString() @@ -148,7 +148,7 @@ func TestMismatchWithoutVolumesRepairsSetting(t *testing.T) { } } -func TestInvalidFileAndBootID(t *testing.T) { +func bddTestInvalidFileAndBootID(t testing.TB) { store := openState(t) path := filepath.Join(t.TempDir(), "host-id") if err := os.WriteFile(path, []byte("not-a-uuid\n"), 0o600); err != nil { @@ -179,7 +179,7 @@ func TestInvalidFileAndBootID(t *testing.T) { } } -func TestHostIDErrorPaths(t *testing.T) { +func bddTestHostIDErrorPaths(t testing.TB) { store := openState(t) if _, err := New("", store).Ensure(context.Background()); err == nil { t.Fatal("empty path did not fail") @@ -206,7 +206,7 @@ func TestHostIDErrorPaths(t *testing.T) { } } -func TestHostIDReadBootAndStoreErrors(t *testing.T) { +func bddTestHostIDReadBootAndStoreErrors(t testing.TB) { if value, err := ReadBootID(""); err != nil || value == "" { t.Fatalf("default boot ID = %q, %v", value, err) } @@ -223,7 +223,7 @@ func TestHostIDReadBootAndStoreErrors(t *testing.T) { } } -func TestHostIDEnsureReadErrors(t *testing.T) { +func bddTestHostIDEnsureReadErrors(t testing.TB) { store := openState(t) directory := t.TempDir() if _, err := New(directory, store).Ensure(context.Background()); err == nil { @@ -235,7 +235,7 @@ func TestHostIDEnsureReadErrors(t *testing.T) { } } -func TestHostIDWriteAndReadFailureBranches(t *testing.T) { +func bddTestHostIDWriteAndReadFailureBranches(t testing.TB) { store := openState(t) blocker := filepath.Join(t.TempDir(), "blocker") if err := os.WriteFile(blocker, []byte("x"), 0o600); err != nil { @@ -263,7 +263,7 @@ func TestHostIDWriteAndReadFailureBranches(t *testing.T) { } } -func TestHostIDInvalidStoredSetting(t *testing.T) { +func bddTestHostIDInvalidStoredSetting(t testing.TB) { store := openState(t) manager := New(filepath.Join(t.TempDir(), "host-id"), store) if err := store.Transact(context.Background(), func(tx state.Tx) error { diff --git a/internal/lvm/ginkgo_suite_test.go b/internal/lvm/ginkgo_suite_test.go new file mode 100644 index 0000000..0698d9e --- /dev/null +++ b/internal/lvm/ginkgo_suite_test.go @@ -0,0 +1,54 @@ +package lvm + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "lvm Suite") +} + +func bddSubtest(t testing.TB, name string, fn func(testing.TB)) { + By(name) + fn(t) +} + +var _ = Describe("unit tests", func() { + It("TestCreateBuildsCommandAndObservesAllocatedSize", func() { + bddTestCreateBuildsCommandAndObservesAllocatedSize(GinkgoTB()) + }) + It("TestCreateMapsResourceExhausted", func() { + bddTestCreateMapsResourceExhausted(GinkgoTB()) + }) + It("TestObserveParsesTagsAndNotFound", func() { + bddTestObserveParsesTagsAndNotFound(GinkgoTB()) + }) + It("TestCapacityAppliesHeadroom", func() { + bddTestCapacityAppliesHeadroom(GinkgoTB()) + }) + It("TestEligibilityRejectsThinClusteredMissingAndRootBacked", func() { + bddTestEligibilityRejectsThinClusteredMissingAndRootBacked(GinkgoTB()) + }) + It("TestSetOwnerTagReplacesExistingOwnerTag", func() { + bddTestSetOwnerTagReplacesExistingOwnerTag(GinkgoTB()) + }) + It("TestListManagedAndCandidates", func() { + bddTestListManagedAndCandidates(GinkgoTB()) + }) + It("TestResizeActivateRemoveCommands", func() { + bddTestResizeActivateRemoveCommands(GinkgoTB()) + }) + It("TestValidation", func() { + bddTestValidation(GinkgoTB()) + }) + It("TestOptionsCommandErrorAndSettle", func() { + bddTestOptionsCommandErrorAndSettle(GinkgoTB()) + }) + It("TestJSONAndProbeEdgeCases", func() { + bddTestJSONAndProbeEdgeCases(GinkgoTB()) + }) +}) diff --git a/internal/lvm/lvm_test.go b/internal/lvm/lvm_test.go index 4629798..bc74f55 100644 --- a/internal/lvm/lvm_test.go +++ b/internal/lvm/lvm_test.go @@ -58,7 +58,7 @@ func newTestBackend(runner Runner, options ...Option) *commandBackend { return New(options...).(*commandBackend) } -func TestCreateBuildsCommandAndObservesAllocatedSize(t *testing.T) { +func bddTestCreateBuildsCommandAndObservesAllocatedSize(t testing.TB) { runner := &fakeRunner{} runner.handler = func(name string, args []string) ([]byte, []byte, error) { switch name { @@ -100,7 +100,7 @@ func TestCreateBuildsCommandAndObservesAllocatedSize(t *testing.T) { } } -func TestCreateMapsResourceExhausted(t *testing.T) { +func bddTestCreateMapsResourceExhausted(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { if name == "lvcreate" { return nil, []byte("insufficient free space: 0 extents needed"), errors.New("exit status 5") @@ -120,7 +120,7 @@ func TestCreateMapsResourceExhausted(t *testing.T) { } } -func TestObserveParsesTagsAndNotFound(t *testing.T) { +func bddTestObserveParsesTagsAndNotFound(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { if len(args) > 0 && args[len(args)-1] == "vg1/missing" { return []byte(`{"report":[{"lv":[]}]}`), nil, nil @@ -140,7 +140,7 @@ func TestObserveParsesTagsAndNotFound(t *testing.T) { } } -func TestCapacityAppliesHeadroom(t *testing.T) { +func bddTestCapacityAppliesHeadroom(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { return jsonVG("vg1", "wz--n-", "1000", "800", "4", "0"), nil, nil }} @@ -176,7 +176,7 @@ func TestCapacityAppliesHeadroom(t *testing.T) { } } -func TestEligibilityRejectsThinClusteredMissingAndRootBacked(t *testing.T) { +func bddTestEligibilityRejectsThinClusteredMissingAndRootBacked(t testing.TB) { normalVG := jsonVG("vg1", "wz--n-", "1000", "800", "4", "0") normalLV := jsonLV("lv1", "vg1", "100", "", "/dev/mapper/vg1-lv1") tests := []struct { @@ -194,7 +194,7 @@ func TestEligibilityRejectsThinClusteredMissingAndRootBacked(t *testing.T) { {name: "root-backed", vg: normalVG, lv: []byte(`{"report":[{"lv":[{"lv_name":"root","vg_name":"vg1","lv_attr":"-wi-ao----","lv_tags":"","lv_dm_path":"/dev/mapper/vg1-root"}]}]}`), findmnt: "/dev/mapper/vg1-root\n", criticalPaths: []string{"/"}, want: ErrIneligible}, } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { + bddSubtest(t, test.name, func(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { switch name { case "vgs": @@ -221,7 +221,7 @@ func TestEligibilityRejectsThinClusteredMissingAndRootBacked(t *testing.T) { } } -func TestSetOwnerTagReplacesExistingOwnerTag(t *testing.T) { +func bddTestSetOwnerTagReplacesExistingOwnerTag(t testing.TB) { runner := &fakeRunner{} runner.handler = func(name string, args []string) ([]byte, []byte, error) { if name == "lvs" { @@ -243,7 +243,7 @@ func TestSetOwnerTagReplacesExistingOwnerTag(t *testing.T) { } } -func TestListManagedAndCandidates(t *testing.T) { +func bddTestListManagedAndCandidates(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { return []byte(`{"report":[{"lv":[ {"lv_name":"managed","vg_name":"vg1","lv_size":"1","lv_tags":"kouja.managed=true","lv_dm_path":"/dev/mapper/vg1-managed"}, @@ -267,7 +267,7 @@ func TestListManagedAndCandidates(t *testing.T) { } } -func TestResizeActivateRemoveCommands(t *testing.T) { +func bddTestResizeActivateRemoveCommands(t testing.TB) { runner := &fakeRunner{handler: func(name string, args []string) ([]byte, []byte, error) { if name == "lvs" { return jsonLV("lv1", "vg1", "2097152", "", "/dev/mapper/vg1-lv1"), nil, nil @@ -307,7 +307,7 @@ func TestResizeActivateRemoveCommands(t *testing.T) { } } -func TestValidation(t *testing.T) { +func bddTestValidation(t testing.TB) { runner := &fakeRunner{} backend := newTestBackend(runner) if _, err := backend.Create(context.Background(), "../vg", "lv1", 1, nil); !errors.Is(err, ErrInvalid) { @@ -327,7 +327,7 @@ func TestValidation(t *testing.T) { } } -func TestOptionsCommandErrorAndSettle(t *testing.T) { +func bddTestOptionsCommandErrorAndSettle(t testing.TB) { commandError := &CommandError{Name: "lvs", Args: []string{"x"}, ExitCode: 5, Stderr: "bad", Err: errors.New("exit")} if commandError.Error() == "" || !errors.Is(commandError, commandError.Err) { t.Fatal("command error did not expose its cause") @@ -352,7 +352,7 @@ func TestOptionsCommandErrorAndSettle(t *testing.T) { } } -func TestJSONAndProbeEdgeCases(t *testing.T) { +func bddTestJSONAndProbeEdgeCases(t testing.TB) { var value flexString if err := value.UnmarshalJSON(nil); err != nil || value != "" { t.Fatalf("null flex string = %q, %v", value, err) diff --git a/internal/observability/ginkgo_suite_test.go b/internal/observability/ginkgo_suite_test.go new file mode 100644 index 0000000..99d6f1d --- /dev/null +++ b/internal/observability/ginkgo_suite_test.go @@ -0,0 +1,22 @@ +package observability + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "observability Suite") +} + +var _ = Describe("unit tests", func() { + It("TestMetricsHandler", func() { + bddTestMetricsHandler(GinkgoTB()) + }) + It("TestMetricsRenderAndNilReceiver", func() { + bddTestMetricsRenderAndNilReceiver(GinkgoTB()) + }) +}) diff --git a/internal/observability/metrics_test.go b/internal/observability/metrics_test.go index 936081f..00bc47b 100644 --- a/internal/observability/metrics_test.go +++ b/internal/observability/metrics_test.go @@ -6,7 +6,7 @@ import ( "testing" ) -func TestMetricsHandler(t *testing.T) { +func bddTestMetricsHandler(t testing.TB) { metrics := New() metrics.Inc("test_counter") metrics.Set("test_gauge", 3) @@ -18,7 +18,7 @@ func TestMetricsHandler(t *testing.T) { } } -func TestMetricsRenderAndNilReceiver(t *testing.T) { +func bddTestMetricsRenderAndNilReceiver(t testing.TB) { metrics := New() metrics.Inc("counter") metrics.Set("gauge", 2) diff --git a/internal/operations/ginkgo_suite_test.go b/internal/operations/ginkgo_suite_test.go new file mode 100644 index 0000000..eced494 --- /dev/null +++ b/internal/operations/ginkgo_suite_test.go @@ -0,0 +1,76 @@ +package operations + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "operations Suite") +} + +var _ = Describe("unit tests", func() { + It("TestCreateUnexported", func() { + bddTestCreateUnexported(GinkgoTB()) + }) + It("TestCreateExportAndIdempotentReplay", func() { + bddTestCreateExportAndIdempotentReplay(GinkgoTB()) + }) + It("TestCreateDifferentParametersRejected", func() { + bddTestCreateDifferentParametersRejected(GinkgoTB()) + }) + It("TestUnexportRequiresForceAndSupersedesCredential", func() { + bddTestUnexportRequiresForceAndSupersedesCredential(GinkgoTB()) + }) + It("TestDeleteForceAndSessionGuard", func() { + bddTestDeleteForceAndSessionGuard(GinkgoTB()) + }) + It("TestResizeExtendsVolumeAndRefreshesExport", func() { + bddTestResizeExtendsVolumeAndRefreshesExport(GinkgoTB()) + }) + It("TestExecutionFailureIsJournaledAndNotReplayed", func() { + bddTestExecutionFailureIsJournaledAndNotReplayed(GinkgoTB()) + }) + It("TestTombstoneRetiresStaleDeleteOperation", func() { + bddTestTombstoneRetiresStaleDeleteOperation(GinkgoTB()) + }) + It("TestRetryRearmsFailedOperation", func() { + bddTestRetryRearmsFailedOperation(GinkgoTB()) + }) + It("TestExportDifferentInitiatorRejected", func() { + bddTestExportDifferentInitiatorRejected(GinkgoTB()) + }) + It("TestExportRotationAfterUnexportUsesNewCredential", func() { + bddTestExportRotationAfterUnexportUsesNewCredential(GinkgoTB()) + }) + It("TestExportRotationDifferentInitiator", func() { + bddTestExportRotationDifferentInitiator(GinkgoTB()) + }) + It("TestExportRotationRequiresForceForActiveSession", func() { + bddTestExportRotationRequiresForceForActiveSession(GinkgoTB()) + }) + It("TestExportRotationIsIdempotentForSameAttachment", func() { + bddTestExportRotationIsIdempotentForSameAttachment(GinkgoTB()) + }) + It("TestIntentParsersAndInternalHelpers", func() { + bddTestIntentParsersAndInternalHelpers(GinkgoTB()) + }) + It("TestOperationManagerErrorPaths", func() { + bddTestOperationManagerErrorPaths(GinkgoTB()) + }) + It("TestResizeAndDeleteGuards", func() { + bddTestResizeAndDeleteGuards(GinkgoTB()) + }) + It("TestOperationErrorAndDefaults", func() { + bddTestOperationErrorAndDefaults(GinkgoTB()) + }) + It("TestCreateCapacityGuard", func() { + bddTestCreateCapacityGuard(GinkgoTB()) + }) + It("TestResizeExportFailure", func() { + bddTestResizeExportFailure(GinkgoTB()) + }) +}) diff --git a/internal/operations/manager_test.go b/internal/operations/manager_test.go index ba599a2..1b14476 100644 --- a/internal/operations/manager_test.go +++ b/internal/operations/manager_test.go @@ -254,7 +254,7 @@ func (manager *fakeCredentials) Install(context.Context, target.ExportSpec, cred func (manager *fakeCredentials) Revoke(context.Context, target.ExportSpec) error { return nil } -func newTestManager(t *testing.T) (*Manager, *fakeLVM, *fakeExporter, *fakeCredentials, *state.Store) { +func newTestManager(t testing.TB) (*Manager, *fakeLVM, *fakeExporter, *fakeCredentials, *state.Store) { t.Helper() store, err := state.Open(context.Background(), t.TempDir()+"/state.db") if err != nil { @@ -283,7 +283,7 @@ func newTestManager(t *testing.T) (*Manager, *fakeLVM, *fakeExporter, *fakeCrede return manager, lvmBackend, exporter, credentials, store } -func createRequest(t *testing.T, volumeID, resourceUUID string, size int64, export bool) Request { +func createRequest(t testing.TB, volumeID, resourceUUID string, size int64, export bool) Request { t.Helper() intent := CreateIntent{ ResourceUUID: resourceUUID, @@ -304,7 +304,7 @@ func createRequest(t *testing.T, volumeID, resourceUUID string, size int64, expo return Request{Owner: "spiffe://kouja/caller/test", VolumeID: volumeID, Action: ActionCreate, Intent: raw} } -func getVolume(t *testing.T, store *state.Store, owner, volumeID string) (state.Volume, error) { +func getVolume(t testing.TB, store *state.Store, owner, volumeID string) (state.Volume, error) { t.Helper() var volume state.Volume err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -315,7 +315,7 @@ func getVolume(t *testing.T, store *state.Store, owner, volumeID string) (state. return volume, err } -func getOperation(t *testing.T, store *state.Store, request Request) (state.Operation, error) { +func getOperation(t testing.TB, store *state.Store, request Request) (state.Operation, error) { t.Helper() hash := mustHash(request) var operation state.Operation @@ -327,7 +327,7 @@ func getOperation(t *testing.T, store *state.Store, request Request) (state.Oper return operation, err } -func TestCreateUnexported(t *testing.T) { +func bddTestCreateUnexported(t testing.TB) { manager, lvmBackend, _, _, store := newTestManager(t) request := createRequest(t, "vol1", "resource-1", 64<<20, false) result, err := manager.Run(context.Background(), request) @@ -356,7 +356,7 @@ func TestCreateUnexported(t *testing.T) { } } -func TestCreateExportAndIdempotentReplay(t *testing.T) { +func bddTestCreateExportAndIdempotentReplay(t testing.TB) { manager, lvmBackend, exporter, _, store := newTestManager(t) request := createRequest(t, "vol1", "resource-1", 64<<20, true) result, err := manager.Run(context.Background(), request) @@ -386,7 +386,7 @@ func TestCreateExportAndIdempotentReplay(t *testing.T) { } } -func TestCreateDifferentParametersRejected(t *testing.T) { +func bddTestCreateDifferentParametersRejected(t testing.TB) { manager, _, _, _, store := newTestManager(t) request := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), request); err != nil { @@ -409,7 +409,7 @@ func TestCreateDifferentParametersRejected(t *testing.T) { } } -func TestUnexportRequiresForceAndSupersedesCredential(t *testing.T) { +func bddTestUnexportRequiresForceAndSupersedesCredential(t testing.TB) { manager, _, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, true) if _, err := manager.Run(context.Background(), create); err != nil { @@ -446,7 +446,7 @@ func TestUnexportRequiresForceAndSupersedesCredential(t *testing.T) { } } -func TestDeleteForceAndSessionGuard(t *testing.T) { +func bddTestDeleteForceAndSessionGuard(t testing.TB) { manager, lvmBackend, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, true) if _, err := manager.Run(context.Background(), create); err != nil { @@ -486,7 +486,7 @@ func TestDeleteForceAndSessionGuard(t *testing.T) { } } -func TestResizeExtendsVolumeAndRefreshesExport(t *testing.T) { +func bddTestResizeExtendsVolumeAndRefreshesExport(t testing.TB) { manager, lvmBackend, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, true) if _, err := manager.Run(context.Background(), create); err != nil { @@ -523,7 +523,7 @@ func TestResizeExtendsVolumeAndRefreshesExport(t *testing.T) { } } -func mustJSON(t *testing.T, value any) []byte { +func mustJSON(t testing.TB, value any) []byte { t.Helper() raw, err := json.Marshal(value) if err != nil { @@ -532,7 +532,7 @@ func mustJSON(t *testing.T, value any) []byte { return raw } -func TestExecutionFailureIsJournaledAndNotReplayed(t *testing.T) { +func bddTestExecutionFailureIsJournaledAndNotReplayed(t testing.TB) { manager, lvmBackend, _, _, store := newTestManager(t) lvmBackend.createErr = errors.New("storage offline") request := createRequest(t, "vol1", "resource-1", 64<<20, false) @@ -563,7 +563,7 @@ func TestExecutionFailureIsJournaledAndNotReplayed(t *testing.T) { } } -func TestTombstoneRetiresStaleDeleteOperation(t *testing.T) { +func bddTestTombstoneRetiresStaleDeleteOperation(t testing.TB) { manager, _, _, _, store := newTestManager(t) first := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), first); err != nil { @@ -586,7 +586,7 @@ func TestTombstoneRetiresStaleDeleteOperation(t *testing.T) { } } -func TestRetryRearmsFailedOperation(t *testing.T) { +func bddTestRetryRearmsFailedOperation(t testing.TB) { manager, lvmBackend, _, _, store := newTestManager(t) lvmBackend.createErr = errors.New("storage offline") request := createRequest(t, "vol1", "resource-1", 64<<20, false) @@ -614,7 +614,7 @@ func TestRetryRearmsFailedOperation(t *testing.T) { } } -func TestExportDifferentInitiatorRejected(t *testing.T) { +func bddTestExportDifferentInitiatorRejected(t testing.TB) { manager, _, _, _, _ := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -634,7 +634,7 @@ func TestExportDifferentInitiatorRejected(t *testing.T) { } } -func exportRequest(t *testing.T, owner, volumeID string, intent ExportIntent) Request { +func exportRequest(t testing.TB, owner, volumeID string, intent ExportIntent) Request { t.Helper() raw, err := json.Marshal(intent) if err != nil { @@ -643,7 +643,7 @@ func exportRequest(t *testing.T, owner, volumeID string, intent ExportIntent) Re return Request{Owner: owner, VolumeID: volumeID, Action: ActionExport, Intent: raw} } -func activeCredential(t *testing.T, store *state.Store, owner, volumeID string) state.Credential { +func activeCredential(t testing.TB, store *state.Store, owner, volumeID string) state.Credential { t.Helper() var value state.Credential if err := store.Transact(context.Background(), func(tx state.Tx) error { @@ -656,7 +656,7 @@ func activeCredential(t *testing.T, store *state.Store, owner, volumeID string) return value } -func TestExportRotationAfterUnexportUsesNewCredential(t *testing.T) { +func bddTestExportRotationAfterUnexportUsesNewCredential(t testing.TB) { manager, _, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -688,7 +688,7 @@ func TestExportRotationAfterUnexportUsesNewCredential(t *testing.T) { } } -func TestExportRotationDifferentInitiator(t *testing.T) { +func bddTestExportRotationDifferentInitiator(t testing.TB) { manager, _, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -717,7 +717,7 @@ func TestExportRotationDifferentInitiator(t *testing.T) { } } -func TestExportRotationRequiresForceForActiveSession(t *testing.T) { +func bddTestExportRotationRequiresForceForActiveSession(t testing.TB) { manager, _, exporter, _, _ := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -742,7 +742,7 @@ func TestExportRotationRequiresForceForActiveSession(t *testing.T) { } } -func TestExportRotationIsIdempotentForSameAttachment(t *testing.T) { +func bddTestExportRotationIsIdempotentForSameAttachment(t testing.TB) { manager, _, exporter, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -769,7 +769,7 @@ func TestExportRotationIsIdempotentForSameAttachment(t *testing.T) { } } -func TestIntentParsersAndInternalHelpers(t *testing.T) { +func bddTestIntentParsersAndInternalHelpers(t testing.TB) { if _, err := parseCreateIntent([]byte(`{}`)); !errors.Is(err, ErrInvalidRequest) { t.Fatalf("invalid create = %v", err) } @@ -827,7 +827,7 @@ func TestIntentParsersAndInternalHelpers(t *testing.T) { _ = store } -func TestOperationManagerErrorPaths(t *testing.T) { +func bddTestOperationManagerErrorPaths(t testing.TB) { manager, _, _, _, _ := newTestManager(t) ctx := context.Background() if _, err := manager.Run(ctx, Request{}); !errors.Is(err, ErrInvalidRequest) { @@ -862,7 +862,7 @@ func TestOperationManagerErrorPaths(t *testing.T) { } } -func TestResizeAndDeleteGuards(t *testing.T) { +func bddTestResizeAndDeleteGuards(t testing.TB) { manager, lvmBackend, _, _, store := newTestManager(t) create := createRequest(t, "vol1", "resource-1", 64<<20, false) if _, err := manager.Run(context.Background(), create); err != nil { @@ -905,7 +905,7 @@ func TestResizeAndDeleteGuards(t *testing.T) { } } -func TestOperationErrorAndDefaults(t *testing.T) { +func bddTestOperationErrorAndDefaults(t testing.TB) { plain := &OperationError{Code: "CODE", Err: errors.New("cause")} if plain.Error() == "" || !errors.Is(plain, plain.Err) { t.Fatal("plain operation error did not expose its cause") @@ -920,7 +920,7 @@ func TestOperationErrorAndDefaults(t *testing.T) { } } -func TestCreateCapacityGuard(t *testing.T) { +func bddTestCreateCapacityGuard(t testing.TB) { manager, lvmBackend, _, _, _ := newTestManager(t) lvmBackend.capacity.AllocatableBytes = 0 request := createRequest(t, "vol-capacity", "resource-capacity", 1<<20, false) @@ -929,7 +929,7 @@ func TestCreateCapacityGuard(t *testing.T) { } } -func TestResizeExportFailure(t *testing.T) { +func bddTestResizeExportFailure(t testing.TB) { manager, _, exporter, _, _ := newTestManager(t) create := createRequest(t, "vol-resize", "resource-resize", 64<<20, true) if _, err := manager.Run(context.Background(), create); err != nil { diff --git a/internal/state/ginkgo_suite_test.go b/internal/state/ginkgo_suite_test.go new file mode 100644 index 0000000..e0201bc --- /dev/null +++ b/internal/state/ginkgo_suite_test.go @@ -0,0 +1,49 @@ +package state + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "state Suite") +} + +var _ = Describe("unit tests", func() { + It("TestOpenConfiguresDatabaseAndPersistsSettings", func() { + bddTestOpenConfiguresDatabaseAndPersistsSettings(GinkgoTB()) + }) + It("TestVolumeExportObservationAndCascades", func() { + bddTestVolumeExportObservationAndCascades(GinkgoTB()) + }) + It("TestPaginationAndListing", func() { + bddTestPaginationAndListing(GinkgoTB()) + }) + It("TestOperationsAndGrants", func() { + bddTestOperationsAndGrants(GinkgoTB()) + }) + It("TestValidationForeignKeysAndAudit", func() { + bddTestValidationForeignKeysAndAudit(GinkgoTB()) + }) + It("TestSchemaNewerRejected", func() { + bddTestSchemaNewerRejected(GinkgoTB()) + }) + It("TestBackupCreatesRestorableCopy", func() { + bddTestBackupCreatesRestorableCopy(GinkgoTB()) + }) + It("TestTransactionRollback", func() { + bddTestTransactionRollback(GinkgoTB()) + }) + It("TestStateMiscellaneousCoverage", func() { + bddTestStateMiscellaneousCoverage(GinkgoTB()) + }) + It("TestStateValidationBranches", func() { + bddTestStateValidationBranches(GinkgoTB()) + }) + It("TestNormalizePageAndObservationValidation", func() { + bddTestNormalizePageAndObservationValidation(GinkgoTB()) + }) +}) diff --git a/internal/state/store_test.go b/internal/state/store_test.go index 3894a9d..212d8d4 100644 --- a/internal/state/store_test.go +++ b/internal/state/store_test.go @@ -13,7 +13,7 @@ import ( var testTime = time.Date(2026, 11, 5, 12, 0, 0, 0, time.UTC) -func openTestStore(t *testing.T) *Store { +func openTestStore(t testing.TB) *Store { t.Helper() store, err := Open(context.Background(), filepath.Join(t.TempDir(), "state.db")) if err != nil { @@ -27,7 +27,7 @@ func openTestStore(t *testing.T) *Store { return store } -func runTx(t *testing.T, store *Store, fn func(Tx) error) { +func runTx(t testing.TB, store *Store, fn func(Tx) error) { t.Helper() if err := store.Transact(context.Background(), fn); err != nil { t.Fatalf("Transact: %v", err) @@ -38,7 +38,7 @@ func withTxErr(store *Store, fn func(Tx) error) error { return store.Transact(context.Background(), fn) } -func addVG(t *testing.T, tx Tx, name string) { +func addVG(t testing.TB, tx Tx, name string) { t.Helper() if err := tx.PutVG(VG{ Name: name, @@ -51,7 +51,7 @@ func addVG(t *testing.T, tx Tx, name string) { } } -func addVolume(t *testing.T, tx Tx, owner, volumeID, vgName string) Volume { +func addVolume(t testing.TB, tx Tx, owner, volumeID, vgName string) Volume { t.Helper() volume := Volume{ Owner: owner, @@ -75,7 +75,7 @@ func addVolume(t *testing.T, tx Tx, owner, volumeID, vgName string) Volume { return volume } -func TestOpenConfiguresDatabaseAndPersistsSettings(t *testing.T) { +func bddTestOpenConfiguresDatabaseAndPersistsSettings(t testing.TB) { path := filepath.Join(t.TempDir(), "state.db") store, err := Open(context.Background(), path) if err != nil { @@ -154,7 +154,7 @@ func TestOpenConfiguresDatabaseAndPersistsSettings(t *testing.T) { } } -func TestVolumeExportObservationAndCascades(t *testing.T) { +func bddTestVolumeExportObservationAndCascades(t testing.TB) { store := openTestStore(t) runTx(t, store, func(tx Tx) error { addVG(t, tx, "vg1") @@ -319,7 +319,7 @@ func TestVolumeExportObservationAndCascades(t *testing.T) { }) } -func TestPaginationAndListing(t *testing.T) { +func bddTestPaginationAndListing(t testing.TB) { store := openTestStore(t) runTx(t, store, func(tx Tx) error { addVG(t, tx, "vg1") @@ -361,7 +361,7 @@ func TestPaginationAndListing(t *testing.T) { }) } -func TestOperationsAndGrants(t *testing.T) { +func bddTestOperationsAndGrants(t testing.TB) { store := openTestStore(t) operation := Operation{ ID: "op-1", @@ -461,7 +461,7 @@ func TestOperationsAndGrants(t *testing.T) { }) } -func TestValidationForeignKeysAndAudit(t *testing.T) { +func bddTestValidationForeignKeysAndAudit(t testing.TB) { store := openTestStore(t) runTx(t, store, func(tx Tx) error { addVG(t, tx, "vg1") @@ -557,7 +557,7 @@ func TestValidationForeignKeysAndAudit(t *testing.T) { } } -func TestSchemaNewerRejected(t *testing.T) { +func bddTestSchemaNewerRejected(t testing.TB) { path := filepath.Join(t.TempDir(), "state.db") store, err := Open(context.Background(), path) if err != nil { @@ -575,7 +575,7 @@ func TestSchemaNewerRejected(t *testing.T) { } } -func TestBackupCreatesRestorableCopy(t *testing.T) { +func bddTestBackupCreatesRestorableCopy(t testing.TB) { store := openTestStore(t) runTx(t, store, func(tx Tx) error { return tx.PutSetting("host_uuid", "host-1") @@ -611,7 +611,7 @@ func TestBackupCreatesRestorableCopy(t *testing.T) { } } -func TestTransactionRollback(t *testing.T) { +func bddTestTransactionRollback(t testing.TB) { store := openTestStore(t) marker := errors.New("rollback") if err := withTxErr(store, func(tx Tx) error { @@ -628,7 +628,7 @@ func TestTransactionRollback(t *testing.T) { }) } -func TestStateMiscellaneousCoverage(t *testing.T) { +func bddTestStateMiscellaneousCoverage(t testing.TB) { store := openTestStore(t) if SchemaVersion() == 0 { t.Fatal("schema version is zero") @@ -750,7 +750,7 @@ func TestStateMiscellaneousCoverage(t *testing.T) { } } -func TestStateValidationBranches(t *testing.T) { +func bddTestStateValidationBranches(t testing.TB) { store := openTestStore(t) runTx(t, store, func(tx Tx) error { if _, err := tx.GetVolume("", "v"); !errors.Is(err, ErrInvalid) { @@ -829,7 +829,7 @@ func TestStateValidationBranches(t *testing.T) { } } -func TestNormalizePageAndObservationValidation(t *testing.T) { +func bddTestNormalizePageAndObservationValidation(t testing.TB) { if limit, _ := normalizePage(Page{}); limit != 50 { t.Fatalf("default page = %d", limit) } diff --git a/internal/target/ginkgo_suite_test.go b/internal/target/ginkgo_suite_test.go new file mode 100644 index 0000000..449edcb --- /dev/null +++ b/internal/target/ginkgo_suite_test.go @@ -0,0 +1,70 @@ +package target + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "target Suite") +} + +var _ = Describe("unit tests", func() { + It("TestISCSIExportObserveUnexportDelete", func() { + bddTestISCSIExportObserveUnexportDelete(GinkgoTB()) + }) + It("TestISCSIUnexportUnknownSessionRequiresForce", func() { + bddTestISCSIUnexportUnknownSessionRequiresForce(GinkgoTB()) + }) + It("TestISCSIListSessions", func() { + bddTestISCSIListSessions(GinkgoTB()) + }) + It("TestISCSIValidationAndBackstoreConflict", func() { + bddTestISCSIValidationAndBackstoreConflict(GinkgoTB()) + }) + It("TestConfigFSPathValidationAndSymlink", func() { + bddTestConfigFSPathValidationAndSymlink(GinkgoTB()) + }) + It("TestISCSIResizeAndRestore", func() { + bddTestISCSIResizeAndRestore(GinkgoTB()) + }) + It("TestNVMeExportObserveUnexport", func() { + bddTestNVMeExportObserveUnexport(GinkgoTB()) + }) + It("TestNVMeValidation", func() { + bddTestNVMeValidation(GinkgoTB()) + }) + It("TestNVMeResizeDeleteRestoreAndSessions", func() { + bddTestNVMeResizeDeleteRestoreAndSessions(GinkgoTB()) + }) + It("TestConfigFSPathAndRemove", func() { + bddTestConfigFSPathAndRemove(GinkgoTB()) + }) + It("TestConfigFSErrorBranches", func() { + bddTestConfigFSErrorBranches(GinkgoTB()) + }) + It("TestNVMeExportAndResizeErrorBranches", func() { + bddTestNVMeExportAndResizeErrorBranches(GinkgoTB()) + }) + It("TestISCSIExportErrorBranches", func() { + bddTestISCSIExportErrorBranches(GinkgoTB()) + }) + It("TestTargetConstructorsAndValidation", func() { + bddTestTargetConstructorsAndValidation(GinkgoTB()) + }) + It("TestTargetNoopTeardown", func() { + bddTestTargetNoopTeardown(GinkgoTB()) + }) + It("TestIdempotentExportBranches", func() { + bddTestIdempotentExportBranches(GinkgoTB()) + }) + It("TestPortalAndComponentValidation", func() { + bddTestPortalAndComponentValidation(GinkgoTB()) + }) + It("TestDefaultConfigFSRoot", func() { + bddTestDefaultConfigFSRoot(GinkgoTB()) + }) +}) diff --git a/internal/target/iscsi_test.go b/internal/target/iscsi_test.go index 1411267..2ae3bcb 100644 --- a/internal/target/iscsi_test.go +++ b/internal/target/iscsi_test.go @@ -21,7 +21,7 @@ func (fs *removableFS) Remove(rel string) error { return os.RemoveAll(path) } -func newTestISCSI(t *testing.T) (*ISCSI, *removableFS) { +func newTestISCSI(t testing.TB) (*ISCSI, *removableFS) { t.Helper() fs := &removableFS{ConfigFS: NewConfigFS(t.TempDir())} return NewISCSI(fs), fs @@ -45,7 +45,7 @@ func testCredential() Credential { return Credential{Username: "kouja-user", Secret: "kouja-secret", Keyring: ""} } -func addSessionFile(t *testing.T, fs *ConfigFS, targetName, value string) { +func addSessionFile(t testing.TB, fs *ConfigFS, targetName, value string) { t.Helper() dir := filepath.ToSlash(filepath.Join("target/iscsi", targetName, "fabric_statistics/iscsi_instance")) if err := fs.Mkdir(dir); err != nil { @@ -56,7 +56,7 @@ func addSessionFile(t *testing.T, fs *ConfigFS, targetName, value string) { } } -func TestISCSIExportObserveUnexportDelete(t *testing.T) { +func bddTestISCSIExportObserveUnexportDelete(t testing.TB) { adapter, fs := newTestISCSI(t) spec := testSpec() result, err := adapter.Export(context.Background(), spec, testCredential()) @@ -137,7 +137,7 @@ func TestISCSIExportObserveUnexportDelete(t *testing.T) { } } -func TestISCSIUnexportUnknownSessionRequiresForce(t *testing.T) { +func bddTestISCSIUnexportUnknownSessionRequiresForce(t testing.TB) { adapter, fs := newTestISCSI(t) spec := testSpec() if _, err := adapter.Export(context.Background(), spec, testCredential()); err != nil { @@ -161,7 +161,7 @@ func TestISCSIUnexportUnknownSessionRequiresForce(t *testing.T) { } } -func TestISCSIListSessions(t *testing.T) { +func bddTestISCSIListSessions(t testing.TB) { adapter, fs := newTestISCSI(t) addSessionFile(t, fs.ConfigFS, "iqn.2026-11.io.kouja:one", "0\n") addSessionFile(t, fs.ConfigFS, "iqn.2026-11.io.kouja:two", "1\n") @@ -181,7 +181,7 @@ func TestISCSIListSessions(t *testing.T) { } } -func TestISCSIValidationAndBackstoreConflict(t *testing.T) { +func bddTestISCSIValidationAndBackstoreConflict(t testing.TB) { adapter, fs := newTestISCSI(t) spec := testSpec() if _, err := adapter.Export(context.Background(), spec, Credential{}); !errors.Is(err, ErrInvalid) { @@ -208,7 +208,7 @@ func TestISCSIValidationAndBackstoreConflict(t *testing.T) { } } -func TestConfigFSPathValidationAndSymlink(t *testing.T) { +func bddTestConfigFSPathValidationAndSymlink(t testing.TB) { fs := NewConfigFS(t.TempDir()) if _, err := fs.Path("../escape"); !errors.Is(err, ErrInvalid) { t.Fatalf("escape path = %v, want ErrInvalid", err) @@ -233,7 +233,7 @@ func TestConfigFSPathValidationAndSymlink(t *testing.T) { } } -func TestISCSIResizeAndRestore(t *testing.T) { +func bddTestISCSIResizeAndRestore(t testing.TB) { adapter, fs := newTestISCSI(t) spec := testSpec() if err := adapter.Restore(context.Background(), spec, testCredential()); err != nil { diff --git a/internal/target/nvmet_test.go b/internal/target/nvmet_test.go index c20d5c4..7b202f3 100644 --- a/internal/target/nvmet_test.go +++ b/internal/target/nvmet_test.go @@ -17,7 +17,7 @@ func nvmeSpec() ExportSpec { } } -func TestNVMeExportObserveUnexport(t *testing.T) { +func bddTestNVMeExportObserveUnexport(t testing.TB) { fs := &removableFS{ConfigFS: NewConfigFS(t.TempDir())} adapter := NewNVMe(fs) spec := nvmeSpec() @@ -64,7 +64,7 @@ func TestNVMeExportObserveUnexport(t *testing.T) { } } -func TestNVMeValidation(t *testing.T) { +func bddTestNVMeValidation(t testing.TB) { adapter := NewNVMe(&removableFS{ConfigFS: NewConfigFS(t.TempDir())}) bad := nvmeSpec() bad.Portal = "not-a-portal" @@ -78,7 +78,7 @@ func TestNVMeValidation(t *testing.T) { } } -func TestNVMeResizeDeleteRestoreAndSessions(t *testing.T) { +func bddTestNVMeResizeDeleteRestoreAndSessions(t testing.TB) { fs := &removableFS{ConfigFS: NewConfigFS(t.TempDir())} adapter := NewNVMe(fs) spec := nvmeSpec() @@ -108,7 +108,7 @@ func TestNVMeResizeDeleteRestoreAndSessions(t *testing.T) { } } -func TestConfigFSPathAndRemove(t *testing.T) { +func bddTestConfigFSPathAndRemove(t testing.TB) { fs := NewConfigFS(t.TempDir()) if fs.Root() == "" { t.Fatal("empty root") @@ -157,7 +157,7 @@ func TestConfigFSPathAndRemove(t *testing.T) { } } -func TestConfigFSErrorBranches(t *testing.T) { +func bddTestConfigFSErrorBranches(t testing.TB) { fs := NewConfigFS(t.TempDir()) if _, err := fs.Path(""); err == nil { t.Fatal("empty path did not fail") @@ -224,7 +224,7 @@ func (fs failingFS) Symlink(target, link string) error { return fs.FileSystem.Symlink(target, link) } -func TestNVMeExportAndResizeErrorBranches(t *testing.T) { +func bddTestNVMeExportAndResizeErrorBranches(t testing.TB) { for _, marker := range []string{"attr_allow_any_host", "allowed_hosts", "namespaces/1/device_path", "subsystems/"} { base := &removableFS{ConfigFS: NewConfigFS(t.TempDir())} adapter := NewNVMe(failingFS{FileSystem: base, marker: marker}) @@ -243,7 +243,7 @@ func TestNVMeExportAndResizeErrorBranches(t *testing.T) { } } -func TestISCSIExportErrorBranches(t *testing.T) { +func bddTestISCSIExportErrorBranches(t testing.TB) { base := &removableFS{ConfigFS: NewConfigFS(t.TempDir())} adapter := NewISCSI(failingFS{FileSystem: base, marker: "target/iscsi"}) if _, err := adapter.Export(context.Background(), testSpec(), testCredential()); err == nil { @@ -256,7 +256,7 @@ func TestISCSIExportErrorBranches(t *testing.T) { } } -func TestTargetConstructorsAndValidation(t *testing.T) { +func bddTestTargetConstructorsAndValidation(t testing.TB) { if NewConfigFS("").Root() == "" { t.Fatal("default configfs root is empty") } @@ -271,7 +271,7 @@ func TestTargetConstructorsAndValidation(t *testing.T) { } } -func TestTargetNoopTeardown(t *testing.T) { +func bddTestTargetNoopTeardown(t testing.TB) { nvme := NewNVMe(&removableFS{ConfigFS: NewConfigFS(t.TempDir())}) if err := nvme.Unexport(context.Background(), nvmeSpec(), true); err != nil { t.Fatalf("missing NVMe unexport: %v", err) @@ -287,7 +287,7 @@ func TestTargetNoopTeardown(t *testing.T) { } } -func TestIdempotentExportBranches(t *testing.T) { +func bddTestIdempotentExportBranches(t testing.TB) { nvme := NewNVMe(&removableFS{ConfigFS: NewConfigFS(t.TempDir())}) if _, err := nvme.Export(context.Background(), nvmeSpec(), Credential{}); err != nil { t.Fatal(err) @@ -304,7 +304,7 @@ func TestIdempotentExportBranches(t *testing.T) { } } -func TestPortalAndComponentValidation(t *testing.T) { +func bddTestPortalAndComponentValidation(t testing.TB) { if _, err := portalDirectory("bad"); err == nil { t.Fatal("bad portal did not fail") } @@ -322,7 +322,7 @@ func TestPortalAndComponentValidation(t *testing.T) { } } -func TestDefaultConfigFSRoot(t *testing.T) { +func bddTestDefaultConfigFSRoot(t testing.TB) { fs := NewConfigFS("") if fs.Root() != "/sys/kernel/config" { t.Fatalf("root = %q", fs.Root()) diff --git a/test/csi/ginkgo_suite_test.go b/test/csi/ginkgo_suite_test.go new file mode 100644 index 0000000..36a0567 --- /dev/null +++ b/test/csi/ginkgo_suite_test.go @@ -0,0 +1,21 @@ +//go:build csi_sanity + +package csi_sanity + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "csi_sanity Suite") +} + +var _ = Describe("unit tests", func() { + It("TestSanity", func() { + bddTestSanity(GinkgoTB()) + }) +}) diff --git a/test/csi/sanity_test.go b/test/csi/sanity_test.go index 2a30b53..b4f3c45 100644 --- a/test/csi/sanity_test.go +++ b/test/csi/sanity_test.go @@ -11,7 +11,7 @@ import ( // TestSanity runs the upstream csi-sanity binary when CSI_ENDPOINT is set. // It is intentionally separate from the unit suite because csi-sanity needs a // live CSI socket and, for node tests, a staging directory. -func TestSanity(t *testing.T) { +func bddTestSanity(t testing.TB) { endpoint := os.Getenv("CSI_ENDPOINT") if endpoint == "" { t.Skip("set CSI_ENDPOINT to a live CSI socket") diff --git a/test/e2e/csi_test.go b/test/e2e/csi_test.go index 7881c89..f5b1f7d 100644 --- a/test/e2e/csi_test.go +++ b/test/e2e/csi_test.go @@ -18,7 +18,7 @@ import ( // StorageClass named by KOUJA_E2E_STORAGECLASS are already installed in the // current kubectl context. It exercises dynamic provisioning, attach, login, // format, write, read, unmount, and delete. -func TestCSIIscsiProvisionAndIO(t *testing.T) { +func bddTestCSIIscsiProvisionAndIO(t testing.TB) { if os.Getenv("KOUJA_E2E") != "1" { t.Skip("set KOUJA_E2E=1 with a kubeconfig and installed kouja CSI driver") } @@ -92,7 +92,7 @@ func envOrDefault(key, fallback string) string { return fallback } -func run(t *testing.T, ctx context.Context, stdin string, args ...string) string { +func run(t testing.TB, ctx context.Context, stdin string, args ...string) string { t.Helper() var input *bytes.Reader if stdin != "" { @@ -101,12 +101,12 @@ func run(t *testing.T, ctx context.Context, stdin string, args ...string) string return kubectlReader(t, ctx, input, args...) } -func kubectl(t *testing.T, ctx context.Context, stdin *bytes.Reader, args ...string) string { +func kubectl(t testing.TB, ctx context.Context, stdin *bytes.Reader, args ...string) string { t.Helper() return kubectlReader(t, ctx, stdin, args...) } -func kubectlReader(t *testing.T, ctx context.Context, stdin *bytes.Reader, args ...string) string { +func kubectlReader(t testing.TB, ctx context.Context, stdin *bytes.Reader, args ...string) string { t.Helper() command := exec.CommandContext(ctx, "kubectl", args...) //nolint:gosec // test harness invokes the operator's kubectl if stdin != nil { @@ -119,7 +119,7 @@ func kubectlReader(t *testing.T, ctx context.Context, stdin *bytes.Reader, args return string(output) } -func waitForPhase(t *testing.T, ctx context.Context, namespace, podName, phase string) { +func waitForPhase(t testing.TB, ctx context.Context, namespace, podName, phase string) { t.Helper() ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() @@ -143,7 +143,7 @@ func waitForPhase(t *testing.T, ctx context.Context, namespace, podName, phase s // generation receives a different CHAP secret. The CSI controller must pass // the VolumeAttachment UID to the agent; repeated retries of one attachment // remain idempotent. -func TestCSICredentialRotationOnReattach(t *testing.T) { +func bddTestCSICredentialRotationOnReattach(t testing.TB) { if os.Getenv("KOUJA_E2E") != "1" { t.Skip("set KOUJA_E2E=1 with a kubeconfig and installed kouja CSI driver") } @@ -246,7 +246,7 @@ type attachmentRef struct { uid string } -func attachmentForPV(t *testing.T, ctx context.Context, pvName string) attachmentRef { +func attachmentForPV(t testing.TB, ctx context.Context, pvName string) attachmentRef { t.Helper() raw := kubectl(t, ctx, nil, "get", "volumeattachment", "-o", "json") var list volumeAttachmentList diff --git a/test/e2e/ginkgo_suite_test.go b/test/e2e/ginkgo_suite_test.go new file mode 100644 index 0000000..33f23c4 --- /dev/null +++ b/test/e2e/ginkgo_suite_test.go @@ -0,0 +1,24 @@ +//go:build e2e + +package e2e + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "e2e Suite") +} + +var _ = Describe("unit tests", func() { + It("TestCSIIscsiProvisionAndIO", func() { + bddTestCSIIscsiProvisionAndIO(GinkgoTB()) + }) + It("TestCSICredentialRotationOnReattach", func() { + bddTestCSICredentialRotationOnReattach(GinkgoTB()) + }) +}) diff --git a/test/integration/ginkgo_suite_test.go b/test/integration/ginkgo_suite_test.go new file mode 100644 index 0000000..96e18ea --- /dev/null +++ b/test/integration/ginkgo_suite_test.go @@ -0,0 +1,30 @@ +//go:build integration + +package integration + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + gomega "github.com/onsi/gomega" +) + +func TestGinkgo(t *testing.T) { + gomega.RegisterFailHandler(Fail) + RunSpecs(t, "integration Suite") +} + +var _ = Describe("unit tests", func() { + It("TestISCSIAdapterForceUnexportActiveSession", func() { + bddTestISCSIAdapterForceUnexportActiveSession(GinkgoTB()) + }) + It("TestISCSIAdapterLifecycle", func() { + bddTestISCSIAdapterLifecycle(GinkgoTB()) + }) + It("TestCommandBackendLifecycle", func() { + bddTestCommandBackendLifecycle(GinkgoTB()) + }) + It("TestNVMeTLSAdapterLifecycle", func() { + bddTestNVMeTLSAdapterLifecycle(GinkgoTB()) + }) +}) diff --git a/test/integration/iscsi_force_test.go b/test/integration/iscsi_force_test.go index 43a5b3c..ad3209a 100644 --- a/test/integration/iscsi_force_test.go +++ b/test/integration/iscsi_force_test.go @@ -13,7 +13,7 @@ import ( "github.com/voidstarit/kouja/internal/target" ) -func TestISCSIAdapterForceUnexportActiveSession(t *testing.T) { +func bddTestISCSIAdapterForceUnexportActiveSession(t testing.TB) { if os.Geteuid() != 0 { t.Skip("integration test requires root") } diff --git a/test/integration/iscsi_test.go b/test/integration/iscsi_test.go index ace303c..95d26a4 100644 --- a/test/integration/iscsi_test.go +++ b/test/integration/iscsi_test.go @@ -15,7 +15,7 @@ import ( "github.com/voidstarit/kouja/internal/target" ) -func TestISCSIAdapterLifecycle(t *testing.T) { +func bddTestISCSIAdapterLifecycle(t testing.TB) { if os.Geteuid() != 0 { t.Skip("integration test requires root") } diff --git a/test/integration/lvm_test.go b/test/integration/lvm_test.go index ccd9f52..265aa38 100644 --- a/test/integration/lvm_test.go +++ b/test/integration/lvm_test.go @@ -16,14 +16,14 @@ import ( "github.com/voidstarit/kouja/internal/lvm" ) -func requireCommand(t *testing.T, name string) { +func requireCommand(t testing.TB, name string) { t.Helper() if _, err := exec.LookPath(name); err != nil { t.Skipf("%s is not installed", name) } } -func run(t *testing.T, name string, args ...string) string { +func run(t testing.TB, name string, args ...string) string { t.Helper() command := exec.Command(name, args...) output, err := command.CombinedOutput() @@ -33,7 +33,7 @@ func run(t *testing.T, name string, args ...string) string { return strings.TrimSpace(string(output)) } -func TestCommandBackendLifecycle(t *testing.T) { +func bddTestCommandBackendLifecycle(t testing.TB) { if os.Geteuid() != 0 { t.Skip("integration test requires root") } diff --git a/test/integration/nvme_test.go b/test/integration/nvme_test.go index c8de3ee..cf4d04e 100644 --- a/test/integration/nvme_test.go +++ b/test/integration/nvme_test.go @@ -17,7 +17,7 @@ import ( "github.com/voidstarit/kouja/internal/target" ) -func TestNVMeTLSAdapterLifecycle(t *testing.T) { +func bddTestNVMeTLSAdapterLifecycle(t testing.TB) { if os.Geteuid() != 0 { t.Skip("integration test requires root") }