ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
96 lines
3.7 KiB
Go
96 lines
3.7 KiB
Go
package privacy
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"data-terminal-cloud/internal/outbox"
|
|
)
|
|
|
|
func TestDeleteProcessorValidationAndSafeErrors(t *testing.T) {
|
|
if _, err := NewAccessProcessor(nil); err == nil {
|
|
t.Fatal("nil access database was accepted")
|
|
}
|
|
if _, err := NewDeleteProcessor(nil, nil, nil); err == nil {
|
|
t.Fatal("invalid deletion dependencies were accepted")
|
|
}
|
|
processor := &DeleteProcessor{}
|
|
if _, _, err := processor.executeSubtask(context.Background(), deletionRequest{}, "unknown"); err == nil || !strings.Contains(err.Error(), "unsupported") {
|
|
t.Fatalf("unsupported subsystem error=%v", err)
|
|
}
|
|
if got := safeDeletionError(nil); got != "" {
|
|
t.Fatalf("nil error text=%q", got)
|
|
}
|
|
if got := safeDeletionError(errors.New("temporary object-store failure")); got != "temporary object-store failure" {
|
|
t.Fatalf("short safe error=%q", got)
|
|
}
|
|
longText := strings.Repeat("x", 600)
|
|
if got := safeDeletionError(errors.New(longText)); len(got) != 450 || strings.Contains(got, "secret") {
|
|
t.Fatalf("truncated safe error length=%d", len(got))
|
|
}
|
|
}
|
|
|
|
func TestDeletionRetryCooldownMatchesReceipt(t *testing.T) {
|
|
cause := errors.New("temporary deletion dependency failure")
|
|
for _, test := range []struct {
|
|
name string
|
|
err error
|
|
want time.Duration
|
|
}{
|
|
{"no supplier delay", outbox.Retryable(cause), 30 * time.Second},
|
|
{"short supplier delay", outbox.RetryAfter(cause, time.Second), 30 * time.Second},
|
|
{"long supplier delay", outbox.RetryAfter(cause, 2*time.Minute), 2 * time.Minute},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
got := deletionRetryError(test.err)
|
|
if !outbox.IsRetryable(got) || outbox.RetryDelay(got) != test.want || !errors.Is(got, cause) {
|
|
t.Fatalf("retry metadata or cause changed: %v delay=%v", got, outbox.RetryDelay(got))
|
|
}
|
|
})
|
|
}
|
|
if got := deletionRetryError(cause); got != cause || outbox.IsRetryable(got) {
|
|
t.Fatalf("permanent failure became retryable: %v", got)
|
|
}
|
|
if got := deletionRetryError(nil); got != nil {
|
|
t.Fatalf("nil failure became non-nil: %v", got)
|
|
}
|
|
}
|
|
|
|
func TestSafeDeletionErrorRetainsValidUTF8(t *testing.T) {
|
|
// The former 450-byte slice cut the first Chinese character in this message.
|
|
message := strings.Repeat("x", 449) + "删除依赖暂不可用"
|
|
got := safeDeletionError(errors.New(message))
|
|
if !utf8.ValidString(got) || utf8.RuneCountInString(got) != 450 || !strings.HasSuffix(got, "删") {
|
|
t.Fatalf("failure receipt is not bounded valid text: bytes=%d runes=%d", len(got), utf8.RuneCountInString(got))
|
|
}
|
|
if got := safeDeletionError(errors.New("短错误")); got != "短错误" {
|
|
t.Fatalf("short Unicode error changed: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestCallMemoryDeletionReportsOutstandingProofs(t *testing.T) {
|
|
if err := (callMemoryDeletionPending{}).err(); err != nil {
|
|
t.Fatalf("fully reconciled state was blocked: %v", err)
|
|
}
|
|
for _, test := range []struct {
|
|
pending callMemoryDeletionPending
|
|
want string
|
|
}{
|
|
{callMemoryDeletionPending{Projection: true}, "memory_projection"},
|
|
{callMemoryDeletionPending{Runtime: true}, "runtime_stop_confirmation"},
|
|
{callMemoryDeletionPending{Backup: true}, "backup_cleanup_evidence"},
|
|
{callMemoryDeletionPending{LegacyProjection: true}, "legacy_projection"},
|
|
{callMemoryDeletionPending{LegacyCleanup: true}, "legacy_cleanup_evidence"},
|
|
{callMemoryDeletionPending{Runtime: true, Backup: true}, "runtime_stop_confirmation, backup_cleanup_evidence"},
|
|
} {
|
|
err := test.pending.err()
|
|
if err == nil || err.Error() != "call memory deletion pending: "+test.want || !outbox.IsRetryable(err) || outbox.RetryDelay(err) != time.Minute {
|
|
t.Fatalf("missing cleanup evidence was hidden or acknowledged: want=%q error=%v", test.want, err)
|
|
}
|
|
}
|
|
}
|