orfeas_lyra 0.6.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 +7 -0
- data/CHANGELOG.md +222 -0
- data/LICENSE +21 -0
- data/README.md +1165 -0
- data/Rakefile +728 -0
- data/app/controllers/lyra/application_controller.rb +23 -0
- data/app/controllers/lyra/dashboard_controller.rb +624 -0
- data/app/controllers/lyra/flow_controller.rb +224 -0
- data/app/controllers/lyra/privacy_controller.rb +182 -0
- data/app/views/lyra/dashboard/audit_trail.html.erb +324 -0
- data/app/views/lyra/dashboard/discrepancies.html.erb +125 -0
- data/app/views/lyra/dashboard/event_graph_view.html.erb +525 -0
- data/app/views/lyra/dashboard/heatmap_view.html.erb +155 -0
- data/app/views/lyra/dashboard/index.html.erb +119 -0
- data/app/views/lyra/dashboard/model_overview.html.erb +115 -0
- data/app/views/lyra/dashboard/projections.html.erb +302 -0
- data/app/views/lyra/dashboard/schema.html.erb +283 -0
- data/app/views/lyra/dashboard/schema_history.html.erb +78 -0
- data/app/views/lyra/dashboard/schema_version.html.erb +340 -0
- data/app/views/lyra/dashboard/verification.html.erb +370 -0
- data/app/views/lyra/flow/crud_mapping.html.erb +125 -0
- data/app/views/lyra/flow/timeline.html.erb +260 -0
- data/app/views/lyra/privacy/pii_detection.html.erb +148 -0
- data/app/views/lyra/privacy/policy.html.erb +188 -0
- data/app/workflows/es_async_mode_workflow.rb +80 -0
- data/app/workflows/es_sync_mode_workflow.rb +64 -0
- data/app/workflows/hijack_mode_workflow.rb +54 -0
- data/app/workflows/lifecycle_workflow.rb +43 -0
- data/app/workflows/monitor_mode_workflow.rb +39 -0
- data/config/privacy_policies.rb +273 -0
- data/config/routes.rb +48 -0
- data/lib/lyra/aggregate.rb +131 -0
- data/lib/lyra/associations/event_aware.rb +225 -0
- data/lib/lyra/command.rb +81 -0
- data/lib/lyra/command_handler.rb +155 -0
- data/lib/lyra/configuration.rb +124 -0
- data/lib/lyra/consistency/read_your_writes.rb +91 -0
- data/lib/lyra/correlation.rb +144 -0
- data/lib/lyra/dual_view.rb +231 -0
- data/lib/lyra/engine.rb +67 -0
- data/lib/lyra/event.rb +71 -0
- data/lib/lyra/event_analyzer.rb +135 -0
- data/lib/lyra/event_flow.rb +449 -0
- data/lib/lyra/event_mapper.rb +106 -0
- data/lib/lyra/event_store_adapter.rb +72 -0
- data/lib/lyra/id_generator.rb +137 -0
- data/lib/lyra/interceptors/association_interceptor.rb +169 -0
- data/lib/lyra/interceptors/crud_interceptor.rb +543 -0
- data/lib/lyra/privacy/gdpr_compliance.rb +161 -0
- data/lib/lyra/privacy/pii_detector.rb +85 -0
- data/lib/lyra/privacy/pii_masker.rb +66 -0
- data/lib/lyra/privacy/policy_integration.rb +253 -0
- data/lib/lyra/projection.rb +94 -0
- data/lib/lyra/projections/async_projection_job.rb +63 -0
- data/lib/lyra/projections/cached_projection.rb +322 -0
- data/lib/lyra/projections/cached_relation.rb +757 -0
- data/lib/lyra/projections/event_store_reader.rb +127 -0
- data/lib/lyra/projections/model_projection.rb +143 -0
- data/lib/lyra/schema/diff.rb +331 -0
- data/lib/lyra/schema/event_class_registrar.rb +63 -0
- data/lib/lyra/schema/generator.rb +190 -0
- data/lib/lyra/schema/reporter.rb +188 -0
- data/lib/lyra/schema/store.rb +156 -0
- data/lib/lyra/schema/validator.rb +100 -0
- data/lib/lyra/strict_data_access.rb +363 -0
- data/lib/lyra/verification/crud_lifecycle_workflow.rb +456 -0
- data/lib/lyra/verification/workflow_generator.rb +540 -0
- data/lib/lyra/version.rb +3 -0
- data/lib/lyra/visualization/activity_heatmap.rb +215 -0
- data/lib/lyra/visualization/event_graph.rb +310 -0
- data/lib/lyra/visualization/timeline.rb +398 -0
- data/lib/lyra.rb +150 -0
- data/lib/tasks/dist.rake +391 -0
- data/lib/tasks/gems.rake +185 -0
- data/lib/tasks/lyra_schema.rake +231 -0
- data/lib/tasks/lyra_workflows.rake +452 -0
- data/lib/tasks/public_release.rake +351 -0
- data/lib/tasks/stats.rake +175 -0
- data/lib/tasks/testbed.rake +479 -0
- data/lib/tasks/version.rake +159 -0
- metadata +221 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :lyra do
|
|
4
|
+
namespace :testbed do
|
|
5
|
+
LYRA_TESTBED_PATH = File.expand_path("../../examples/aegean_epay_testbed", __dir__)
|
|
6
|
+
|
|
7
|
+
desc "Run testbed tests in all Lyra modes (part of comprehensive test suite)"
|
|
8
|
+
task :all_modes do
|
|
9
|
+
puts "\n" + "=" * 80
|
|
10
|
+
puts " TESTBED TESTS - ALL LYRA MODES"
|
|
11
|
+
puts " Path: #{LYRA_TESTBED_PATH}"
|
|
12
|
+
puts "=" * 80
|
|
13
|
+
|
|
14
|
+
Dir.chdir(LYRA_TESTBED_PATH) do
|
|
15
|
+
# Ensure bundle is up to date
|
|
16
|
+
system("bundle check || bundle install") || raise("Bundle install failed")
|
|
17
|
+
|
|
18
|
+
# Install required assets (pico CSS and fonts) before running tests
|
|
19
|
+
puts "\n[Setup] Installing required assets..."
|
|
20
|
+
system("bundle exec rails pico:update")
|
|
21
|
+
system("bundle exec rake fonts:install")
|
|
22
|
+
|
|
23
|
+
# Run the comprehensive all_modes test
|
|
24
|
+
success = system("bundle exec rake lyra:test:all_modes")
|
|
25
|
+
raise "Testbed tests failed in some modes" unless success
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
puts "\n" + "=" * 80
|
|
29
|
+
puts " TESTBED TESTS PASSED IN ALL MODES"
|
|
30
|
+
puts "=" * 80
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc "Run testbed tests in a specific mode (LYRA_MODE=disabled|monitor|hijack|event_sourcing)"
|
|
34
|
+
task :mode do
|
|
35
|
+
mode = ENV["LYRA_MODE"] || "disabled"
|
|
36
|
+
projection = ENV["LYRA_PROJECTION_MODE"]
|
|
37
|
+
|
|
38
|
+
puts "\n=== Running testbed tests in #{mode} mode ==="
|
|
39
|
+
puts " Projection mode: #{projection || 'default'}" if projection
|
|
40
|
+
|
|
41
|
+
Dir.chdir(LYRA_TESTBED_PATH) do
|
|
42
|
+
# Ensure required assets are installed
|
|
43
|
+
system("bundle exec rails pico:update > /dev/null 2>&1")
|
|
44
|
+
system("bundle exec rake fonts:install > /dev/null 2>&1")
|
|
45
|
+
|
|
46
|
+
env = { "LYRA_MODE" => mode, "RAILS_ENV" => "test" }
|
|
47
|
+
env["LYRA_PROJECTION_MODE"] = projection if projection
|
|
48
|
+
|
|
49
|
+
success = system(env, "bundle exec rails test")
|
|
50
|
+
raise "Testbed tests failed in #{mode} mode" unless success
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc "Run testbed integration tests only"
|
|
55
|
+
task :integration do
|
|
56
|
+
puts "\n=== Running testbed integration tests ==="
|
|
57
|
+
|
|
58
|
+
Dir.chdir(LYRA_TESTBED_PATH) do
|
|
59
|
+
# Ensure required assets are installed
|
|
60
|
+
system("bundle exec rails pico:update > /dev/null 2>&1")
|
|
61
|
+
system("bundle exec rake fonts:install > /dev/null 2>&1")
|
|
62
|
+
|
|
63
|
+
success = system("bundle exec rails test test/integration/")
|
|
64
|
+
raise "Testbed integration tests failed" unless success
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Helper module for running tests and collecting results
|
|
71
|
+
module ComprehensiveTestRunner
|
|
72
|
+
LYRA_CORE_MODES = [
|
|
73
|
+
{ lyra_mode: "disabled", projection_mode: nil, name: "Disabled" },
|
|
74
|
+
{ lyra_mode: "monitor", projection_mode: nil, name: "Monitor" },
|
|
75
|
+
{ lyra_mode: "hijack", projection_mode: nil, name: "Hijack" },
|
|
76
|
+
{ lyra_mode: "event_sourcing", projection_mode: "sync", name: "ES Sync" },
|
|
77
|
+
{ lyra_mode: "event_sourcing", projection_mode: "async", name: "ES Async" },
|
|
78
|
+
{ lyra_mode: "event_sourcing", projection_mode: "disabled", name: "ES Disabled" }
|
|
79
|
+
].freeze
|
|
80
|
+
|
|
81
|
+
TESTBED_MODES = [
|
|
82
|
+
{ lyra_mode: "disabled", projection_mode: nil, name: "Lyra Disabled" },
|
|
83
|
+
{ lyra_mode: "monitor", projection_mode: nil, name: "Monitor Mode" },
|
|
84
|
+
{ lyra_mode: "hijack", projection_mode: nil, name: "Hijack Mode" },
|
|
85
|
+
{ lyra_mode: "event_sourcing", projection_mode: "sync", name: "ES Sync" },
|
|
86
|
+
{ lyra_mode: "event_sourcing", projection_mode: "async", name: "ES Async" },
|
|
87
|
+
{ lyra_mode: "event_sourcing", projection_mode: "disabled", name: "ES Disabled" }
|
|
88
|
+
].freeze
|
|
89
|
+
|
|
90
|
+
GEM_COMPONENTS = [
|
|
91
|
+
{ path: "gems/pam_dsl", name: "PAM DSL" },
|
|
92
|
+
{ path: "gems/petri_flow", name: "PetriFlow" }
|
|
93
|
+
].freeze
|
|
94
|
+
|
|
95
|
+
class << self
|
|
96
|
+
def run_core_tests
|
|
97
|
+
require "open3"
|
|
98
|
+
results = []
|
|
99
|
+
|
|
100
|
+
LYRA_CORE_MODES.each_with_index do |config, index|
|
|
101
|
+
puts "\n[#{index + 1}/#{LYRA_CORE_MODES.size}] Core tests: #{config[:name]}"
|
|
102
|
+
puts "-" * 60
|
|
103
|
+
|
|
104
|
+
env = { "LYRA_MODE" => config[:lyra_mode] }
|
|
105
|
+
env["LYRA_PROJECTION_MODE"] = config[:projection_mode] if config[:projection_mode]
|
|
106
|
+
|
|
107
|
+
config_start = Time.now
|
|
108
|
+
stdout, stderr, status = Open3.capture3(env, "bundle exec rake test")
|
|
109
|
+
config_duration = Time.now - config_start
|
|
110
|
+
|
|
111
|
+
result = parse_test_output(stdout + stderr)
|
|
112
|
+
result[:name] = config[:name]
|
|
113
|
+
# Use parsed output for success (SimpleCov may return exit code 2 for coverage issues)
|
|
114
|
+
result[:passed] = result[:tests] > 0 && result[:failures] == 0 && result[:errors] == 0
|
|
115
|
+
result[:duration] = config_duration
|
|
116
|
+
results << result
|
|
117
|
+
|
|
118
|
+
print_result(result, config_duration)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
results
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def run_gem_tests
|
|
125
|
+
require "open3"
|
|
126
|
+
results = []
|
|
127
|
+
lyra_root = File.expand_path("../..", __dir__)
|
|
128
|
+
|
|
129
|
+
GEM_COMPONENTS.each_with_index do |component, index|
|
|
130
|
+
puts "\n[#{index + 1}/#{GEM_COMPONENTS.size}] #{component[:name]}"
|
|
131
|
+
puts "-" * 60
|
|
132
|
+
|
|
133
|
+
gem_path = File.join(lyra_root, component[:path])
|
|
134
|
+
|
|
135
|
+
# Use unbundled_env to use each gem's own Gemfile
|
|
136
|
+
Bundler.with_unbundled_env do
|
|
137
|
+
Dir.chdir(gem_path) do
|
|
138
|
+
system("bundle check > /dev/null 2>&1 || bundle install > /dev/null 2>&1")
|
|
139
|
+
|
|
140
|
+
config_start = Time.now
|
|
141
|
+
stdout, stderr, status = Open3.capture3("bundle exec rake test")
|
|
142
|
+
config_duration = Time.now - config_start
|
|
143
|
+
|
|
144
|
+
result = parse_test_output(stdout + stderr)
|
|
145
|
+
result[:name] = component[:name]
|
|
146
|
+
# Use parsed output for success (SimpleCov may return exit code 2 for coverage issues)
|
|
147
|
+
result[:passed] = result[:tests] > 0 && result[:failures] == 0 && result[:errors] == 0
|
|
148
|
+
result[:duration] = config_duration
|
|
149
|
+
results << result
|
|
150
|
+
|
|
151
|
+
print_result(result, config_duration)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
results
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def run_testbed_tests
|
|
160
|
+
require "open3"
|
|
161
|
+
testbed_path = File.expand_path("../../examples/aegean_epay_testbed", __dir__)
|
|
162
|
+
results = []
|
|
163
|
+
|
|
164
|
+
# Use unbundled_env to avoid inheriting Lyra's bundler environment
|
|
165
|
+
# The testbed has its own Gemfile with gems like prawn that Lyra doesn't have
|
|
166
|
+
Bundler.with_unbundled_env do
|
|
167
|
+
Dir.chdir(testbed_path) do
|
|
168
|
+
system("bundle check > /dev/null 2>&1 || bundle install > /dev/null 2>&1")
|
|
169
|
+
|
|
170
|
+
# Install required assets (pico CSS and fonts) before running tests
|
|
171
|
+
puts "\n[Setup] Installing required assets..."
|
|
172
|
+
system("bundle exec rails pico:update > /dev/null 2>&1")
|
|
173
|
+
system("bundle exec rake fonts:install > /dev/null 2>&1")
|
|
174
|
+
|
|
175
|
+
TESTBED_MODES.each_with_index do |config, index|
|
|
176
|
+
puts "\n[#{index + 1}/#{TESTBED_MODES.size}] Testbed: #{config[:name]}"
|
|
177
|
+
puts "-" * 60
|
|
178
|
+
|
|
179
|
+
# Reset database between modes to prevent state pollution
|
|
180
|
+
system("bundle exec rails db:test:prepare > /dev/null 2>&1")
|
|
181
|
+
|
|
182
|
+
env = { "LYRA_MODE" => config[:lyra_mode], "RAILS_ENV" => "test" }
|
|
183
|
+
env["LYRA_PROJECTION_MODE"] = config[:projection_mode] if config[:projection_mode]
|
|
184
|
+
|
|
185
|
+
config_start = Time.now
|
|
186
|
+
stdout, stderr, status = Open3.capture3(env, "bundle exec rails test")
|
|
187
|
+
config_duration = Time.now - config_start
|
|
188
|
+
|
|
189
|
+
result = parse_test_output(stdout + stderr)
|
|
190
|
+
result[:name] = config[:name]
|
|
191
|
+
# Use parsed output for success (SimpleCov may return exit code 2 for coverage issues)
|
|
192
|
+
result[:passed] = result[:tests] > 0 && result[:failures] == 0 && result[:errors] == 0
|
|
193
|
+
result[:duration] = config_duration
|
|
194
|
+
results << result
|
|
195
|
+
|
|
196
|
+
print_result(result, config_duration)
|
|
197
|
+
|
|
198
|
+
# Print failure details if any
|
|
199
|
+
unless result[:passed]
|
|
200
|
+
output = stdout + stderr
|
|
201
|
+
# Print last 100 lines to capture failure details
|
|
202
|
+
puts "\n--- FAILURE OUTPUT (last 100 lines) ---"
|
|
203
|
+
puts output.lines.last(100).join
|
|
204
|
+
puts "--- END FAILURE OUTPUT ---\n"
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
results
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def run_no_pam_dsl_tests
|
|
214
|
+
require "open3"
|
|
215
|
+
results = []
|
|
216
|
+
lyra_root = File.expand_path("../..", __dir__)
|
|
217
|
+
|
|
218
|
+
LYRA_CORE_MODES.each_with_index do |config, index|
|
|
219
|
+
puts "\n[#{index + 1}/#{LYRA_CORE_MODES.size}] #{config[:name]} (no PAM DSL)"
|
|
220
|
+
puts "-" * 60
|
|
221
|
+
|
|
222
|
+
env = {
|
|
223
|
+
"LYRA_MODE" => config[:lyra_mode],
|
|
224
|
+
"LYRA_DISABLE_PAM_DSL" => "true"
|
|
225
|
+
}
|
|
226
|
+
env["LYRA_PROJECTION_MODE"] = config[:projection_mode] if config[:projection_mode]
|
|
227
|
+
|
|
228
|
+
config_start = Time.now
|
|
229
|
+
Dir.chdir(lyra_root) do
|
|
230
|
+
stdout, stderr, status = Open3.capture3(env, "bundle exec rake test")
|
|
231
|
+
config_duration = Time.now - config_start
|
|
232
|
+
|
|
233
|
+
result = parse_test_output(stdout + stderr)
|
|
234
|
+
result[:name] = config[:name]
|
|
235
|
+
# Use parsed output for success (SimpleCov may return exit code 2)
|
|
236
|
+
result[:passed] = result[:tests] > 0 && result[:failures] == 0 && result[:errors] == 0
|
|
237
|
+
result[:duration] = config_duration
|
|
238
|
+
results << result
|
|
239
|
+
|
|
240
|
+
print_result(result, config_duration)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
results
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def parse_test_output(output)
|
|
248
|
+
clean_output = output.gsub(/\e\[[0-9;]*m/, "")
|
|
249
|
+
if clean_output =~ /(\d+) tests?, (\d+) assertions?, (\d+) failures?, (\d+) errors?, (\d+) skips?/
|
|
250
|
+
{ tests: $1.to_i, assertions: $2.to_i, failures: $3.to_i, errors: $4.to_i, skips: $5.to_i }
|
|
251
|
+
else
|
|
252
|
+
{ tests: 0, assertions: 0, failures: 0, errors: 0, skips: 0 }
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def print_result(result, duration)
|
|
257
|
+
status_icon = result[:passed] ? "\u2713" : "\u2717"
|
|
258
|
+
status_color = result[:passed] ? "\e[32m" : "\e[31m"
|
|
259
|
+
puts "#{status_color}#{status_icon}\e[0m #{result[:tests]} tests, #{result[:failures]} failures, " \
|
|
260
|
+
"#{result[:errors]} errors, #{result[:skips].to_i} skips (#{duration.round(1)}s)"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def generate_report(core_results, gem_results, testbed_results, total_duration, no_pam_dsl_results: [])
|
|
264
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
|
|
265
|
+
# Save to testbed reports folder
|
|
266
|
+
reports_dir = File.expand_path("../../examples/aegean_epay_testbed/reports", __dir__)
|
|
267
|
+
FileUtils.mkdir_p(reports_dir)
|
|
268
|
+
report_path = File.join(reports_dir, "comprehensive_test_#{timestamp}.md")
|
|
269
|
+
|
|
270
|
+
report = generate_markdown_report(core_results, gem_results, testbed_results, total_duration, no_pam_dsl_results: no_pam_dsl_results)
|
|
271
|
+
File.write(report_path, report)
|
|
272
|
+
|
|
273
|
+
puts "\nReport saved to: #{report_path}"
|
|
274
|
+
report_path
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def generate_markdown_report(core_results, gem_results, testbed_results, total_duration, no_pam_dsl_results: [])
|
|
278
|
+
core_passed = core_results.count { |r| r[:passed] }
|
|
279
|
+
gem_passed = gem_results.count { |r| r[:passed] }
|
|
280
|
+
testbed_passed = testbed_results.count { |r| r[:passed] }
|
|
281
|
+
no_pam_passed = no_pam_dsl_results.count { |r| r[:passed] }
|
|
282
|
+
all_passed = core_passed == core_results.size &&
|
|
283
|
+
gem_passed == gem_results.size &&
|
|
284
|
+
testbed_passed == testbed_results.size &&
|
|
285
|
+
(no_pam_dsl_results.empty? || no_pam_passed == no_pam_dsl_results.size)
|
|
286
|
+
|
|
287
|
+
no_pam_dsl_section = if no_pam_dsl_results.any?
|
|
288
|
+
<<~SECTION
|
|
289
|
+
|
|
290
|
+
## Phase 4: Lyra Core Tests Without PAM DSL (All Modes)
|
|
291
|
+
|
|
292
|
+
| Mode | Tests | Assertions | Failures | Errors | Skips | Duration | Status |
|
|
293
|
+
|------|-------|------------|----------|--------|-------|----------|--------|
|
|
294
|
+
#{no_pam_dsl_results.map { |r| "| #{r[:name]} | #{r[:tests]} | #{r[:assertions]} | #{r[:failures]} | #{r[:errors]} | #{r[:skips]} | #{r[:duration].round(1)}s | #{r[:passed] ? "✅" : "❌"} |" }.join("\n")}
|
|
295
|
+
SECTION
|
|
296
|
+
else
|
|
297
|
+
""
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
no_pam_summary_row = if no_pam_dsl_results.any?
|
|
301
|
+
"| Lyra (no PAM DSL) | #{no_pam_passed}/#{no_pam_dsl_results.size} | #{no_pam_dsl_results.sum { |r| r[:tests] }} | #{no_pam_dsl_results.sum { |r| r[:failures] }} | #{no_pam_dsl_results.sum { |r| r[:errors] }} | #{no_pam_dsl_results.sum { |r| r[:skips] }} |"
|
|
302
|
+
else
|
|
303
|
+
""
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
<<~REPORT
|
|
307
|
+
# Comprehensive Test Report
|
|
308
|
+
|
|
309
|
+
**Generated:** #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}
|
|
310
|
+
**Status:** #{all_passed ? "✅ ALL PASSED" : "❌ FAILURES DETECTED"}
|
|
311
|
+
**Total Duration:** #{total_duration.round(1)}s
|
|
312
|
+
|
|
313
|
+
## Summary
|
|
314
|
+
|
|
315
|
+
| Phase | Components Passed | Total Tests | Failures | Errors | Skips |
|
|
316
|
+
|-------|-------------------|-------------|----------|--------|-------|
|
|
317
|
+
| Lyra Core | #{core_passed}/#{core_results.size} | #{core_results.sum { |r| r[:tests] }} | #{core_results.sum { |r| r[:failures] }} | #{core_results.sum { |r| r[:errors] }} | #{core_results.sum { |r| r[:skips] }} |
|
|
318
|
+
| PAM DSL & PetriFlow | #{gem_passed}/#{gem_results.size} | #{gem_results.sum { |r| r[:tests] }} | #{gem_results.sum { |r| r[:failures] }} | #{gem_results.sum { |r| r[:errors] }} | #{gem_results.sum { |r| r[:skips] }} |
|
|
319
|
+
| Testbed | #{testbed_passed}/#{testbed_results.size} | #{testbed_results.sum { |r| r[:tests] }} | #{testbed_results.sum { |r| r[:failures] }} | #{testbed_results.sum { |r| r[:errors] }} | #{testbed_results.sum { |r| r[:skips] }} |
|
|
320
|
+
#{no_pam_summary_row}
|
|
321
|
+
|
|
322
|
+
## Phase 1: Lyra Core Tests (All Modes)
|
|
323
|
+
|
|
324
|
+
| Mode | Tests | Assertions | Failures | Errors | Skips | Duration | Status |
|
|
325
|
+
|------|-------|------------|----------|--------|-------|----------|--------|
|
|
326
|
+
#{core_results.map { |r| "| #{r[:name]} | #{r[:tests]} | #{r[:assertions]} | #{r[:failures]} | #{r[:errors]} | #{r[:skips]} | #{r[:duration].round(1)}s | #{r[:passed] ? "✅" : "❌"} |" }.join("\n")}
|
|
327
|
+
|
|
328
|
+
## Phase 2: PAM DSL & PetriFlow Tests
|
|
329
|
+
|
|
330
|
+
| Component | Tests | Assertions | Failures | Errors | Skips | Duration | Status |
|
|
331
|
+
|-----------|-------|------------|----------|--------|-------|----------|--------|
|
|
332
|
+
#{gem_results.map { |r| "| #{r[:name]} | #{r[:tests]} | #{r[:assertions]} | #{r[:failures]} | #{r[:errors]} | #{r[:skips]} | #{r[:duration].round(1)}s | #{r[:passed] ? "✅" : "❌"} |" }.join("\n")}
|
|
333
|
+
|
|
334
|
+
## Phase 3: Testbed Tests (All Modes)
|
|
335
|
+
|
|
336
|
+
| Mode | Tests | Assertions | Failures | Errors | Skips | Duration | Status |
|
|
337
|
+
|------|-------|------------|----------|--------|-------|----------|--------|
|
|
338
|
+
#{testbed_results.map { |r| "| #{r[:name]} | #{r[:tests]} | #{r[:assertions]} | #{r[:failures]} | #{r[:errors]} | #{r[:skips]} | #{r[:duration].round(1)}s | #{r[:passed] ? "✅" : "❌"} |" }.join("\n")}
|
|
339
|
+
#{no_pam_dsl_section}
|
|
340
|
+
## Environment
|
|
341
|
+
|
|
342
|
+
- **Ruby:** #{RUBY_VERSION}
|
|
343
|
+
- **Rails:** #{Rails::VERSION::STRING rescue "N/A"}
|
|
344
|
+
- **Platform:** #{RUBY_PLATFORM}
|
|
345
|
+
- **Git Branch:** #{`git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip rescue "unknown"}
|
|
346
|
+
- **Git Commit:** #{`git rev-parse --short HEAD 2>/dev/null`.strip rescue "unknown"}
|
|
347
|
+
REPORT
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
namespace :test do
|
|
353
|
+
desc "Run Lyra core tests without PAM DSL (all 6 modes)"
|
|
354
|
+
task :without_pam_dsl do
|
|
355
|
+
puts "\n" + "=" * 80
|
|
356
|
+
puts " LYRA CORE TESTS WITHOUT PAM DSL - ALL MODES"
|
|
357
|
+
puts " Environment: LYRA_DISABLE_PAM_DSL=true"
|
|
358
|
+
puts "=" * 80
|
|
359
|
+
|
|
360
|
+
require "open3"
|
|
361
|
+
results = []
|
|
362
|
+
|
|
363
|
+
ComprehensiveTestRunner::LYRA_CORE_MODES.each_with_index do |config, index|
|
|
364
|
+
puts "\n[#{index + 1}/#{ComprehensiveTestRunner::LYRA_CORE_MODES.size}] #{config[:name]} (no PAM DSL)"
|
|
365
|
+
puts "-" * 60
|
|
366
|
+
|
|
367
|
+
env = {
|
|
368
|
+
"LYRA_MODE" => config[:lyra_mode],
|
|
369
|
+
"LYRA_DISABLE_PAM_DSL" => "true"
|
|
370
|
+
}
|
|
371
|
+
env["LYRA_PROJECTION_MODE"] = config[:projection_mode] if config[:projection_mode]
|
|
372
|
+
|
|
373
|
+
config_start = Time.now
|
|
374
|
+
stdout, stderr, status = Open3.capture3(env, "bundle exec rake test")
|
|
375
|
+
config_duration = Time.now - config_start
|
|
376
|
+
|
|
377
|
+
result = ComprehensiveTestRunner.parse_test_output(stdout + stderr)
|
|
378
|
+
result[:name] = config[:name]
|
|
379
|
+
# Use parsed output for success (SimpleCov may return exit code 2)
|
|
380
|
+
result[:passed] = result[:tests] > 0 && result[:failures] == 0 && result[:errors] == 0
|
|
381
|
+
result[:duration] = config_duration
|
|
382
|
+
results << result
|
|
383
|
+
|
|
384
|
+
ComprehensiveTestRunner.print_result(result, config_duration)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
passed = results.count { |r| r[:passed] }
|
|
388
|
+
total_duration = results.sum { |r| r[:duration] }
|
|
389
|
+
|
|
390
|
+
puts "\n" + "=" * 80
|
|
391
|
+
puts " LYRA WITHOUT PAM DSL SUMMARY: #{passed}/#{results.size} modes passed"
|
|
392
|
+
puts " Total Duration: #{total_duration.round(1)}s"
|
|
393
|
+
puts "=" * 80
|
|
394
|
+
|
|
395
|
+
exit 1 if results.any? { |r| !r[:passed] }
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
desc "Run Lyra core tests in all modes"
|
|
399
|
+
task :all_modes do
|
|
400
|
+
puts "\n" + "=" * 80
|
|
401
|
+
puts " LYRA CORE TESTS - ALL MODES"
|
|
402
|
+
puts "=" * 80
|
|
403
|
+
|
|
404
|
+
start_time = Time.now
|
|
405
|
+
results = ComprehensiveTestRunner.run_core_tests
|
|
406
|
+
total_duration = Time.now - start_time
|
|
407
|
+
|
|
408
|
+
passed = results.count { |r| r[:passed] }
|
|
409
|
+
puts "\n" + "=" * 80
|
|
410
|
+
puts " LYRA CORE TESTS SUMMARY: #{passed}/#{results.size} modes passed"
|
|
411
|
+
puts " Total Duration: #{total_duration.round(1)}s"
|
|
412
|
+
puts "=" * 80
|
|
413
|
+
|
|
414
|
+
exit 1 if results.any? { |r| !r[:passed] }
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
desc "Run comprehensive test suite (Lyra core + PAM DSL + PetriFlow + Testbed + No PAM DSL) with report"
|
|
418
|
+
task :comprehensive do
|
|
419
|
+
puts "\n" + "=" * 80
|
|
420
|
+
puts " COMPREHENSIVE TEST SUITE"
|
|
421
|
+
puts " Running: Lyra core (all modes) + PAM DSL + PetriFlow + Testbed (all modes) + No PAM DSL"
|
|
422
|
+
puts "=" * 80
|
|
423
|
+
|
|
424
|
+
start_time = Time.now
|
|
425
|
+
all_passed = true
|
|
426
|
+
|
|
427
|
+
# Phase 1: Lyra core tests
|
|
428
|
+
puts "\n" + "-" * 80
|
|
429
|
+
puts " PHASE 1: Lyra Core Tests (All Modes)"
|
|
430
|
+
puts "-" * 80
|
|
431
|
+
core_results = ComprehensiveTestRunner.run_core_tests
|
|
432
|
+
all_passed = false if core_results.any? { |r| !r[:passed] }
|
|
433
|
+
|
|
434
|
+
# Phase 2: PAM DSL & PetriFlow tests
|
|
435
|
+
puts "\n" + "-" * 80
|
|
436
|
+
puts " PHASE 2: PAM DSL & PetriFlow Tests"
|
|
437
|
+
puts "-" * 80
|
|
438
|
+
gem_results = ComprehensiveTestRunner.run_gem_tests
|
|
439
|
+
all_passed = false if gem_results.any? { |r| !r[:passed] }
|
|
440
|
+
|
|
441
|
+
# Phase 3: Testbed tests
|
|
442
|
+
puts "\n" + "-" * 80
|
|
443
|
+
puts " PHASE 3: Testbed Tests (All Modes)"
|
|
444
|
+
puts "-" * 80
|
|
445
|
+
testbed_results = ComprehensiveTestRunner.run_testbed_tests
|
|
446
|
+
all_passed = false if testbed_results.any? { |r| !r[:passed] }
|
|
447
|
+
|
|
448
|
+
# Phase 4: Lyra core tests without PAM DSL
|
|
449
|
+
puts "\n" + "-" * 80
|
|
450
|
+
puts " PHASE 4: Lyra Core Tests Without PAM DSL (All Modes)"
|
|
451
|
+
puts "-" * 80
|
|
452
|
+
no_pam_dsl_results = ComprehensiveTestRunner.run_no_pam_dsl_tests
|
|
453
|
+
all_passed = false if no_pam_dsl_results.any? { |r| !r[:passed] }
|
|
454
|
+
|
|
455
|
+
total_duration = Time.now - start_time
|
|
456
|
+
|
|
457
|
+
# Generate report
|
|
458
|
+
puts "\n" + "-" * 80
|
|
459
|
+
puts " GENERATING REPORT"
|
|
460
|
+
puts "-" * 80
|
|
461
|
+
report_path = ComprehensiveTestRunner.generate_report(
|
|
462
|
+
core_results, gem_results, testbed_results, total_duration,
|
|
463
|
+
no_pam_dsl_results: no_pam_dsl_results
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
# Final summary
|
|
467
|
+
puts "\n" + "=" * 80
|
|
468
|
+
if all_passed
|
|
469
|
+
puts " \e[32m✓ COMPREHENSIVE TEST SUITE PASSED\e[0m"
|
|
470
|
+
else
|
|
471
|
+
puts " \e[31m✗ COMPREHENSIVE TEST SUITE FAILED\e[0m"
|
|
472
|
+
end
|
|
473
|
+
puts " Total Duration: #{total_duration.round(1)}s"
|
|
474
|
+
puts " Report: #{report_path}"
|
|
475
|
+
puts "=" * 80
|
|
476
|
+
|
|
477
|
+
exit 1 unless all_passed
|
|
478
|
+
end
|
|
479
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :lyra do
|
|
4
|
+
VERSION_FILES = {
|
|
5
|
+
lyra: "lib/lyra/version.rb",
|
|
6
|
+
petri_flow: "gems/petri_flow/lib/petri_flow/version.rb",
|
|
7
|
+
pam_dsl: "gems/pam_dsl/lib/pam_dsl/version.rb"
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
desc "Show current versions of all components"
|
|
11
|
+
task :version do
|
|
12
|
+
puts "\n" + "=" * 50
|
|
13
|
+
puts "Lyra/ORFEAS Component Versions"
|
|
14
|
+
puts "=" * 50
|
|
15
|
+
|
|
16
|
+
versions = {}
|
|
17
|
+
VERSION_FILES.each do |name, file|
|
|
18
|
+
versions[name] = extract_version(file)
|
|
19
|
+
puts "%-15s %s" % [name, versions[name]]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
puts "-" * 50
|
|
23
|
+
lyra_v = Gem::Version.new(versions[:lyra])
|
|
24
|
+
all_valid = [:petri_flow, :pam_dsl].all? do |gem|
|
|
25
|
+
Gem::Version.new(versions[gem]) <= lyra_v
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if all_valid
|
|
29
|
+
puts "Status: OK (Lyra >= all gem versions)"
|
|
30
|
+
else
|
|
31
|
+
puts "Status: WARNING (Lyra should be >= all gem versions)"
|
|
32
|
+
end
|
|
33
|
+
puts "=" * 50 + "\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "Set version for components. Scope: all (default), lyra, petri_flow, pam_dsl, gems"
|
|
37
|
+
task :set_version, [:version, :scope] do |_t, args|
|
|
38
|
+
unless args[:version]
|
|
39
|
+
puts "Usage:"
|
|
40
|
+
puts " rake lyra:set_version[1.0.0] # All components"
|
|
41
|
+
puts " rake lyra:set_version[1.0.0,lyra] # Lyra only"
|
|
42
|
+
puts " rake lyra:set_version[1.0.0,petri_flow] # PetriFlow + Lyra"
|
|
43
|
+
puts " rake lyra:set_version[1.0.0,pam_dsl] # PAM DSL + Lyra"
|
|
44
|
+
puts " rake lyra:set_version[1.0.0,gems] # Both gems + Lyra"
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
new_version = args[:version]
|
|
49
|
+
scope = (args[:scope] || "all").to_sym
|
|
50
|
+
|
|
51
|
+
unless new_version.match?(/^\d+\.\d+\.\d+$/)
|
|
52
|
+
puts "Error: Version must be in format X.Y.Z (e.g., 1.0.0)"
|
|
53
|
+
exit 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Determine which components to update
|
|
57
|
+
components = case scope
|
|
58
|
+
when :all
|
|
59
|
+
[:lyra, :petri_flow, :pam_dsl]
|
|
60
|
+
when :lyra
|
|
61
|
+
[:lyra]
|
|
62
|
+
when :petri_flow
|
|
63
|
+
[:lyra, :petri_flow]
|
|
64
|
+
when :pam_dsl
|
|
65
|
+
[:lyra, :pam_dsl]
|
|
66
|
+
when :gems
|
|
67
|
+
[:lyra, :petri_flow, :pam_dsl]
|
|
68
|
+
else
|
|
69
|
+
puts "Unknown scope: #{scope}"
|
|
70
|
+
puts "Valid scopes: all, lyra, petri_flow, pam_dsl, gems"
|
|
71
|
+
exit 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
puts "\n" + "=" * 50
|
|
75
|
+
puts "Setting Version to #{new_version}"
|
|
76
|
+
puts "Scope: #{scope}"
|
|
77
|
+
puts "=" * 50
|
|
78
|
+
|
|
79
|
+
# Validate: Lyra must be >= gem versions
|
|
80
|
+
new_v = Gem::Version.new(new_version)
|
|
81
|
+
VERSION_FILES.each do |name, file|
|
|
82
|
+
next if components.include?(name)
|
|
83
|
+
current_v = Gem::Version.new(extract_version(file))
|
|
84
|
+
if current_v > new_v && name != :lyra
|
|
85
|
+
puts "Error: Cannot set Lyra to #{new_version} - #{name} is at #{current_v}"
|
|
86
|
+
puts "Lyra version must be >= all gem versions"
|
|
87
|
+
exit 1
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
components.each do |name|
|
|
92
|
+
file = VERSION_FILES[name]
|
|
93
|
+
old_version = extract_version(file)
|
|
94
|
+
update_version_file(file, new_version)
|
|
95
|
+
puts "#{name}: #{old_version} -> #{new_version}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Show final state
|
|
99
|
+
puts "-" * 50
|
|
100
|
+
puts "Final versions:"
|
|
101
|
+
VERSION_FILES.each do |name, file|
|
|
102
|
+
puts " %-15s %s" % [name, extract_version(file)]
|
|
103
|
+
end
|
|
104
|
+
puts "=" * 50 + "\n"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
desc "Bump version (major/minor/patch). Scope: all (default), lyra, petri_flow, pam_dsl, gems"
|
|
108
|
+
task :bump, [:type, :scope] do |_t, args|
|
|
109
|
+
type = args[:type] || "patch"
|
|
110
|
+
scope = args[:scope] || "all"
|
|
111
|
+
|
|
112
|
+
# Calculate new version based on Lyra's current version
|
|
113
|
+
current = extract_version(VERSION_FILES[:lyra])
|
|
114
|
+
new_version = calculate_new_version(current, type)
|
|
115
|
+
|
|
116
|
+
# Invoke set_version with the calculated version
|
|
117
|
+
Rake::Task["lyra:set_version"].invoke(new_version, scope)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def extract_version(file)
|
|
123
|
+
return "0.0.0" unless File.exist?(file)
|
|
124
|
+
|
|
125
|
+
content = File.read(file)
|
|
126
|
+
match = content.match(/VERSION\s*=\s*["']([^"']+)["']/)
|
|
127
|
+
match ? match[1] : "0.0.0"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def calculate_new_version(current, type)
|
|
131
|
+
parts = current.split(".").map(&:to_i)
|
|
132
|
+
parts = [0, 0, 0] if parts.length < 3
|
|
133
|
+
|
|
134
|
+
case type.to_s.downcase
|
|
135
|
+
when "major"
|
|
136
|
+
"#{parts[0] + 1}.0.0"
|
|
137
|
+
when "minor"
|
|
138
|
+
"#{parts[0]}.#{parts[1] + 1}.0"
|
|
139
|
+
when "patch"
|
|
140
|
+
"#{parts[0]}.#{parts[1]}.#{parts[2] + 1}"
|
|
141
|
+
else
|
|
142
|
+
if type.match?(/^\d+\.\d+\.\d+$/)
|
|
143
|
+
type
|
|
144
|
+
else
|
|
145
|
+
puts "Unknown version type: #{type}"
|
|
146
|
+
puts "Use: major, minor, patch, or a specific version (e.g., 1.0.0)"
|
|
147
|
+
exit 1
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def update_version_file(file, new_version)
|
|
153
|
+
return unless File.exist?(file)
|
|
154
|
+
|
|
155
|
+
content = File.read(file)
|
|
156
|
+
updated = content.gsub(/VERSION\s*=\s*["'][^"']+["']/, "VERSION = \"#{new_version}\"")
|
|
157
|
+
File.write(file, updated)
|
|
158
|
+
end
|
|
159
|
+
end
|