@specsage/cli 0.1.12 → 0.1.13

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.
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Shared base error type for step clients (StepClient, DirectStepClient).
4
+ # Runner can rescue this base type to handle errors from either client.
5
+ # Each client defines its own StepError subclass for API stability.
6
+ class StepClientError < StandardError; end
package/lib/runner.rb CHANGED
@@ -11,6 +11,7 @@ require 'fileutils'
11
11
  # Determine SpecSage home directory for locating resources
12
12
  SPECSAGE_HOME ||= File.expand_path('..', __dir__)
13
13
 
14
+ require_relative 'client_errors'
14
15
  require_relative 'step_client'
15
16
 
16
17
  class Runner
@@ -25,7 +26,8 @@ class Runner
25
26
 
26
27
  # Initialize runner with scenario data from server
27
28
  # @param all_scenarios [Hash] optional map of scenario_id => scenario_data for pre-scenario lookup
28
- def initialize(scenario_data, visible: false, record: false, publisher: nil, server_run_id: nil, all_scenarios: nil)
29
+ # @param step_client [StepClient, DirectStepClient] optional pre-configured client for step processing
30
+ def initialize(scenario_data, visible: false, record: false, publisher: nil, server_run_id: nil, all_scenarios: nil, step_client: nil)
29
31
  @scenario = normalize_scenario_data(scenario_data)
30
32
  @scenario_id = @scenario['id']
31
33
  @scenario_name = @scenario['name'] || @scenario['id'] || 'unnamed'
@@ -38,7 +40,7 @@ class Runner
38
40
  @next_request_id = 1
39
41
  @node_channel_poisoned = false
40
42
  @publisher = publisher
41
- @step_client = nil
43
+ @step_client = step_client # Accept pre-configured client (nil = create StepClient in run())
42
44
  @server_run_id = server_run_id
43
45
  @credentials = {} # Credentials received from server { "NAME" => "value" }
44
46
  @max_steps = nil # Max browser actions allowed, received from server on first step
@@ -51,7 +53,10 @@ class Runner
51
53
 
52
54
  raise ArgumentError, 'server_run_id is required' unless @server_run_id
53
55
 
54
- @step_client = StepClient.new(
56
+ # Only create StepClient if not injected (CLI path uses HTTP, Sidekiq injects DirectStepClient)
57
+ # TODO: Validate publisher presence here when step_client is nil. Currently crashes with
58
+ # NoMethodError on @publisher.base_url if caller omits both step_client and publisher.
59
+ @step_client ||= StepClient.new(
55
60
  base_url: @publisher.base_url,
56
61
  server_run_id: @server_run_id,
57
62
  api_key: @publisher.api_key
@@ -124,7 +129,7 @@ class Runner
124
129
  cleanup_temp_dir
125
130
 
126
131
  result[:verdict]
127
- rescue StepClient::StepError => e
132
+ rescue StepClientError => e
128
133
  send_client_verdict_if_needed('ERROR', "Server error: #{e.message}")
129
134
  stop_node_process
130
135
  upload_video
@@ -245,7 +250,7 @@ class Runner
245
250
  status: status,
246
251
  reason: reason
247
252
  )
248
- rescue StepClient::StepError => e
253
+ rescue StepClientError => e
249
254
  log "Warning: Failed to send main scenario verdict: #{e.message}"
250
255
  end
251
256
 
@@ -587,7 +592,7 @@ class Runner
587
592
  log "Uploading video (#{@video_data.bytesize} bytes)..."
588
593
  @step_client.upload_video(scenario_id: @scenario_id, video_data: @video_data)
589
594
  log "Video uploaded successfully."
590
- rescue StepClient::StepError => e
595
+ rescue StepClientError => e
591
596
  log "Warning: Failed to upload video: #{e.message}"
592
597
  end
593
598
 
@@ -641,7 +646,7 @@ class Runner
641
646
  status: status,
642
647
  reason: reason
643
648
  )
644
- rescue StepClient::StepError => e
649
+ rescue StepClientError => e
645
650
  log "Warning: Failed to send verdict to server: #{e.message}"
646
651
  end
647
652
 
@@ -7,9 +7,12 @@ require "net/http"
7
7
  require "uri"
8
8
  require "json"
9
9
  require "base64"
10
+ require_relative "client_errors"
10
11
 
11
12
  class StepClient
12
- class StepError < StandardError; end
13
+ # Subclass shared error for API stability (supports subclassing, .class checks, .name)
14
+ # Runner can rescue either StepClient::StepError or the base StepClientError
15
+ class StepError < StepClientError; end
13
16
 
14
17
  attr_reader :server_run_id
15
18
 
@@ -59,6 +62,10 @@ class StepClient
59
62
 
60
63
  response = post("/api/runs/#{@server_run_id}/step", body)
61
64
 
65
+ # TODO: Credentials defaulted to {} may change semantics. Server returns nil when no
66
+ # credentials are present (after first step). Returning {} instead of nil may cause
67
+ # downstream code to branch incorrectly (`if credentials` is always truthy). Consider
68
+ # returning nil when server returns nil, or document this as intentional contract.
62
69
  {
63
70
  action: response["action"],
64
71
  step_number: response["step_number"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specsage/cli",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "SpecSage CLI - AI-powered end-to-end testing automation (Node wrapper for Ruby CLI)",
5
5
  "type": "module",
6
6
  "bin": {