From d31211d388b376666a24d3f205a1068ebe3a655b Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 14 Aug 2026 09:10:52 +0200 Subject: [PATCH] OSAC-3704: Add regression tests for VirtualNetwork delete protection with typed references Migration 90 fixed the trigger to use typed reference paths, but lacked tests verifying the fix works. Added three tests confirming check_virtual_network_not_in_use() correctly blocks deletion when Subnets, SecurityGroups, or NATGateways reference the VirtualNetwork using typed reference format. Assisted-by: Claude Code Signed-off-by: Your Name --- ...date_triggers_for_typed_references_test.go | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/fulfillment-service/internal/database/migrations/90_update_triggers_for_typed_references_test.go b/fulfillment-service/internal/database/migrations/90_update_triggers_for_typed_references_test.go index 4a09eaab2..3b3328992 100644 --- a/fulfillment-service/internal/database/migrations/90_update_triggers_for_typed_references_test.go +++ b/fulfillment-service/internal/database/migrations/90_update_triggers_for_typed_references_test.go @@ -16,7 +16,9 @@ package migrations import ( "context" "encoding/json" + "errors" + "github.com/jackc/pgx/v5/pgconn" . "github.com/onsi/ginkgo/v2/dsl/core" . "github.com/onsi/gomega" ) @@ -125,4 +127,88 @@ var _ = DescribeMigration("Update triggers for typed references", func() { Expect(vnObj["id"]).To(Equal("vn-id")) Expect(vnObj["name"]).To(Equal("vn-name")) }) + + It("Prevents soft-deleting a virtual network referenced by a subnet with typed reference", func(ctx context.Context) { + err := tool.Migrate(ctx, 90) + Expect(err).ToNot(HaveOccurred()) + + // Insert a VirtualNetwork + _, err = conn.Exec(ctx, + `insert into virtual_networks (id, tenant, data) + values ('vn-typed', 'system', '{}')`) + Expect(err).ToNot(HaveOccurred()) + + // Insert a Subnet with typed reference format + _, err = conn.Exec(ctx, + `insert into subnets (id, tenant, data) + values ('subnet-typed', 'system', $1::jsonb)`, + `{"spec":{"virtual_network":{"id":"vn-typed","name":"vn-typed"},"ipv4_cidr":"10.0.2.0/24"}}`) + Expect(err).ToNot(HaveOccurred()) + + // Attempt to soft-delete the VirtualNetwork should fail + _, err = conn.Exec(ctx, + `update virtual_networks set deletion_timestamp = now() where id = 'vn-typed'`) + Expect(err).To(HaveOccurred()) + var pgErr *pgconn.PgError + Expect(errors.As(err, &pgErr)).To(BeTrue()) + Expect(pgErr.Code).To(Equal("Z0003")) + Expect(pgErr.Message).To(ContainSubstring("vn-typed")) + Expect(pgErr.Message).To(ContainSubstring("Subnet")) + }) + + It("Prevents soft-deleting a virtual network referenced by a security group with typed reference", func(ctx context.Context) { + err := tool.Migrate(ctx, 90) + Expect(err).ToNot(HaveOccurred()) + + // Insert a VirtualNetwork + _, err = conn.Exec(ctx, + `insert into virtual_networks (id, tenant, data) + values ('vn-sg-typed', 'system', '{}')`) + Expect(err).ToNot(HaveOccurred()) + + // Insert a SecurityGroup with typed reference format + _, err = conn.Exec(ctx, + `insert into security_groups (id, tenant, data) + values ('sg-typed', 'system', $1::jsonb)`, + `{"spec":{"virtual_network":{"id":"vn-sg-typed","name":"vn-sg-typed"}}}`) + Expect(err).ToNot(HaveOccurred()) + + // Attempt to soft-delete the VirtualNetwork should fail + _, err = conn.Exec(ctx, + `update virtual_networks set deletion_timestamp = now() where id = 'vn-sg-typed'`) + Expect(err).To(HaveOccurred()) + var pgErr *pgconn.PgError + Expect(errors.As(err, &pgErr)).To(BeTrue()) + Expect(pgErr.Code).To(Equal("Z0003")) + Expect(pgErr.Message).To(ContainSubstring("vn-sg-typed")) + Expect(pgErr.Message).To(ContainSubstring("SecurityGroup")) + }) + + It("Prevents soft-deleting a virtual network referenced by a NAT gateway with typed reference", func(ctx context.Context) { + err := tool.Migrate(ctx, 90) + Expect(err).ToNot(HaveOccurred()) + + // Insert a VirtualNetwork + _, err = conn.Exec(ctx, + `insert into virtual_networks (id, tenant, data) + values ('vn-ng-typed', 'system', '{}')`) + Expect(err).ToNot(HaveOccurred()) + + // Insert a NATGateway with typed reference format + _, err = conn.Exec(ctx, + `insert into nat_gateways (id, tenant, data) + values ('ng-typed', 'system', $1::jsonb)`, + `{"spec":{"virtual_network":{"id":"vn-ng-typed","name":"vn-ng-typed"}}}`) + Expect(err).ToNot(HaveOccurred()) + + // Attempt to soft-delete the VirtualNetwork should fail + _, err = conn.Exec(ctx, + `update virtual_networks set deletion_timestamp = now() where id = 'vn-ng-typed'`) + Expect(err).To(HaveOccurred()) + var pgErr *pgconn.PgError + Expect(errors.As(err, &pgErr)).To(BeTrue()) + Expect(pgErr.Code).To(Equal("Z0003")) + Expect(pgErr.Message).To(ContainSubstring("vn-ng-typed")) + Expect(pgErr.Message).To(ContainSubstring("NATGateway")) + }) })