1package app
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/neferwl/internal/adapters/sessionsecurity"
8 "github.com/bnema/neferwl/internal/ports"
9 "github.com/bnema/neferwl/internal/sessionlock"
10)
11
12// Native Close validation is exercised by DRM's generated-KMS tests. This
13// boundary test proves that even a name-discarded physical lifetime enters the
14// authoritative ledger, and only affirmative inactivity can retire it.
15func TestSecurityDuplicatePhysicalLifetimeRequiresConfirmedInactive(t *testing.T) {
16 for _, inactive := range []bool{false, true} {
17 name := "unconfirmed"
18 if inactive {
19 name = "confirmed"
20 }
21 t.Run(name, func(t *testing.T) {
22 gate := &sessionsecurity.Gate{}
23 state, err := gate.Engage()
24 if err != nil {
25 t.Fatal(err)
26 }
27 events := make(chan ports.SecurityBackendEvent, 4)
28 set := newOutputSet(context.Background(), nil)
29 set.wireSecurity(outputChannels{security: gate, securityEvents: events})
30 run := func(ctx context.Context, _ <-chan ports.Scene, _ <-chan ports.SurfaceContent, _ <-chan ports.CursorChange, _ <-chan ports.CaptureRequest, _ <-chan ports.SecurityState, _ ports.OutputInstance) error {
31 <-ctx.Done()
32 return nil
33 }
34 if !set.start(context.Background(), "DP-1", run) {
35 t.Fatal("primary output failed to start")
36 }
37 primary := (<-events).(ports.SecurityOutputAdded)
38 duplicate, err := registerDiscardedOutput(set, "DP-1")
39 if err != nil {
40 t.Fatal(err)
41 }
42 added := (<-events).(ports.SecurityOutputAdded)
43 if added.Instance != duplicate || added.Output != primary.Output || duplicate <= primary.Instance {
44 t.Fatalf("duplicate lifetime registration: primary=%+v duplicate=%+v", primary, added)
45 }
46 if len(set.outs) != 1 || set.outs["DP-1"].instance != primary.Instance {
47 t.Fatal("discarded lifetime replaced routed output")
48 }
49 var ledger sessionlock.Readiness
50 if !ledger.Add(primary.Instance) || !ledger.Add(duplicate) || !ledger.Begin(state.Generation) {
51 t.Fatal("ledger setup failed")
52 }
53 ledger.BackendBarrier(state.Generation)
54 ledger.CaptureBarrier(state.Generation)
55 ledger.Record(ports.OutputProtection{Generation: state.Generation, Instance: primary.Instance, Kind: ports.ProtectionProtectedFrame})
56 if ledger.Ready() {
57 t.Fatal("primary proof hid duplicate physical output")
58 }
59 err = retireDiscardedOutput(set, duplicate, inactive)
60 if inactive {
61 if err != nil {
62 t.Fatal(err)
63 }
64 removed := (<-events).(ports.SecurityOutputRemoved)
65 if removed.Instance != duplicate {
66 t.Fatal("removed primary instead of duplicate")
67 }
68 if !ledger.Remove(removed.Instance) || !ledger.Ready() {
69 t.Fatal("confirmed inactive duplicate did not retire")
70 }
71 } else {
72 if err == nil {
73 t.Fatal("unconfirmed close accepted")
74 }
75 select {
76 case event := <-events:
77 t.Fatalf("false retirement/proof: %+v", event)
78 default:
79 }
80 if ledger.Ready() {
81 t.Fatal("unproved duplicate permitted locked")
82 }
83 }
84 if !gate.Snapshot().Protected {
85 t.Fatal("discarding duplicate released gate")
86 }
87 if err := set.wait(); err != nil {
88 t.Fatal(err)
89 }
90 })
91 }
92}
93
94func TestSecurityDiscardedLifetimeCounterExhaustion(t *testing.T) {
95 events := make(chan ports.SecurityBackendEvent, 2)
96 set := newOutputSet(context.Background(), nil)
97 set.wireSecurity(outputChannels{securityEvents: events})
98 set.nextInstance = ^ports.OutputInstance(0) - 1
99 instance, err := registerDiscardedOutput(set, "DP-1")
100 if err != nil || instance != ^ports.OutputInstance(0) {
101 t.Fatalf("last lifetime: %d %v", instance, err)
102 }
103 if added := (<-events).(ports.SecurityOutputAdded); added.Instance != instance {
104 t.Fatal("registration differs")
105 }
106 for range 2 {
107 if instance, err := registerDiscardedOutput(set, "DP-1"); instance != 0 || err == nil {
108 t.Fatalf("exhaustion: %d %v", instance, err)
109 }
110 }
111 if set.nextInstance != ^ports.OutputInstance(0) {
112 t.Fatal("counter wrapped")
113 }
114 select {
115 case event := <-events:
116 t.Fatalf("exhaustion registered: %+v", event)
117 default:
118 }
119 if err := set.wait(); err == nil {
120 t.Fatal("exhaustion not retained")
121 }
122}