lex-extinction 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/legion/extensions/extinction/actors/protocol_monitor.rb +61 -24
- data/lib/legion/extensions/extinction/client.rb +6 -8
- data/lib/legion/extensions/extinction/helpers/archiver.rb +58 -0
- data/lib/legion/extensions/extinction/helpers/levels.rb +43 -18
- data/lib/legion/extensions/extinction/helpers/protocol_state.rb +67 -73
- data/lib/legion/extensions/extinction/runners/extinction.rb +125 -79
- data/lib/legion/extensions/extinction/settings.rb +26 -0
- data/lib/legion/extensions/extinction/version.rb +1 -1
- data/lib/legion/extensions/extinction.rb +12 -12
- metadata +148 -16
- data/Gemfile +0 -10
- data/lex-extinction.gemspec +0 -28
- data/lib/legion/extensions/extinction/local_migrations/20260316000040_create_extinction_state.rb +0 -13
- data/spec/legion/extensions/extinction/actors/protocol_monitor_spec.rb +0 -45
- data/spec/legion/extensions/extinction/client_spec.rb +0 -13
- data/spec/legion/extensions/extinction/helpers/levels_spec.rb +0 -180
- data/spec/legion/extensions/extinction/helpers/protocol_state_spec.rb +0 -291
- data/spec/legion/extensions/extinction/runners/extinction_spec.rb +0 -114
- data/spec/local_persistence_spec.rb +0 -188
- data/spec/spec_helper.rb +0 -20
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'legion/extensions/extinction/helpers/levels'
|
|
4
|
-
|
|
5
|
-
RSpec.describe Legion::Extensions::Extinction::Helpers::Levels do
|
|
6
|
-
describe 'ESCALATION_LEVELS' do
|
|
7
|
-
it 'defines exactly four levels' do
|
|
8
|
-
expect(described_class::ESCALATION_LEVELS.size).to eq(4)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
it 'is keyed by integers 1 through 4' do
|
|
12
|
-
expect(described_class::ESCALATION_LEVELS.keys).to eq([1, 2, 3, 4])
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'is frozen' do
|
|
16
|
-
expect(described_class::ESCALATION_LEVELS).to be_frozen
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
it 'defines level 1 as mesh_isolation' do
|
|
20
|
-
expect(described_class::ESCALATION_LEVELS[1][:name]).to eq(:mesh_isolation)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'defines level 2 as forced_sentinel' do
|
|
24
|
-
expect(described_class::ESCALATION_LEVELS[2][:name]).to eq(:forced_sentinel)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'defines level 3 as full_suspension' do
|
|
28
|
-
expect(described_class::ESCALATION_LEVELS[3][:name]).to eq(:full_suspension)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'defines level 4 as cryptographic_erasure' do
|
|
32
|
-
expect(described_class::ESCALATION_LEVELS[4][:name]).to eq(:cryptographic_erasure)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'marks levels 1-3 as reversible' do
|
|
36
|
-
[1, 2, 3].each do |level|
|
|
37
|
-
expect(described_class::ESCALATION_LEVELS[level][:reversible]).to be true
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it 'marks level 4 as not reversible' do
|
|
42
|
-
expect(described_class::ESCALATION_LEVELS[4][:reversible]).to be false
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it 'assigns governance_council authority to levels 1 and 2' do
|
|
46
|
-
expect(described_class::ESCALATION_LEVELS[1][:authority]).to eq(:governance_council)
|
|
47
|
-
expect(described_class::ESCALATION_LEVELS[2][:authority]).to eq(:governance_council)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it 'assigns council_plus_executive authority to level 3' do
|
|
51
|
-
expect(described_class::ESCALATION_LEVELS[3][:authority]).to eq(:council_plus_executive)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it 'assigns physical_keyholders authority to level 4' do
|
|
55
|
-
expect(described_class::ESCALATION_LEVELS[4][:authority]).to eq(:physical_keyholders)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
describe 'VALID_LEVELS' do
|
|
60
|
-
it 'contains exactly [1, 2, 3, 4]' do
|
|
61
|
-
expect(described_class::VALID_LEVELS).to eq([1, 2, 3, 4])
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it 'is frozen' do
|
|
65
|
-
expect(described_class::VALID_LEVELS).to be_frozen
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
describe '.valid_level?' do
|
|
70
|
-
it 'returns true for level 1' do
|
|
71
|
-
expect(described_class.valid_level?(1)).to be true
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
it 'returns true for level 2' do
|
|
75
|
-
expect(described_class.valid_level?(2)).to be true
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
it 'returns true for level 3' do
|
|
79
|
-
expect(described_class.valid_level?(3)).to be true
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it 'returns true for level 4' do
|
|
83
|
-
expect(described_class.valid_level?(4)).to be true
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it 'returns false for level 0' do
|
|
87
|
-
expect(described_class.valid_level?(0)).to be false
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'returns false for level 5' do
|
|
91
|
-
expect(described_class.valid_level?(5)).to be false
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
it 'returns false for negative levels' do
|
|
95
|
-
expect(described_class.valid_level?(-1)).to be false
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it 'returns false for nil' do
|
|
99
|
-
expect(described_class.valid_level?(nil)).to be false
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it 'returns false for string level' do
|
|
103
|
-
expect(described_class.valid_level?('1')).to be false
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
describe '.level_info' do
|
|
108
|
-
it 'returns the full info hash for a valid level' do
|
|
109
|
-
info = described_class.level_info(1)
|
|
110
|
-
expect(info).to be_a(Hash)
|
|
111
|
-
expect(info.keys).to contain_exactly(:name, :reversible, :authority)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
it 'returns nil for an invalid level' do
|
|
115
|
-
expect(described_class.level_info(99)).to be_nil
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
it 'returns nil for level 0' do
|
|
119
|
-
expect(described_class.level_info(0)).to be_nil
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
[1, 2, 3, 4].each do |level|
|
|
123
|
-
it "returns a hash for level #{level}" do
|
|
124
|
-
expect(described_class.level_info(level)).to be_a(Hash)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
describe '.reversible?' do
|
|
130
|
-
it 'returns true for level 1' do
|
|
131
|
-
expect(described_class.reversible?(1)).to be true
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it 'returns true for level 2' do
|
|
135
|
-
expect(described_class.reversible?(2)).to be true
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
it 'returns true for level 3' do
|
|
139
|
-
expect(described_class.reversible?(3)).to be true
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
it 'returns false for level 4' do
|
|
143
|
-
expect(described_class.reversible?(4)).to be false
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
it 'returns false for an invalid level (fallback to false)' do
|
|
147
|
-
expect(described_class.reversible?(99)).to be false
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
it 'returns false for nil level' do
|
|
151
|
-
expect(described_class.reversible?(nil)).to be false
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
describe '.required_authority' do
|
|
156
|
-
it 'returns :governance_council for level 1' do
|
|
157
|
-
expect(described_class.required_authority(1)).to eq(:governance_council)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it 'returns :governance_council for level 2' do
|
|
161
|
-
expect(described_class.required_authority(2)).to eq(:governance_council)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
it 'returns :council_plus_executive for level 3' do
|
|
165
|
-
expect(described_class.required_authority(3)).to eq(:council_plus_executive)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
it 'returns :physical_keyholders for level 4' do
|
|
169
|
-
expect(described_class.required_authority(4)).to eq(:physical_keyholders)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
it 'returns nil for an invalid level' do
|
|
173
|
-
expect(described_class.required_authority(0)).to be_nil
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it 'returns nil for an out-of-range level' do
|
|
177
|
-
expect(described_class.required_authority(5)).to be_nil
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'legion/extensions/extinction/helpers/levels'
|
|
4
|
-
require 'legion/extensions/extinction/helpers/protocol_state'
|
|
5
|
-
|
|
6
|
-
RSpec.describe Legion::Extensions::Extinction::Helpers::ProtocolState do
|
|
7
|
-
subject(:state) { described_class.new }
|
|
8
|
-
|
|
9
|
-
describe '#initialize' do
|
|
10
|
-
it 'starts at level 0' do
|
|
11
|
-
expect(state.current_level).to eq(0)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it 'starts as inactive' do
|
|
15
|
-
expect(state.active).to be false
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'starts with an empty history' do
|
|
19
|
-
expect(state.history).to eq([])
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
describe '#escalate' do
|
|
24
|
-
context 'with an invalid level' do
|
|
25
|
-
it 'returns :invalid_level for level 0' do
|
|
26
|
-
expect(state.escalate(0, authority: :governance_council, reason: 'test')).to eq(:invalid_level)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it 'returns :invalid_level for level 5' do
|
|
30
|
-
expect(state.escalate(5, authority: :governance_council, reason: 'test')).to eq(:invalid_level)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'does not change current_level on invalid level' do
|
|
34
|
-
state.escalate(99, authority: :governance_council, reason: 'test')
|
|
35
|
-
expect(state.current_level).to eq(0)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it 'does not add to history on invalid level' do
|
|
39
|
-
state.escalate(99, authority: :governance_council, reason: 'test')
|
|
40
|
-
expect(state.history).to be_empty
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
context 'when already at or above the requested level' do
|
|
45
|
-
before { state.escalate(2, authority: :governance_council, reason: 'setup') }
|
|
46
|
-
|
|
47
|
-
it 'returns :already_at_or_above for same level' do
|
|
48
|
-
expect(state.escalate(2, authority: :governance_council, reason: 'test')).to eq(:already_at_or_above)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it 'returns :already_at_or_above for lower level' do
|
|
52
|
-
expect(state.escalate(1, authority: :governance_council, reason: 'test')).to eq(:already_at_or_above)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
context 'with insufficient authority' do
|
|
57
|
-
it 'returns :insufficient_authority for level 1 with wrong authority' do
|
|
58
|
-
result = state.escalate(1, authority: :physical_keyholders, reason: 'test')
|
|
59
|
-
expect(result).to eq(:insufficient_authority)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'returns :insufficient_authority for level 3 with governance_council' do
|
|
63
|
-
state.escalate(1, authority: :governance_council, reason: 'step1')
|
|
64
|
-
state.escalate(2, authority: :governance_council, reason: 'step2')
|
|
65
|
-
result = state.escalate(3, authority: :governance_council, reason: 'test')
|
|
66
|
-
expect(result).to eq(:insufficient_authority)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'returns :insufficient_authority for level 4 with governance_council' do
|
|
70
|
-
result = state.escalate(4, authority: :governance_council, reason: 'test')
|
|
71
|
-
expect(result).to eq(:insufficient_authority)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
it 'does not change level on insufficient authority' do
|
|
75
|
-
state.escalate(1, authority: :physical_keyholders, reason: 'test')
|
|
76
|
-
expect(state.current_level).to eq(0)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
context 'with valid escalation' do
|
|
81
|
-
it 'returns :escalated for level 1 with governance_council' do
|
|
82
|
-
expect(state.escalate(1, authority: :governance_council, reason: 'threat')).to eq(:escalated)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it 'updates current_level to 1' do
|
|
86
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
87
|
-
expect(state.current_level).to eq(1)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
it 'sets active to true' do
|
|
91
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
92
|
-
expect(state.active).to be true
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it 'appends an entry to history' do
|
|
96
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
97
|
-
expect(state.history.size).to eq(1)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it 'records the correct action in history' do
|
|
101
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
102
|
-
expect(state.history.last[:action]).to eq(:escalate)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it 'records the level in history' do
|
|
106
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
107
|
-
expect(state.history.last[:level]).to eq(1)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it 'records the authority in history' do
|
|
111
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
112
|
-
expect(state.history.last[:authority]).to eq(:governance_council)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
it 'records the reason in history' do
|
|
116
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
117
|
-
expect(state.history.last[:reason]).to eq('threat')
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
it 'records a Time in history' do
|
|
121
|
-
state.escalate(1, authority: :governance_council, reason: 'threat')
|
|
122
|
-
expect(state.history.last[:at]).to be_a(Time)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it 'escalates to level 4 with physical_keyholders after reaching level 3' do
|
|
126
|
-
state.escalate(1, authority: :governance_council, reason: 's1')
|
|
127
|
-
state.escalate(2, authority: :governance_council, reason: 's2')
|
|
128
|
-
state.escalate(3, authority: :council_plus_executive, reason: 's3')
|
|
129
|
-
result = state.escalate(4, authority: :physical_keyholders, reason: 's4')
|
|
130
|
-
expect(result).to eq(:escalated)
|
|
131
|
-
expect(state.current_level).to eq(4)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it 'accumulates history across multiple escalations' do
|
|
135
|
-
state.escalate(1, authority: :governance_council, reason: 'r1')
|
|
136
|
-
state.escalate(2, authority: :governance_council, reason: 'r2')
|
|
137
|
-
expect(state.history.size).to eq(2)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
it 'calls save_to_local after escalation' do
|
|
141
|
-
expect(state).to receive(:save_to_local)
|
|
142
|
-
state.escalate(1, authority: :governance_council, reason: 'test')
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it 'trims history beyond MAX_HISTORY' do
|
|
146
|
-
s = described_class.new
|
|
147
|
-
s.instance_variable_set(:@history, Array.new(510) { { action: :escalate, at: Time.now } })
|
|
148
|
-
s.send(:trim_history)
|
|
149
|
-
expect(s.history.size).to eq(500)
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
describe '#deescalate' do
|
|
155
|
-
context 'when protocol is not active' do
|
|
156
|
-
it 'returns :not_active' do
|
|
157
|
-
expect(state.deescalate(0, authority: :governance_council, reason: 'test')).to eq(:not_active)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
context 'when target_level is invalid (not lower than current)' do
|
|
162
|
-
before { state.escalate(2, authority: :governance_council, reason: 'setup') }
|
|
163
|
-
|
|
164
|
-
it 'returns :invalid_target when target equals current' do
|
|
165
|
-
result = state.deescalate(2, authority: :governance_council, reason: 'test')
|
|
166
|
-
expect(result).to eq(:invalid_target)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it 'returns :invalid_target when target is above current' do
|
|
170
|
-
result = state.deescalate(3, authority: :governance_council, reason: 'test')
|
|
171
|
-
expect(result).to eq(:invalid_target)
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
context 'when current level is irreversible (level 4)' do
|
|
176
|
-
before do
|
|
177
|
-
state.escalate(1, authority: :governance_council, reason: 's1')
|
|
178
|
-
state.escalate(2, authority: :governance_council, reason: 's2')
|
|
179
|
-
state.escalate(3, authority: :council_plus_executive, reason: 's3')
|
|
180
|
-
state.escalate(4, authority: :physical_keyholders, reason: 's4')
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
it 'returns :irreversible' do
|
|
184
|
-
result = state.deescalate(0, authority: :physical_keyholders, reason: 'try')
|
|
185
|
-
expect(result).to eq(:irreversible)
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
it 'does not change current_level' do
|
|
189
|
-
state.deescalate(0, authority: :physical_keyholders, reason: 'try')
|
|
190
|
-
expect(state.current_level).to eq(4)
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
context 'with insufficient authority for deescalation' do
|
|
195
|
-
before do
|
|
196
|
-
state.escalate(1, authority: :governance_council, reason: 's1')
|
|
197
|
-
state.escalate(2, authority: :governance_council, reason: 's2')
|
|
198
|
-
state.escalate(3, authority: :council_plus_executive, reason: 's3')
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
it 'rejects wrong authority for level 3' do
|
|
202
|
-
result = state.deescalate(0, authority: :governance_council, reason: 'test')
|
|
203
|
-
expect(result).to eq(:insufficient_authority)
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it 'accepts correct authority for level 3' do
|
|
207
|
-
result = state.deescalate(0, authority: :council_plus_executive, reason: 'test')
|
|
208
|
-
expect(result).to eq(:deescalated)
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
context 'with valid de-escalation' do
|
|
213
|
-
before { state.escalate(2, authority: :governance_council, reason: 'setup') }
|
|
214
|
-
|
|
215
|
-
it 'returns :deescalated' do
|
|
216
|
-
result = state.deescalate(1, authority: :governance_council, reason: 'resolved')
|
|
217
|
-
expect(result).to eq(:deescalated)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
it 'updates current_level to the target' do
|
|
221
|
-
state.deescalate(1, authority: :governance_council, reason: 'resolved')
|
|
222
|
-
expect(state.current_level).to eq(1)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
it 'remains active when target_level is positive' do
|
|
226
|
-
state.deescalate(1, authority: :governance_council, reason: 'resolved')
|
|
227
|
-
expect(state.active).to be true
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
it 'becomes inactive when target_level is 0' do
|
|
231
|
-
state.deescalate(0, authority: :governance_council, reason: 'fully resolved')
|
|
232
|
-
expect(state.active).to be false
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it 'appends a deescalate entry to history' do
|
|
236
|
-
state.deescalate(0, authority: :governance_council, reason: 'resolved')
|
|
237
|
-
last = state.history.last
|
|
238
|
-
expect(last[:action]).to eq(:deescalate)
|
|
239
|
-
expect(last[:level]).to eq(0)
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
it 'records the reason in history' do
|
|
243
|
-
state.deescalate(0, authority: :governance_council, reason: 'resolved')
|
|
244
|
-
expect(state.history.last[:reason]).to eq('resolved')
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
it 'records a Time in the deescalate history entry' do
|
|
248
|
-
state.deescalate(0, authority: :governance_council, reason: 'resolved')
|
|
249
|
-
expect(state.history.last[:at]).to be_a(Time)
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
it 'calls save_to_local after deescalation' do
|
|
253
|
-
expect(state).to receive(:save_to_local)
|
|
254
|
-
state.deescalate(0, authority: :governance_council, reason: 'test')
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
describe '#to_h' do
|
|
260
|
-
it 'returns a hash' do
|
|
261
|
-
expect(state.to_h).to be_a(Hash)
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
it 'includes current_level' do
|
|
265
|
-
expect(state.to_h[:current_level]).to eq(0)
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
it 'includes active' do
|
|
269
|
-
expect(state.to_h[:active]).to be false
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
it 'includes history_size' do
|
|
273
|
-
expect(state.to_h[:history_size]).to eq(0)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
it 'includes level_info as nil when at level 0' do
|
|
277
|
-
expect(state.to_h[:level_info]).to be_nil
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
it 'includes non-nil level_info when escalated' do
|
|
281
|
-
state.escalate(1, authority: :governance_council, reason: 'test')
|
|
282
|
-
expect(state.to_h[:level_info]).not_to be_nil
|
|
283
|
-
expect(state.to_h[:level_info][:name]).to eq(:mesh_isolation)
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
it 'reflects updated history_size after escalation' do
|
|
287
|
-
state.escalate(1, authority: :governance_council, reason: 'test')
|
|
288
|
-
expect(state.to_h[:history_size]).to eq(1)
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
end
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'legion/extensions/extinction/client'
|
|
4
|
-
|
|
5
|
-
RSpec.describe Legion::Extensions::Extinction::Runners::Extinction do
|
|
6
|
-
let(:client) { Legion::Extensions::Extinction::Client.new }
|
|
7
|
-
|
|
8
|
-
describe '#escalate' do
|
|
9
|
-
it 'escalates to level 1' do
|
|
10
|
-
result = client.escalate(level: 1, authority: :governance_council, reason: 'threat detected')
|
|
11
|
-
expect(result[:escalated]).to be true
|
|
12
|
-
expect(result[:level]).to eq(1)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'rejects wrong authority' do
|
|
16
|
-
result = client.escalate(level: 4, authority: :governance_council, reason: 'test')
|
|
17
|
-
expect(result[:escalated]).to be false
|
|
18
|
-
expect(result[:reason]).to eq(:insufficient_authority)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
it 'rejects escalation to same or lower level' do
|
|
22
|
-
client.escalate(level: 2, authority: :governance_council, reason: 'test')
|
|
23
|
-
result = client.escalate(level: 1, authority: :governance_council, reason: 'test')
|
|
24
|
-
expect(result[:escalated]).to be false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
it 'escalates to level 4 with physical keyholders' do
|
|
28
|
-
client.escalate(level: 1, authority: :governance_council, reason: 'step 1')
|
|
29
|
-
client.escalate(level: 2, authority: :governance_council, reason: 'step 2')
|
|
30
|
-
client.escalate(level: 3, authority: :council_plus_executive, reason: 'step 3')
|
|
31
|
-
result = client.escalate(level: 4, authority: :physical_keyholders, reason: 'final')
|
|
32
|
-
expect(result[:escalated]).to be true
|
|
33
|
-
expect(result[:info][:reversible]).to be false
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
describe '#deescalate' do
|
|
38
|
-
it 'deescalates from reversible level' do
|
|
39
|
-
client.escalate(level: 2, authority: :governance_council, reason: 'test')
|
|
40
|
-
result = client.deescalate(target_level: 0, authority: :governance_council, reason: 'resolved')
|
|
41
|
-
expect(result[:deescalated]).to be true
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'cannot deescalate level 4 (irreversible)' do
|
|
45
|
-
client.escalate(level: 1, authority: :governance_council, reason: 's1')
|
|
46
|
-
client.escalate(level: 2, authority: :governance_council, reason: 's2')
|
|
47
|
-
client.escalate(level: 3, authority: :council_plus_executive, reason: 's3')
|
|
48
|
-
client.escalate(level: 4, authority: :physical_keyholders, reason: 's4')
|
|
49
|
-
result = client.deescalate(target_level: 0, authority: :physical_keyholders, reason: 'try')
|
|
50
|
-
expect(result[:deescalated]).to be false
|
|
51
|
-
expect(result[:reason]).to eq(:irreversible)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
describe '#extinction_status' do
|
|
56
|
-
it 'returns current state' do
|
|
57
|
-
status = client.extinction_status
|
|
58
|
-
expect(status[:current_level]).to eq(0)
|
|
59
|
-
expect(status[:active]).to be false
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
describe '#escalate side effects' do
|
|
64
|
-
it 'emits escalation event when Legion::Events is defined' do
|
|
65
|
-
events = Module.new { def self.emit(*, **); end }
|
|
66
|
-
stub_const('Legion::Events', events)
|
|
67
|
-
expect(events).to receive(:emit).with('extinction.mesh_isolation', hash_including(level: 1))
|
|
68
|
-
client.escalate(level: 1, authority: :governance_council, reason: 'test')
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it 'triggers cryptographic erasure at level 4' do
|
|
72
|
-
events = Module.new { def self.emit(*, **); end }
|
|
73
|
-
stub_const('Legion::Events', events)
|
|
74
|
-
pc_mod = Module.new { def self.erase_all; end }
|
|
75
|
-
stub_const('Legion::Extensions::Privatecore::Runners::Privatecore', pc_mod)
|
|
76
|
-
|
|
77
|
-
client.escalate(level: 1, authority: :governance_council, reason: 's1')
|
|
78
|
-
client.escalate(level: 2, authority: :governance_council, reason: 's2')
|
|
79
|
-
client.escalate(level: 3, authority: :council_plus_executive, reason: 's3')
|
|
80
|
-
expect(pc_mod).to receive(:erase_all)
|
|
81
|
-
client.escalate(level: 4, authority: :physical_keyholders, reason: 'final')
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
describe '#monitor_protocol' do
|
|
86
|
-
it 'returns status hash at level 0' do
|
|
87
|
-
result = client.monitor_protocol
|
|
88
|
-
expect(result[:current_level]).to eq(0)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it 'detects stale escalation' do
|
|
92
|
-
client.escalate(level: 1, authority: :governance_council, reason: 'test')
|
|
93
|
-
state = client.send(:protocol_state)
|
|
94
|
-
state.history.last[:at] = Time.now.utc - 100_000
|
|
95
|
-
|
|
96
|
-
events = Module.new { def self.emit(*, **); end }
|
|
97
|
-
stub_const('Legion::Events', events)
|
|
98
|
-
expect(events).to receive(:emit).with('extinction.stale_escalation', anything)
|
|
99
|
-
client.monitor_protocol
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
describe '#check_reversibility' do
|
|
104
|
-
it 'reports levels 1-3 as reversible' do
|
|
105
|
-
[1, 2, 3].each do |level|
|
|
106
|
-
expect(client.check_reversibility(level: level)[:reversible]).to be true
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it 'reports level 4 as irreversible' do
|
|
111
|
-
expect(client.check_reversibility(level: 4)[:reversible]).to be false
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|