openc3-cosmos-cfdp 1.0.2 → 2.0.0
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/README.md +10 -1
- data/microservices/CFDP/Gemfile +3 -11
- data/microservices/CFDP/Gemfile.dev +35 -0
- data/microservices/CFDP/Gemfile.prod +29 -0
- data/microservices/CFDP/app/controllers/cfdp_controller.rb +8 -0
- data/microservices/CFDP/app/models/cfdp_crc_checksum.rb +3 -1
- data/microservices/CFDP/app/models/cfdp_mib.rb +2 -1
- data/microservices/CFDP/app/models/cfdp_pdu.rb +43 -19
- data/microservices/CFDP/app/models/cfdp_receive_transaction.rb +7 -2
- data/microservices/CFDP/app/models/cfdp_source_transaction.rb +6 -1
- data/microservices/CFDP/app/models/cfdp_transaction.rb +1 -1
- data/microservices/CFDP/app/models/cfdp_user.rb +14 -4
- data/microservices/CFDP/config/application.rb +0 -11
- data/microservices/CFDP/config/environments/test.rb +1 -1
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_file_data.rb +6 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_finished.rb +17 -5
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_metadata.rb +28 -11
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_tlv.rb +5 -2
- data/microservices/CFDP/lib/cfdp_pdu/cfdp_pdu_user_ops.rb +16 -9
- data/microservices/CFDP/spec/controllers/cfdp_controller_spec.rb +373 -0
- data/microservices/CFDP/spec/models/cfdp_crc_checksum_spec.rb +134 -0
- data/microservices/CFDP/spec/models/cfdp_mib_spec.rb +389 -0
- data/microservices/CFDP/spec/models/cfdp_pdu_metadata_spec.rb +102 -1
- data/microservices/CFDP/spec/models/cfdp_pdu_user_ops_spec.rb +562 -0
- data/microservices/CFDP/spec/models/cfdp_receive_transaction_spec.rb +598 -0
- data/microservices/CFDP/spec/models/cfdp_transaction_spec.rb +331 -0
- data/microservices/CFDP/spec/models/cfdp_user_spec.rb +575 -0
- metadata +15 -6
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
4
|
# All Rights Reserved.
|
5
5
|
#
|
6
6
|
# Licensed for Evaluation and Educational Use
|
@@ -13,6 +13,9 @@
|
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
14
14
|
#
|
15
15
|
# The development of this software was funded in-whole or in-part by MethaneSAT LLC.
|
16
|
+
#
|
17
|
+
# The development of this software was funded in-part by Sandia National Laboratories.
|
18
|
+
# See https://github.com/OpenC3/openc3-cosmos-cfdp/pull/12 for details
|
16
19
|
|
17
20
|
class CfdpPdu < OpenC3::Packet
|
18
21
|
def self.decom_tlv(pdu, pdu_hash, variable_data)
|
@@ -77,7 +80,7 @@ class CfdpPdu < OpenC3::Packet
|
|
77
80
|
s = define_message_to_user_tlv()
|
78
81
|
s.buffer = tlv_data
|
79
82
|
tlv["MESSAGE_TO_USER"] = s.read("MESSAGE_TO_USER")
|
80
|
-
tlv.merge!(pdu.decom_message_to_user(tlv["MESSAGE_TO_USER"]))
|
83
|
+
tlv.merge!(pdu.decom_message_to_user(tlv["MESSAGE_TO_USER"], version: pdu_hash["VERSION"]))
|
81
84
|
|
82
85
|
when "FAULT_HANDLER_OVERRIDE"
|
83
86
|
s = define_fault_handler_override_tlv()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
# Copyright
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
4
|
# All Rights Reserved.
|
5
5
|
#
|
6
6
|
# Licensed for Evaluation and Educational Use
|
@@ -13,6 +13,9 @@
|
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
14
14
|
#
|
15
15
|
# The development of this software was funded in-whole or in-part by MethaneSAT LLC.
|
16
|
+
#
|
17
|
+
# The development of this software was funded in-part by Sandia National Laboratories.
|
18
|
+
# See https://github.com/OpenC3/openc3-cosmos-cfdp/pull/12 for details
|
16
19
|
|
17
20
|
class CfdpPdu < OpenC3::Packet
|
18
21
|
|
@@ -495,19 +498,23 @@ class CfdpPdu < OpenC3::Packet
|
|
495
498
|
end
|
496
499
|
|
497
500
|
# Table 6-16
|
498
|
-
def define_directory_listing_response_message
|
501
|
+
def define_directory_listing_response_message(version:)
|
499
502
|
s1 = OpenC3::Packet.new(nil, nil, :BIG_ENDIAN)
|
500
503
|
item = s1.append_item("RESPONSE_CODE", 1, :UINT)
|
501
|
-
|
504
|
+
if version >= 1
|
505
|
+
item.states = { "SUCCESSFUL" => 0, "UNSUCCESSFUL" => 1 }
|
506
|
+
else
|
507
|
+
item.states = { "SUCCESSFUL" => 0, "UNSUCCESSFUL" => 0xFF }
|
508
|
+
end
|
502
509
|
s1.append_item("SPARE", 7, :UINT)
|
503
510
|
return s1, define_length_value(), define_length_value()
|
504
511
|
end
|
505
512
|
|
506
|
-
def build_directory_listing_response_message(response_code:, directory_name:, directory_file_name:)
|
513
|
+
def build_directory_listing_response_message(response_code:, directory_name:, directory_file_name:, version:)
|
507
514
|
s1 = define_reserved_cfdp_message_header()
|
508
515
|
s1.write("MSG_ID", "cfdp")
|
509
516
|
s1.write("MSG_TYPE", "DIRECTORY_LISTING_RESPONSE")
|
510
|
-
fixed, s2, s3 = define_directory_listing_response_message()
|
517
|
+
fixed, s2, s3 = define_directory_listing_response_message(version: version)
|
511
518
|
fixed.write("RESPONSE_CODE", response_code)
|
512
519
|
fixed.write("SPARE", 0)
|
513
520
|
s2.write("LENGTH", directory_name.to_s.length)
|
@@ -517,10 +524,10 @@ class CfdpPdu < OpenC3::Packet
|
|
517
524
|
return s1.buffer(false) + fixed.buffer(false) + s2.buffer(false) + s3.buffer(false)
|
518
525
|
end
|
519
526
|
|
520
|
-
def decom_directory_listing_response_message(message_to_user)
|
527
|
+
def decom_directory_listing_response_message(message_to_user, version:)
|
521
528
|
result = {}
|
522
529
|
message_to_user = message_to_user[5..-1] # Remove header
|
523
|
-
fixed, s2, s3 = define_directory_listing_response_message()
|
530
|
+
fixed, s2, s3 = define_directory_listing_response_message(version: version)
|
524
531
|
fixed.buffer = message_to_user[0..(fixed.defined_length - 1)]
|
525
532
|
result["RESPONSE_CODE"] = fixed.read("RESPONSE_CODE")
|
526
533
|
message_to_user = message_to_user[fixed.defined_length..-1]
|
@@ -689,7 +696,7 @@ class CfdpPdu < OpenC3::Packet
|
|
689
696
|
return result
|
690
697
|
end
|
691
698
|
|
692
|
-
def decom_message_to_user(message_to_user)
|
699
|
+
def decom_message_to_user(message_to_user, version:)
|
693
700
|
s1 = define_reserved_cfdp_message_header()
|
694
701
|
if message_to_user.length >= 5 # Minimum size
|
695
702
|
s1.buffer = message_to_user[0..(s1.defined_length - 1)]
|
@@ -723,7 +730,7 @@ class CfdpPdu < OpenC3::Packet
|
|
723
730
|
when "DIRECTORY_LISTING_REQUEST"
|
724
731
|
return decom_directory_listing_request_message(message_to_user)
|
725
732
|
when "DIRECTORY_LISTING_RESPONSE"
|
726
|
-
return decom_directory_listing_response_message(message_to_user)
|
733
|
+
return decom_directory_listing_response_message(message_to_user, version: version)
|
727
734
|
when "REMOTE_STATUS_REPORT_REQUEST"
|
728
735
|
return decom_remote_status_report_request_message(message_to_user)
|
729
736
|
when "REMOTE_STATUS_REPORT_RESPONSE"
|
@@ -0,0 +1,373 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# Licensed for Evaluation and Educational Use
|
7
|
+
#
|
8
|
+
# This file may only be used commercially under the terms of a commercial license
|
9
|
+
# purchased from OpenC3, Inc.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
+
#
|
15
|
+
# The development of this software was funded in-whole or in-part by Sandia National Laboratories.
|
16
|
+
# See https://github.com/OpenC3/openc3-cosmos-cfdp/pull/12 for details
|
17
|
+
|
18
|
+
require 'rails_helper'
|
19
|
+
|
20
|
+
$orig_cfdp_user = $cfdp_user
|
21
|
+
|
22
|
+
RSpec.describe CfdpController, type: :controller do
|
23
|
+
before(:each) do
|
24
|
+
@mock_user = double("CfdpUser")
|
25
|
+
@mock_transaction = double("CfdpTransaction")
|
26
|
+
allow(@mock_transaction).to receive(:id).and_return("1__123")
|
27
|
+
$cfdp_user = @mock_user
|
28
|
+
|
29
|
+
# Setup authorization mock
|
30
|
+
allow(controller).to receive(:check_authorization).and_return(true)
|
31
|
+
allow(controller).to receive(:authorization).and_return(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
after(:each) do
|
35
|
+
$cfdp_user = $orig_cfdp_user
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "PUT requests" do
|
39
|
+
it "handles put request successfully" do
|
40
|
+
params = {
|
41
|
+
destination_entity_id: "2",
|
42
|
+
destination_file_name: "dest.txt",
|
43
|
+
source_file_name: "source.txt",
|
44
|
+
controller: "cfdp",
|
45
|
+
action: "put"
|
46
|
+
}
|
47
|
+
|
48
|
+
# Expect CfdpUser to be called with proper parameters
|
49
|
+
expect(@mock_user).to receive(:start_source_transaction).with(
|
50
|
+
ActionController::Parameters.new(params)
|
51
|
+
).and_return(@mock_transaction)
|
52
|
+
|
53
|
+
post :put, params: params
|
54
|
+
|
55
|
+
expect(response).to have_http_status(:success)
|
56
|
+
expect(response.body).to eq("1__123")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "requires destination_entity_id" do
|
60
|
+
post :put, params: {}
|
61
|
+
|
62
|
+
expect(response).to have_http_status(:bad_request)
|
63
|
+
expect(JSON.parse(response.body)["message"]).to include("destination_entity_id")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "handles errors" do
|
67
|
+
allow(@mock_user).to receive(:start_source_transaction).and_raise("Test error")
|
68
|
+
|
69
|
+
post :put, params: { destination_entity_id: "2" }
|
70
|
+
|
71
|
+
expect(response).to have_http_status(:internal_server_error)
|
72
|
+
expect(JSON.parse(response.body)["message"]).to include("Test error")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "CANCEL requests" do
|
77
|
+
it "handles cancel request successfully" do
|
78
|
+
params = { action: "cancel", controller: "cfdp", transaction_id: "1__123" }
|
79
|
+
|
80
|
+
# Expect CfdpUser to be called with proper parameters
|
81
|
+
expect(@mock_user).to receive(:cancel).with(
|
82
|
+
ActionController::Parameters.new(params)
|
83
|
+
).and_return(@mock_transaction)
|
84
|
+
|
85
|
+
post :cancel, params: params
|
86
|
+
|
87
|
+
expect(response).to have_http_status(:success)
|
88
|
+
expect(response.body).to eq("1__123")
|
89
|
+
end
|
90
|
+
|
91
|
+
it "requires transaction_id" do
|
92
|
+
post :cancel, params: {}
|
93
|
+
|
94
|
+
expect(response).to have_http_status(:bad_request)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "handles transaction not found" do
|
98
|
+
allow(@mock_user).to receive(:cancel).and_return(nil)
|
99
|
+
|
100
|
+
post :cancel, params: { transaction_id: "1__123" }
|
101
|
+
|
102
|
+
expect(response).to have_http_status(:not_found)
|
103
|
+
expect(JSON.parse(response.body)["message"]).to include("not found")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "handles errors" do
|
107
|
+
allow(@mock_user).to receive(:cancel).and_raise("Test error")
|
108
|
+
|
109
|
+
post :cancel, params: { transaction_id: "1__123" }
|
110
|
+
|
111
|
+
expect(response).to have_http_status(:internal_server_error)
|
112
|
+
expect(JSON.parse(response.body)["message"]).to include("Test error")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "SUSPEND requests" do
|
117
|
+
it "handles suspend request successfully" do
|
118
|
+
params = { transaction_id: "1__123", controller: "cfdp", action: "suspend" }
|
119
|
+
|
120
|
+
# Expect CfdpUser to be called with proper parameters
|
121
|
+
expect(@mock_user).to receive(:suspend).with(
|
122
|
+
ActionController::Parameters.new(params)
|
123
|
+
).and_return(@mock_transaction)
|
124
|
+
|
125
|
+
post :suspend, params: params
|
126
|
+
|
127
|
+
expect(response).to have_http_status(:success)
|
128
|
+
expect(response.body).to eq("1__123")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "requires transaction_id" do
|
132
|
+
post :suspend, params: {}
|
133
|
+
|
134
|
+
expect(response).to have_http_status(:bad_request)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "handles transaction not found" do
|
138
|
+
allow(@mock_user).to receive(:suspend).and_return(nil)
|
139
|
+
|
140
|
+
post :suspend, params: { transaction_id: "1__123" }
|
141
|
+
|
142
|
+
expect(response).to have_http_status(:not_found)
|
143
|
+
expect(JSON.parse(response.body)["message"]).to include("not found")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe "RESUME requests" do
|
148
|
+
it "handles resume request successfully" do
|
149
|
+
params = { action: "resume", controller: "cfdp", transaction_id: "1__123" }
|
150
|
+
|
151
|
+
# Expect CfdpUser to be called with proper parameters
|
152
|
+
expect(@mock_user).to receive(:resume).with(
|
153
|
+
ActionController::Parameters.new(params)
|
154
|
+
).and_return(@mock_transaction)
|
155
|
+
|
156
|
+
post :resume, params: params
|
157
|
+
|
158
|
+
expect(response).to have_http_status(:success)
|
159
|
+
expect(response.body).to eq("1__123")
|
160
|
+
end
|
161
|
+
|
162
|
+
it "requires transaction_id" do
|
163
|
+
post :resume, params: {}
|
164
|
+
|
165
|
+
expect(response).to have_http_status(:bad_request)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "handles transaction not found" do
|
169
|
+
allow(@mock_user).to receive(:resume).and_return(nil)
|
170
|
+
|
171
|
+
post :resume, params: { transaction_id: "1__123" }
|
172
|
+
|
173
|
+
expect(response).to have_http_status(:not_found)
|
174
|
+
expect(JSON.parse(response.body)["message"]).to include("not found")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "REPORT requests" do
|
179
|
+
it "handles report request successfully" do
|
180
|
+
params = { transaction_id: "1__123", controller: "cfdp", action: "report" }
|
181
|
+
|
182
|
+
# Expect CfdpUser to be called with proper parameters
|
183
|
+
expect(@mock_user).to receive(:report).with(
|
184
|
+
ActionController::Parameters.new(params)
|
185
|
+
).and_return(@mock_transaction)
|
186
|
+
|
187
|
+
post :report, params: params
|
188
|
+
|
189
|
+
expect(response).to have_http_status(:success)
|
190
|
+
expect(response.body).to eq("1__123")
|
191
|
+
end
|
192
|
+
|
193
|
+
it "requires transaction_id" do
|
194
|
+
post :report, params: {}
|
195
|
+
|
196
|
+
expect(response).to have_http_status(:bad_request)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "handles transaction not found" do
|
200
|
+
allow(@mock_user).to receive(:report).and_return(nil)
|
201
|
+
|
202
|
+
post :report, params: { transaction_id: "1__123" }
|
203
|
+
|
204
|
+
expect(response).to have_http_status(:not_found)
|
205
|
+
expect(JSON.parse(response.body)["message"]).to include("not found")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "DIRECTORY_LISTING requests" do
|
210
|
+
it "handles directory listing request successfully" do
|
211
|
+
params = {
|
212
|
+
remote_entity_id: "2",
|
213
|
+
directory_name: "test_dir",
|
214
|
+
directory_file_name: "result.txt",
|
215
|
+
controller: "cfdp",
|
216
|
+
action: "directory_listing"
|
217
|
+
}
|
218
|
+
|
219
|
+
# Expect CfdpUser to be called with proper parameters
|
220
|
+
expect(@mock_user).to receive(:start_directory_listing).with(
|
221
|
+
ActionController::Parameters.new(params)
|
222
|
+
).and_return(@mock_transaction)
|
223
|
+
|
224
|
+
post :directory_listing, params: params
|
225
|
+
|
226
|
+
expect(response).to have_http_status(:success)
|
227
|
+
expect(response.body).to eq("1__123")
|
228
|
+
end
|
229
|
+
|
230
|
+
it "requires remote_entity_id, directory_name and directory_file_name" do
|
231
|
+
post :directory_listing, params: {}
|
232
|
+
|
233
|
+
expect(response).to have_http_status(:bad_request)
|
234
|
+
expect(JSON.parse(response.body)["message"]).to include("missing")
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "SUBSCRIBE requests" do
|
239
|
+
before(:each) do
|
240
|
+
allow(CfdpTopic).to receive(:subscribe_indications).and_return("0-0")
|
241
|
+
end
|
242
|
+
|
243
|
+
it "handles subscribe request successfully" do
|
244
|
+
post :subscribe
|
245
|
+
|
246
|
+
expect(response).to have_http_status(:success)
|
247
|
+
expect(response.body).to eq("0-0")
|
248
|
+
end
|
249
|
+
|
250
|
+
it "handles errors" do
|
251
|
+
allow(CfdpTopic).to receive(:subscribe_indications).and_raise("Test error")
|
252
|
+
|
253
|
+
post :subscribe
|
254
|
+
|
255
|
+
expect(response).to have_http_status(:internal_server_error)
|
256
|
+
expect(JSON.parse(response.body)["message"]).to include("Test error")
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe "INDICATIONS requests" do
|
261
|
+
before(:each) do
|
262
|
+
@indications = {
|
263
|
+
continuation: "1-1",
|
264
|
+
indications: [
|
265
|
+
{
|
266
|
+
"time" => Time.now.to_nsec_from_epoch,
|
267
|
+
"indication_type" => "Transaction-Finished",
|
268
|
+
"transaction_id" => "1__123",
|
269
|
+
"condition_code" => "NO_ERROR"
|
270
|
+
}
|
271
|
+
]
|
272
|
+
}
|
273
|
+
allow(CfdpTopic).to receive(:read_indications).and_return(@indications)
|
274
|
+
end
|
275
|
+
|
276
|
+
it "gets indications for all transactions" do
|
277
|
+
get :indications
|
278
|
+
|
279
|
+
expect(response).to have_http_status(:success)
|
280
|
+
parsed = JSON.parse(response.body)
|
281
|
+
expect(parsed["continuation"]).to eq("1-1")
|
282
|
+
expect(parsed["indications"]).to be_an(Array)
|
283
|
+
expect(parsed["indications"].length).to eq(1)
|
284
|
+
end
|
285
|
+
|
286
|
+
it "gets indications for a specific transaction" do
|
287
|
+
get :indications, params: { transaction_id: "1__123" }
|
288
|
+
|
289
|
+
expect(response).to have_http_status(:success)
|
290
|
+
expect(CfdpTopic).to have_received(:read_indications).with(
|
291
|
+
hash_including(transaction_id: "1__123")
|
292
|
+
)
|
293
|
+
end
|
294
|
+
|
295
|
+
it "handles errors" do
|
296
|
+
allow(CfdpTopic).to receive(:read_indications).and_raise("Test error")
|
297
|
+
|
298
|
+
get :indications
|
299
|
+
|
300
|
+
expect(response).to have_http_status(:internal_server_error)
|
301
|
+
expect(JSON.parse(response.body)["message"]).to include("Test error")
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
describe "TRANSACTIONS requests" do
|
306
|
+
before(:each) do
|
307
|
+
@transactions = {
|
308
|
+
"1__123" => @mock_transaction,
|
309
|
+
"1__124" => @mock_transaction
|
310
|
+
}
|
311
|
+
|
312
|
+
allow(CfdpMib).to receive(:transactions).and_return(@transactions)
|
313
|
+
allow(CfdpMib).to receive(:cleanup_old_transactions)
|
314
|
+
|
315
|
+
allow(@mock_transaction).to receive(:as_json).and_return({"id" => "1__123", "state" => "ACTIVE"})
|
316
|
+
allow(@mock_transaction).to receive(:transaction_status).and_return("ACTIVE")
|
317
|
+
allow(@mock_transaction).to receive(:id).and_return("1__123")
|
318
|
+
end
|
319
|
+
|
320
|
+
it "gets all transactions" do
|
321
|
+
get :transactions
|
322
|
+
|
323
|
+
expect(response).to have_http_status(:success)
|
324
|
+
|
325
|
+
parsed = JSON.parse(response.body)
|
326
|
+
expect(parsed).to be_an(Array)
|
327
|
+
expect(parsed.length).to eq(2)
|
328
|
+
end
|
329
|
+
|
330
|
+
it "gets only active transactions" do
|
331
|
+
get :transactions, params: { active: "true" }
|
332
|
+
|
333
|
+
expect(response).to have_http_status(:success)
|
334
|
+
|
335
|
+
parsed = JSON.parse(response.body)
|
336
|
+
expect(parsed).to be_an(Array)
|
337
|
+
expect(parsed.length).to eq(2)
|
338
|
+
end
|
339
|
+
|
340
|
+
it "handles errors" do
|
341
|
+
allow(CfdpMib).to receive(:transactions).and_raise("Test error")
|
342
|
+
|
343
|
+
get :transactions
|
344
|
+
|
345
|
+
expect(response).to have_http_status(:internal_server_error)
|
346
|
+
expect(JSON.parse(response.body)["message"]).to include("Test error")
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe "check_authorization" do
|
351
|
+
before(:each) do
|
352
|
+
# Reset the mock to allow the real method to be called
|
353
|
+
allow(controller).to receive(:check_authorization).and_call_original
|
354
|
+
|
355
|
+
# Setup mocks needed by the method
|
356
|
+
@entity = {
|
357
|
+
"id" => 1,
|
358
|
+
"cmd_info" => ["TARGET", "PACKET", "ITEM"],
|
359
|
+
"tlm_info" => []
|
360
|
+
}
|
361
|
+
|
362
|
+
allow(CfdpMib).to receive(:entity).and_return(@entity)
|
363
|
+
allow(CfdpMib).to receive(:source_entity_id).and_return(1)
|
364
|
+
allow(CfdpMib).to receive(:cleanup_old_transactions)
|
365
|
+
end
|
366
|
+
|
367
|
+
it "authorizes when entity_id is numeric" do
|
368
|
+
allow(controller).to receive(:authorization).and_return(true)
|
369
|
+
|
370
|
+
expect(controller.send(:check_authorization)).to be true
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2025 OpenC3, Inc.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# Licensed for Evaluation and Educational Use
|
7
|
+
#
|
8
|
+
# This file may only be used commercially under the terms of a commercial license
|
9
|
+
# purchased from OpenC3, Inc.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
14
|
+
#
|
15
|
+
# The development of this software was funded in-whole or in-part by Sandia National Laboratories.
|
16
|
+
# See https://github.com/OpenC3/openc3-cosmos-cfdp/pull/12 for details
|
17
|
+
|
18
|
+
require 'rails_helper'
|
19
|
+
require 'tempfile'
|
20
|
+
|
21
|
+
RSpec.describe CfdpCrcChecksum do
|
22
|
+
before(:each) do
|
23
|
+
# Mock OpenC3::Crc32
|
24
|
+
@mock_crc32 = double("OpenC3::Crc32")
|
25
|
+
allow(@mock_crc32).to receive(:calc).and_return(0x12345678)
|
26
|
+
allow(OpenC3::Crc32).to receive(:new).and_return(@mock_crc32)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "initialize" do
|
30
|
+
it "initializes with default values" do
|
31
|
+
checksum = CfdpCrcChecksum.new
|
32
|
+
expect(checksum.crc).to eq(@mock_crc32)
|
33
|
+
expect(checksum.instance_variable_get(:@checksum)).to eq(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "initializes with custom values" do
|
37
|
+
# These are the defaults from the OpenC3::Crc32 class
|
38
|
+
poly = 0x04C11DB7
|
39
|
+
seed = 0xFFFFFFFF
|
40
|
+
xor = true
|
41
|
+
reflect = true
|
42
|
+
|
43
|
+
# Expect it to be called with these specific parameters
|
44
|
+
expect(OpenC3::Crc32).to receive(:new).with(poly, seed, xor, reflect).and_return(@mock_crc32)
|
45
|
+
|
46
|
+
checksum = CfdpCrcChecksum.new(poly, seed, xor, reflect)
|
47
|
+
expect(checksum.crc).to eq(@mock_crc32)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "add" do
|
52
|
+
it "always returns 0 (not supported)" do
|
53
|
+
checksum = CfdpCrcChecksum.new
|
54
|
+
result = checksum.add(100, "test data")
|
55
|
+
expect(result).to eq(0)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "checksum" do
|
60
|
+
it "calculates checksum from file" do
|
61
|
+
# Create a tempfile with content
|
62
|
+
file = Tempfile.new('crc_test')
|
63
|
+
file.write("test data")
|
64
|
+
file.rewind
|
65
|
+
|
66
|
+
# Expect the calc method to be called with the file data
|
67
|
+
expect(@mock_crc32).to receive(:calc).with("test data").and_return(0xABCDEF12)
|
68
|
+
|
69
|
+
checksum = CfdpCrcChecksum.new
|
70
|
+
result = checksum.checksum(file, false)
|
71
|
+
|
72
|
+
# The result should be masked to 32 bits
|
73
|
+
expect(result).to eq(0xABCDEF12)
|
74
|
+
|
75
|
+
file.close
|
76
|
+
file.unlink
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "check" do
|
81
|
+
it "returns true when checksums match" do
|
82
|
+
# Create a tempfile with content
|
83
|
+
file = Tempfile.new('crc_test')
|
84
|
+
file.write("test data")
|
85
|
+
file.rewind
|
86
|
+
|
87
|
+
checksum = CfdpCrcChecksum.new
|
88
|
+
|
89
|
+
# First, mock the internal checksum call to return a specific value
|
90
|
+
expect(checksum).to receive(:checksum).with(file, false).and_return(0x12345678)
|
91
|
+
|
92
|
+
# Then check against the same value
|
93
|
+
expect(checksum.check(file, 0x12345678, false)).to be true
|
94
|
+
|
95
|
+
file.close
|
96
|
+
file.unlink
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns false when checksums don't match" do
|
100
|
+
# Create a tempfile with content
|
101
|
+
file = Tempfile.new('crc_test')
|
102
|
+
file.write("test data")
|
103
|
+
file.rewind
|
104
|
+
|
105
|
+
checksum = CfdpCrcChecksum.new
|
106
|
+
|
107
|
+
# First, mock the internal checksum call to return a specific value
|
108
|
+
expect(checksum).to receive(:checksum).with(file, false).and_return(0x12345678)
|
109
|
+
|
110
|
+
# Then check against a different value
|
111
|
+
expect(checksum.check(file, 0x87654321, false)).to be false
|
112
|
+
|
113
|
+
file.close
|
114
|
+
file.unlink
|
115
|
+
end
|
116
|
+
|
117
|
+
it "handles an empty file" do
|
118
|
+
# Create an empty tempfile
|
119
|
+
file = Tempfile.new('crc_test')
|
120
|
+
file.rewind
|
121
|
+
|
122
|
+
checksum = CfdpCrcChecksum.new
|
123
|
+
|
124
|
+
# For an empty file, mock the CRC to return a specific value
|
125
|
+
expect(checksum).to receive(:checksum).with(file, false).and_return(0)
|
126
|
+
|
127
|
+
# The check should pass when comparing with the same value
|
128
|
+
expect(checksum.check(file, 0, false)).to be true
|
129
|
+
|
130
|
+
file.close
|
131
|
+
file.unlink
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|