655f5f66f8
gates / gates (push) Successful in 6m0s
- S4 / R-242: GoldenVouchChecker - a published golden unvouched for 2 h raises golden_not_vouched (operator only) - S6 / R-244: the delete cascade de-references app_log_issues; one-time sweep flag -sweep-app-log-residue=count|apply - S10 / R-266: read_failed storage entry shows "read failed", not 0 % - S29 / R-250: host-key scan IPv4 first, ~2 min ladder, "safe to press Save again" error - S30 / R-246: stale_at retired (reader, setter, badge) - S32 / R-521: storage_reconnected ends the storage_disconnected cooldown on both channels - S33 / R-402: host page "Last full check (off-site)" line; two wire-gate allowlist entries removed Each with a test seen failing first. Not built or deployed yet (operator-attended deploy). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
49 lines
2.1 KiB
Go
49 lines
2.1 KiB
Go
package offsite
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// R-250 (operator ruling 2026-10-12, S29, `09` §3 decision 224): a fresh Storage Box name resolves
|
|
// AAAA-first, and the hub pod has no IPv6 route — measured 2026-08-07: `dial tcp [2a01:…]:23: connect:
|
|
// network is unreachable` while the A record already worked. The scan dials IPv4 FIRST and falls back to
|
|
// any family only when IPv4 fails.
|
|
// RED-PROOF: with the old single `DialContext(ctx, "tcp", …)` the first network recorded is "tcp".
|
|
func TestScannerDialsIPv4First(t *testing.T) {
|
|
var nets []string
|
|
s := SSHHostKeyScanner{dial: func(_ context.Context, network, _ string) (net.Conn, error) {
|
|
nets = append(nets, network)
|
|
return nil, errors.New("refused in test")
|
|
}}
|
|
if _, err := s.Fingerprint(context.Background(), "box.example", 23); err == nil {
|
|
t.Fatal("a failed dial must fail the scan")
|
|
}
|
|
if len(nets) != 2 || nets[0] != "tcp4" || nets[1] != "tcp" {
|
|
t.Fatalf("dial order = %v, want [tcp4 tcp]", nets)
|
|
}
|
|
}
|
|
|
|
// R-250 second half: when the scan runs out, the operator is told it is safe to press again, and a
|
|
// second press really creates nothing twice. RED-PROOF: with the old wrap ("offsite: host-key scan …")
|
|
// errors.Is fails and the text has no „SAFE to press".
|
|
func TestScanOutOfRetriesSaysSafeToPressAgain(t *testing.T) {
|
|
p, fake, _ := newTestProvisioner(t)
|
|
p.Scanner = &fakeScanner{err: errors.New("dial tcp: lookup box: no such host")}
|
|
_, err := p.ProvisionOffsite(context.Background(), "cust-r250", Input{Enabled: true, Type: "shared", QuotaGB: 10})
|
|
if !errors.Is(err, ErrScanNotYetReachable) || !strings.Contains(err.Error(), "SAFE to press Save again") {
|
|
t.Fatalf("err = %v, want the safe-to-press-again error", err)
|
|
}
|
|
n := len(fake.Subaccounts)
|
|
p.Scanner = &fakeScanner{fp: "SHA256:ok"}
|
|
if _, err := p.ProvisionOffsite(context.Background(), "cust-r250", Input{Enabled: true, Type: "shared", QuotaGB: 10}); err != nil {
|
|
t.Fatalf("second press: %v", err)
|
|
}
|
|
if len(fake.Subaccounts) != n {
|
|
t.Fatalf("the second press created another sub-account (%d → %d)", n, len(fake.Subaccounts))
|
|
}
|
|
}
|