Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388package millproto
import ( "bytes" "encoding/binary" "strings" "testing"
millv1 "tangled.org/core/spindle/mill/proto/gen" "tangled.org/core/spindle/quota")
func TestEncodeDecodeRoundTrip(t *testing.T) { var buf bytes.Buffer enc := NewEncoder(&buf)
want := &Message{ ReserveSeat: &millv1.ReserveSeat{ LeaseId: "lease-1", TargetEngine: "microvm", RawWorkflowJson: `{"name":"build"}`, Knot: "knot.example", Rkey: "abc123", TtlSeconds: 30, RepoDid: "did:web:example.com", }, } if err := enc.Encode(want); err != nil { t.Fatalf("Encode() error = %v", err) }
got, err := NewDecoder(&buf).Decode() if err != nil { t.Fatalf("Decode() error = %v", err) } rs := got.GetReserveSeat() if rs == nil { t.Fatal("decoded message missing reserve_seat") } if rs.LeaseId != "lease-1" || rs.TargetEngine != "microvm" || rs.TtlSeconds != 30 || rs.RepoDid != "did:web:example.com" { t.Fatalf("round-trip mismatch: %+v", rs) }}
func TestDecoderRejectsOversizedMessage(t *testing.T) { var tooLarge bytes.Buffer var header [4]byte binary.BigEndian.PutUint32(header[:], MaxMessageBytes+1) tooLarge.Write(header[:])
if _, err := NewDecoder(&tooLarge).Decode(); err == nil { t.Fatal("expected oversized message error") }}
func TestValidationRules(t *testing.T) { tests := []struct { name string msg *Message wantErr bool }{ { name: "valid ack message", msg: &Message{ Ack: &millv1.Ack{ Epoch: "inc-1", UpToSeqno: 5, }, }, wantErr: false, }, { name: "valid hello message", msg: &Message{ Hello: &millv1.Hello{ProtocolVersion: 1, Arch: "amd64", Labels: []string{"linux"}, Epoch: "inc-1", CacheNamespace: "tar-zstd-v1/linux/amd64", }, }, wantErr: false, }, { name: "invalid message with zero payloads", msg: &Message{}, wantErr: true, }, { name: "invalid message with multiple payloads", msg: &Message{ Ack: &millv1.Ack{Epoch: "inc-1", UpToSeqno: 5}, Committed: &millv1.Committed{LeaseId: "x"}, }, wantErr: true, }, { name: "invalid ack message missing epoch", msg: &Message{ Ack: &millv1.Ack{ UpToSeqno: 5, }, }, wantErr: true, }, { name: "invalid node snapshot with zero seqno", msg: &Message{ NodeSnapshot: &millv1.NodeSnapshot{ Seqno: 0, }, }, wantErr: true, }, { name: "valid node snapshot with positive seqno", msg: &Message{ NodeSnapshot: &millv1.NodeSnapshot{ Seqno: 1, }, }, wantErr: false, }, { name: "invalid stream batch with zero seqno entry", msg: &Message{ EventBatch: &millv1.EventBatch{ Epoch: "inc-1", Events: []*millv1.Event{ { Seqno: 0, LeaseId: "lease-1", Payload: &millv1.Event_StatusEvent{ StatusEvent: &millv1.StatusEvent{ Status: millv1.NonterminalStatus_NONTERMINAL_STATUS_RUNNING, }, }, }, }, }, }, wantErr: true, }, { name: "invalid stream batch with empty entries", msg: &Message{ EventBatch: &millv1.EventBatch{ Epoch: "inc-1", Events: []*millv1.Event{}, }, }, wantErr: true, }, { name: "invalid reserve result with unknown enum", msg: &Message{ ReserveResult: &millv1.ReserveResult{ LeaseId: "lease-1", RejectClass: millv1.RejectClass(99), }, }, wantErr: true, }, { name: "invalid stream entry with malformed oneof (empty payload)", msg: &Message{ EventBatch: &millv1.EventBatch{ Epoch: "inc-1", Events: []*millv1.Event{ { Seqno: 1, LeaseId: "lease-1", Payload: nil, }, }, }, }, wantErr: true, }, { name: "valid stream batch status event", msg: &Message{ EventBatch: &millv1.EventBatch{ Epoch: "inc-1", Events: []*millv1.Event{ { Seqno: 1, LeaseId: "lease-1", Payload: &millv1.Event_StatusEvent{ StatusEvent: &millv1.StatusEvent{ Status: millv1.NonterminalStatus_NONTERMINAL_STATUS_RUNNING, }, }, }, }, }, }, wantErr: false, }, { name: "valid stream batch attempt result", msg: &Message{ EventBatch: &millv1.EventBatch{ Epoch: "inc-1", Events: []*millv1.Event{ { Seqno: 1, LeaseId: "lease-1", Payload: &millv1.Event_AttemptResult{ AttemptResult: &millv1.AttemptResult{ Status: millv1.TerminalStatus_TERMINAL_STATUS_SUCCESS, }, }, }, }, }, }, wantErr: false, }, { name: "valid quota reserve request", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_RESERVE, LeaseId: "lease-1", Kind: "nix_cache", Key: "key-1", Resources: map[string]int64{"cache_storage_bytes": 100}, }, }, wantErr: false, }, { name: "invalid quota request - empty request_id", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "", Operation: millv1.QuotaOperation_QUOTA_OPERATION_RESERVE, LeaseId: "lease-1", }, }, wantErr: true, }, { name: "invalid quota request - unspecified operation", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_UNSPECIFIED, LeaseId: "lease-1", }, }, wantErr: true, }, { name: "invalid quota request - empty lease_id", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_RESERVE, LeaseId: "", }, }, wantErr: true, }, { name: "invalid quota request - negative resource amount", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_RESERVE, LeaseId: "lease-1", Resources: map[string]int64{"cache_storage_bytes": -1}, }, }, wantErr: true, }, { name: "valid quota transition request with reservation_id", msg: &Message{ QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_BEGIN_COMMIT, LeaseId: "lease-1", ReservationId: "res-1", }, }, wantErr: false, }, { name: "valid reserve result reporting workflow resources", msg: &Message{ ReserveResult: &millv1.ReserveResult{ LeaseId: "lease-1", Accepted: true, QuotaResources: map[string]int64{ "workflows": 1, "memory_mib": 2048, "vcpus": 2, "disk_mib": 8192, }, }, }, wantErr: false, }, { name: "valid reserve result reporting custom resources", msg: &Message{ ReserveResult: &millv1.ReserveResult{ LeaseId: "lease-1", Accepted: true, QuotaResources: map[string]int64{ "gpu_count": 4, }, }, }, wantErr: false, }, { name: "invalid reserve result - negative resource amount", msg: &Message{ ReserveResult: &millv1.ReserveResult{ LeaseId: "lease-1", Accepted: true, QuotaResources: map[string]int64{"memory_mib": -1}, }, }, wantErr: true, }, { name: "valid quota response with resource field shape", msg: &Message{ QuotaResp: &millv1.QuotaResponse{ RequestId: "req-1", ReservationId: "res-1", Allowed: true, Resource: "cache_storage_bytes", }, }, wantErr: false, }, { name: "invalid quota response - empty request_id", msg: &Message{ QuotaResp: &millv1.QuotaResponse{ RequestId: "", }, }, wantErr: true, }, }
for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { err := validator.Validate(tc.msg) if (err != nil) != tc.wantErr { t.Fatalf("Validate() error = %v, wantErr = %v", err, tc.wantErr) } }) }}
func TestQuotaResourceMapBounds(t *testing.T) { tooMany := make(map[string]int64) for i := range quota.MaxResourcePairs + 1 { tooMany[strings.Repeat("x", i+1)] = 1 } for _, resources := range []map[string]int64{ {"": 1}, {strings.Repeat("é", 33): 1}, {"large": quota.MaxResourceAmount + 1}, tooMany, } { msg := &Message{QuotaReq: &millv1.QuotaRequest{ RequestId: "req-1", Operation: millv1.QuotaOperation_QUOTA_OPERATION_RESERVE, LeaseId: "lease-1", Kind: "custom", Key: "key", Resources: resources, }} if err := validator.Validate(msg); err == nil { t.Fatalf("expected invalid resources: %v", resources) } }}