Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
package importer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"evobgp/internal/store"
|
|
)
|
|
|
|
type importBackend struct {
|
|
store.Backend
|
|
created int
|
|
onCreate func()
|
|
}
|
|
|
|
func (s *importBackend) GetModule(string, string) (*store.Module, error) {
|
|
return &store.Module{Type: "AS_PREFIXES"}, nil
|
|
}
|
|
func (s *importBackend) ListCommunities(string) ([]*store.Community, error) { return nil, nil }
|
|
func (s *importBackend) CreateASEntry(_, _ string, e *store.ASEntry) (*store.ASEntry, error) {
|
|
s.created++
|
|
if s.onCreate != nil {
|
|
s.onCreate()
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
func TestCSVByteLimitRejectsValidTruncatedPrefix(t *testing.T) {
|
|
header := "asn,community\n"
|
|
for _, extra := range []int{0, 1} {
|
|
s := &importBackend{}
|
|
body := header + strings.Repeat("\n", MaxCSVBytes-len(header)) + strings.Repeat("\n", extra)
|
|
_, err := ImportModuleEntriesCSV(context.Background(), s, "tenant", "module", strings.NewReader(body))
|
|
if extra == 0 && err != nil {
|
|
t.Fatalf("exact limit: %v", err)
|
|
}
|
|
if extra == 1 && !errors.Is(err, ErrCSVTooLarge) {
|
|
t.Fatalf("limit+1: %v", err)
|
|
}
|
|
if s.created != 0 {
|
|
t.Fatal("oversized import created entries")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCSVRowLimitCheckedBeforeWrites(t *testing.T) {
|
|
for _, header := range []string{"", "asn,community\n"} {
|
|
for _, extra := range []int{0, 1} {
|
|
s := &importBackend{}
|
|
_, err := ImportModuleEntriesCSV(context.Background(), s, "tenant", "module", strings.NewReader(header+strings.Repeat("64512,\n", MaxCSVRows+extra)))
|
|
if extra == 0 && (err != nil || s.created != MaxCSVRows) {
|
|
t.Fatalf("exact rows: created=%d err=%v", s.created, err)
|
|
}
|
|
if extra == 1 && (!errors.Is(err, ErrCSVTooLarge) || s.created != 0) {
|
|
t.Fatalf("rows+1: created=%d err=%v", s.created, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCSVCancellationStopsFurtherWrites(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
s := &importBackend{onCreate: cancel}
|
|
_, err := ImportModuleEntriesCSV(ctx, s, "tenant", "module", strings.NewReader("asn,community\n64512,\n64513,\n"))
|
|
if !errors.Is(err, context.Canceled) || s.created != 1 {
|
|
t.Fatalf("created=%d err=%v", s.created, err)
|
|
}
|
|
}
|
|
|
|
func TestCSVFieldLimitAndQuotedFields(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name, input string
|
|
oversized bool
|
|
}{
|
|
{"exact", "64512," + strings.Repeat(",", MaxCSVFields-2) + "\n", false},
|
|
{"over", "64512," + strings.Repeat(",", MaxCSVFields-1) + "\n", true},
|
|
{"many_fields", strings.Repeat(",", 1<<20) + "\n", true},
|
|
{"quoted_extra", "64512,,\"" + strings.Repeat(",", 128) + "\nquoted \"\"text\"\"\"\n", false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s := &importBackend{}
|
|
_, err := ImportModuleEntriesCSV(context.Background(), s, "tenant", "module", strings.NewReader(tc.input))
|
|
if tc.oversized {
|
|
if !errors.Is(err, ErrCSVTooLarge) || s.created != 0 {
|
|
t.Fatalf("created=%d err=%v", s.created, err)
|
|
}
|
|
} else if err != nil || s.created != 1 {
|
|
t.Fatalf("created=%d err=%v", s.created, err)
|
|
}
|
|
})
|
|
}
|
|
}
|