package main import ( "bytes" "os" "path/filepath" "strings" "testing" "time" ) func TestWriteJSONFileCreatesParentDirectory(t *testing.T) { path := filepath.Join(t.TempDir(), "workflow", "validation-summary.json") if err := writeJSONFile(path, map[string]bool{"valid": true}); err != nil { t.Fatal(err) } contents, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if string(contents) != "{\"valid\":true}\n" { t.Fatalf("unexpected contents %q", contents) } } func validBundle(now time.Time) bundle { return bundle{ SchemaVersion: 1, GeneratedAt: now.Add(-time.Minute), ExpiresAt: now.Add(24 * time.Hour), AdmittedIPv4: []addressRange{{Start: "192.0.2.0", End: "192.0.2.255"}, {Start: "198.51.100.1", End: "198.51.100.1"}}, ExceptionIPv4: []addressRange{{Start: "203.0.113.0", End: "203.0.113.15"}}, AdmittedIntervalCount: 2, AdmittedAddressCount: 257, ExceptionIntervalCount: 1, ExceptionAddressCount: 16, } } func TestValidateAndRender(t *testing.T) { now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC) validated, err := validateBundle(now, validBundle(now)) if err != nil { t.Fatal(err) } var output bytes.Buffer if err := render(&output, validated, "ens3"); err != nil { t.Fatal(err) } for _, expected := range []string{ "delete table inet oci_l4_relay_web_allow", "192.0.2.0-192.0.2.255", "198.51.100.1", "203.0.113.0-203.0.113.15", "iifname \"ens3\" tcp dport { 80, 443, 465, 993 }", "counter name web_allow_denied drop", } { if !strings.Contains(output.String(), expected) { t.Errorf("rendered output does not contain %q", expected) } } if strings.Contains(output.String(), "587") { t.Fatal("rendered output admits cluster-local submission") } } func TestValidationFailures(t *testing.T) { now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC) tests := map[string]func(*bundle){ "schema": func(value *bundle) { value.SchemaVersion = 2 }, "future": func(value *bundle) { value.GeneratedAt = now.Add(10 * time.Minute) }, "expiry": func(value *bundle) { value.ExpiresAt = now.Add(30 * time.Minute) }, "lifetime": func(value *bundle) { value.ExpiresAt = now.Add(8 * 24 * time.Hour) }, "overlap": func(value *bundle) { value.AdmittedIPv4[1] = addressRange{Start: "192.0.2.128", End: "192.0.2.200"} }, "reversed": func(value *bundle) { value.AdmittedIPv4[0] = addressRange{Start: "192.0.2.10", End: "192.0.2.1"} }, "ipv6": func(value *bundle) { value.AdmittedIPv4[0] = addressRange{Start: "2001:db8::1", End: "2001:db8::2"} }, "noncanonical": func(value *bundle) { value.AdmittedIPv4[0].Start = "192.000.002.000" }, "address count": func(value *bundle) { value.AdmittedAddressCount++ }, } for name, mutate := range tests { t.Run(name, func(t *testing.T) { value := validBundle(now) mutate(&value) if _, err := validateBundle(now, value); err == nil { t.Fatal("expected validation failure") } }) } } func TestRejectsUnsafeInterface(t *testing.T) { now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC) validated, err := validateBundle(now, validBundle(now)) if err != nil { t.Fatal(err) } if err := render(&bytes.Buffer{}, validated, "ens3\nflush ruleset"); err == nil { t.Fatal("expected unsafe interface rejection") } } func TestRenderFailClosed(t *testing.T) { var output bytes.Buffer if err := renderFailClosed(&output, "ens3"); err != nil { t.Fatal(err) } if !strings.Contains(output.String(), "iifname \"ens3\" tcp dport { 80, 443, 465, 993 } counter name web_allow_denied drop") { t.Fatal("fail-closed ruleset does not drop every positive-policy surface") } } func TestPolicyBounds(t *testing.T) { now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC) validated, err := validateBundle(now, validBundle(now)) if err != nil { t.Fatal(err) } if err := validatePolicy(updateConfig{minimumAddresses: 257, maximumAddresses: 257, minimumIntervals: 2, maximumExceptionAddresses: 16, maximumExceptionIntervals: 1}, validated); err != nil { t.Fatal(err) } for name, configuration := range map[string]updateConfig{ "minimum addresses": {minimumAddresses: 258, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions}, "maximum addresses": {minimumAddresses: 1, maximumAddresses: 256, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions}, "minimum intervals": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 3, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions}, "maximum exception addresses": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 15, maximumExceptionIntervals: maxExceptions}, "maximum exception intervals": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: 0}, } { t.Run(name, func(t *testing.T) { if err := validatePolicy(configuration, validated); err == nil { t.Fatal("expected policy-bound failure") } }) } } func TestAddStaticExceptions(t *testing.T) { now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC) validated, err := validateBundle(now, validBundle(now)) if err != nil { t.Fatal(err) } validated, err = addStaticExceptions(validated, []string{"203.0.113.16", "198.51.100.50"}) if err != nil { t.Fatal(err) } var output bytes.Buffer if err := render(&output, validated, "ens3"); err != nil { t.Fatal(err) } for _, expected := range []string{"198.51.100.50", "203.0.113.0-203.0.113.16"} { if !strings.Contains(output.String(), expected) { t.Errorf("rendered output does not contain %q", expected) } } }