tinyrelayfile
tinyrelay/internal/daemon/maintenance_test.go/

internal/daemon/maintenance_test.go raw
1package daemon
2
3import (
4 "context"
5 "errors"
6 "testing"
7 "time"
8)
9
10func TestMaintenanceDrainsAndRejectsNewOperations(t *testing.T) {
11 var gate maintenanceGate
12 _, finish, err := gate.beginOperation(context.Background())
13 if err != nil {
14 t.Fatal(err)
15 }
16 started := make(chan struct{})
17 finished := make(chan error, 1)
18 go func() {
19 close(started)
20 _, release, beginErr := gate.beginMaintenance(context.Background())
21 if beginErr == nil {
22 release()
23 }
24 finished <- beginErr
25 }()
26 <-started
27 select {
28 case <-finished:
29 t.Fatal("maintenance completed before active operation drained")
30 case <-time.After(20 * time.Millisecond):
31 }
32 if _, _, err := gate.beginOperation(context.Background()); !errors.Is(err, ErrMaintenance) {
33 t.Fatalf("new operation error = %v", err)
34 }
35 finish()
36 select {
37 case err := <-finished:
38 if err != nil {
39 t.Fatal(err)
40 }
41 case <-time.After(time.Second):
42 t.Fatal("maintenance did not acquire after drain")
43 }
44}
45
46func TestMaintenanceCancellationClearsAdmissionFlag(t *testing.T) {
47 var gate maintenanceGate
48 _, finish, err := gate.beginOperation(context.Background())
49 if err != nil {
50 t.Fatal(err)
51 }
52 ctx, cancel := context.WithCancel(context.Background())
53 result := make(chan error, 1)
54 go func() {
55 _, _, beginErr := gate.beginMaintenance(ctx)
56 result <- beginErr
57 }()
58 cancel()
59 if err := <-result; !errors.Is(err, context.Canceled) {
60 t.Fatalf("maintenance cancellation error = %v", err)
61 }
62 finish()
63 _, release, err := gate.beginOperation(context.Background())
64 if err != nil {
65 t.Fatalf("operation admission remained blocked: %v", err)
66 }
67 release()
68}
69
70func TestMaintenanceIsExclusiveAndNestedMarkersDoNotDeadlock(t *testing.T) {
71 var gate maintenanceGate
72 maintenanceCtx, release, err := gate.beginMaintenance(context.Background())
73 if err != nil {
74 t.Fatal(err)
75 }
76 defer release()
77 if _, nestedRelease, err := gate.beginMaintenance(maintenanceCtx); err != nil {
78 t.Fatal(err)
79 } else {
80 nestedRelease()
81 }
82 if _, _, err := gate.beginMaintenance(context.Background()); !errors.Is(err, ErrMaintenanceBusy) {
83 t.Fatalf("concurrent maintenance error = %v", err)
84 }
85 if _, nestedRelease, err := gate.beginOperation(maintenanceCtx); err != nil {
86 t.Fatal(err)
87 } else {
88 nestedRelease()
89 }
90}
91
92func TestMaintenanceOperationMarkerAllowsNestedCallbacks(t *testing.T) {
93 var gate maintenanceGate
94 ctx, release, err := gate.beginOperation(context.Background())
95 if err != nil {
96 t.Fatal(err)
97 }
98 defer release()
99 nested, nestedRelease, err := gate.beginOperation(ctx)
100 if err != nil {
101 t.Fatal(err)
102 }
103 nestedRelease()
104 if nested != ctx {
105 t.Fatal("nested operation did not preserve context marker")
106 }
107}
108
109func TestClosingPermanentlyRejectsOperationsAndMaintenance(t *testing.T) {
110 var gate maintenanceGate
111 gate.markClosing()
112 if _, _, err := gate.beginOperation(context.Background()); !errors.Is(err, ErrTenantClosing) {
113 t.Fatalf("closing operation error = %v", err)
114 }
115 if _, _, err := gate.beginMaintenance(context.Background()); !errors.Is(err, ErrTenantClosing) {
116 t.Fatalf("closing maintenance error = %v", err)
117 }
118}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs