diff --git a/internal/cnpgi/common/wal_test.go b/internal/cnpgi/common/wal_test.go index 1529800..0ce2dfd 100644 --- a/internal/cnpgi/common/wal_test.go +++ b/internal/cnpgi/common/wal_test.go @@ -21,15 +21,20 @@ package common import ( "context" + "os" + "path/filepath" barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api" barmanRestorer "github.com/cloudnative-pg/barman-cloud/pkg/restorer" cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" + "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" ) var _ = Describe("resolveRestoreObjectStore", func() { @@ -177,3 +182,65 @@ var _ = Describe("clearEndOfWALStreamFlag", func() { Expect(isEOS).To(BeFalse()) }) }) + +var _ = Describe("resolveArchiveEmptyWalArchiveCheck", func() { + // skipAnnotation mirrors the unexported constant in cloudnative-pg's + // pkg/utils; hard-coding the literal makes a divergence surface as a + // failing test rather than silently disabling the check. + const skipAnnotation = "cnpg.io/skipEmptyWalArchiveCheck" + + clusterWith := func(annotationValue *string) *cnpgv1.Cluster { + cluster := &cnpgv1.Cluster{} + if annotationValue != nil { + cluster.Annotations = map[string]string{skipAnnotation: *annotationValue} + } + return cluster + } + + // markerPath returns the marker file path inside a fresh temp dir, + // creating the file there when present is true. + markerPath := func(present bool) string { + filePath := filepath.Join(GinkgoT().TempDir(), metadata.CheckEmptyWalArchiveFile) + if present { + Expect(os.WriteFile(filePath, []byte{}, 0o600)).To(Succeed()) + } + return filePath + } + + When("the operator sets the decision", func() { + It("obeys true, ignoring the annotation and the marker file", func() { + // annotation would skip the check and the marker is absent, yet the + // operator's explicit true must still win. + got, err := resolveArchiveEmptyWalArchiveCheck( + ptr.To(true), clusterWith(ptr.To("enabled")), markerPath(false)) + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(BeTrue()) + }) + + It("obeys false, ignoring the annotation and the marker file", func() { + // annotation would keep the check on and the marker is present, yet the + // operator's explicit false must still win. + got, err := resolveArchiveEmptyWalArchiveCheck( + ptr.To(false), clusterWith(nil), markerPath(true)) + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(BeFalse()) + }) + }) + + When("the operator predates the field (nil decision)", func() { + DescribeTable( + "falls back to the annotation combined with the marker file", + func(annotationValue *string, markerPresent bool, expected bool) { + got, err := resolveArchiveEmptyWalArchiveCheck( + nil, clusterWith(annotationValue), markerPath(markerPresent)) + Expect(err).NotTo(HaveOccurred()) + Expect(got).To(Equal(expected)) + }, + Entry("no annotation and marker present: check runs", nil, true, true), + Entry("no annotation and marker absent: check skipped", nil, false, false), + Entry("opt-out annotation and marker present: check skipped", ptr.To("enabled"), true, false), + Entry("unrelated annotation value and marker present: check runs", ptr.To("something-else"), true, true), + Entry("empty annotation value and marker present: check runs", ptr.To(""), true, true), + ) + }) +}) diff --git a/internal/cnpgi/restore/restore_test.go b/internal/cnpgi/restore/restore_test.go new file mode 100644 index 0000000..a88a7f3 --- /dev/null +++ b/internal/cnpgi/restore/restore_test.go @@ -0,0 +1,66 @@ +/* +Copyright © contributors to CloudNativePG, established as +CloudNativePG a Series of LF Projects, LLC. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package restore + +import ( + cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" + "k8s.io/utils/ptr" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("resolveRestoreEmptyWalArchiveCheck", func() { + // skipAnnotation mirrors the unexported constant in cloudnative-pg's + // pkg/utils; hard-coding the literal makes a divergence surface as a + // failing test rather than silently disabling the check. + const skipAnnotation = "cnpg.io/skipEmptyWalArchiveCheck" + + clusterWith := func(annotationValue *string) *cnpgv1.Cluster { + cluster := &cnpgv1.Cluster{} + if annotationValue != nil { + cluster.Annotations = map[string]string{skipAnnotation: *annotationValue} + } + return cluster + } + + When("the operator sets the decision", func() { + It("obeys true even when the annotation would skip the check", func() { + Expect(resolveRestoreEmptyWalArchiveCheck(ptr.To(true), clusterWith(ptr.To("enabled")))).To(BeTrue()) + }) + + It("obeys false even when the annotation would keep the check on", func() { + Expect(resolveRestoreEmptyWalArchiveCheck(ptr.To(false), clusterWith(nil))).To(BeFalse()) + }) + }) + + When("the operator predates the field (nil decision)", func() { + DescribeTable( + "falls back to the Cluster annotation, never to a marker file", + func(annotationValue *string, expected bool) { + Expect(resolveRestoreEmptyWalArchiveCheck(nil, clusterWith(annotationValue))).To(Equal(expected)) + }, + Entry("no annotation: check runs", nil, true), + Entry("opt-out annotation: check skipped", ptr.To("enabled"), false), + Entry("unrelated annotation value: check runs", ptr.To("something-else"), true), + Entry("empty annotation value: check runs", ptr.To(""), true), + ) + }) +}) diff --git a/internal/cnpgi/restore/suite_test.go b/internal/cnpgi/restore/suite_test.go new file mode 100644 index 0000000..b110bef --- /dev/null +++ b/internal/cnpgi/restore/suite_test.go @@ -0,0 +1,32 @@ +/* +Copyright © contributors to CloudNativePG, established as +CloudNativePG a Series of LF Projects, LLC. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package restore + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestRestore(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Restore job hook test suite") +}