package osd import ( "testing" ) func TestTableSelectionRequiresAnIdentifier(t *testing.T) { if (Table{}).Selected() { t.Fatal("zero generation and hash mean no table is selected yet") } if !(Table{Gen: 3}).Selected() || !(Table{Hash: 9}).Selected() { t.Fatal("either identifier alone selects a table") } } func TestCeilingFromChannelScalesTheKnobLinearly(t *testing.T) { // The temporary operator maxPower ceiling maps the full CH6 channel // range onto the 0..127 level domain. It is operator intent only. cases := []struct { channel int16 want uint8 }{ {-1024, 0}, {-1023, 0}, {0, 63}, {512, 95}, {1023, 126}, {1024, 127}, } for _, c := range cases { if got := CeilingFromChannel(c.channel); got != c.want { t.Fatalf("CeilingFromChannel(%d)=%d want %d", c.channel, got, c.want) } } if CeilingFromChannel(-2000) != 0 || CeilingFromChannel(2000) != 127 { t.Fatal("out-of-range channels must clamp to the level domain") } } func TestSnapshotCarriesFourDistinctContractValues(t *testing.T) { // The four contract values must stay distinct: every field survives a // roundtrip at its own offset, so no value can silently alias another // (for example a mode/power collapse of CH6 ceiling and applied VTX // state). snapshot := Snapshot{ Sequence: 7, TimestampMS: 99, Armed: true, Video: true, OpConfig: OperatorConfig{ Table: Table{Gen: 3, Hash: 0xdeadbeefcafe}, MaxPower: 95, }, Policy: GSPolicy{ Pending: false, Level: 42, Reason: [8]byte{'a', 'd', 'a', 'p', 't'}, TimestampMS: 1234, }, Ack: VTXAck{ Pending: false, Table: Table{Gen: 2, Hash: 0xfeed}, Level: 40, Lifecycle: VTXLifecycleApplied, }, Link: GSObservedLink{ Known: true, AgeMS: 250, }, PublishedMS: 555, } snapshot.Channels[5] = 100 got, err := Decode(Encode(snapshot)) if err != nil { t.Fatal(err) } if got != snapshot { t.Fatalf("roundtrip lost contract state: %+v != %+v", got, snapshot) } } func TestSnapshotCarriesExplicitPendingStates(t *testing.T) { // Until the policy, acknowledgment and link producers exist the // snapshot must carry their explicit pending/unknown states, never // fabricated levels or metrics. snapshot := Snapshot{ Armed: true, OpConfig: OperatorConfig{ MaxPower: 63, }, Policy: GSPolicy{Pending: true}, Ack: VTXAck{Pending: true, Lifecycle: VTXLifecyclePending}, Link: GSObservedLink{Known: false}, } got, err := Decode(Encode(snapshot)) if err != nil { t.Fatal(err) } if !got.Policy.Pending || got.Policy.Level != 0 { t.Fatalf("policy=%+v must stay explicitly pending", got.Policy) } if !got.Ack.Pending || got.Ack.Level != 0 || got.Ack.Lifecycle != VTXLifecyclePending { t.Fatalf("ack=%+v must stay explicitly pending", got.Ack) } if got.Link.Known || got.Link.AgeMS != 0 { t.Fatalf("link=%+v must stay explicitly unknown", got.Link) } if got.OpConfig != snapshot.OpConfig { t.Fatalf("operator config=%+v must survive", got.OpConfig) } } func TestDecodeRejectsV1Snapshots(t *testing.T) { // A v1-sized record cannot carry the contract; a v2-sized record that // claims v1 must be rejected by version, not parsed loosely. v1Shaped := make([]byte, Size) v1Shaped[0], v1Shaped[1], v1Shaped[2] = Magic0, Magic1, Version-1 if _, err := Decode(v1Shaped); err == nil { t.Fatal("a v1-marked record must be rejected") } } func TestVTXLifecycleStatesAreDistinct(t *testing.T) { if VTXLifecyclePending == VTXLifecycleApplied || VTXLifecycleApplied == VTXLifecycleError || VTXLifecyclePending == VTXLifecycleError { t.Fatal("pending, applied and error must be distinct lifecycle states") } }