Class: GraphWeaver::Testing::Cassette

Inherits:
Object
  • Object
show all
Defined in:
lib/graph_weaver/testing/cassette.rb

Overview

Capture/replay above the transport (no HTTP interception): a cassette is a YAML file of variables, response entries, keyed on the normalized query + variables.

 # record against a real client, replay when the file exists:
 client = GraphWeaver::Testing::Cassette.use("github", client: real)

Cassettes hold real responses — anonymize before committing:

 Cassette.new("spec/cassettes/github.yml").anonymize!(schema:)

keeps every shape (list lengths, null positions, enums, __typename, id relationships via a consistent mapping) while replacing values with fakes, semantically matched where field names allow.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Cassette

Returns a new instance of Cassette.



39
40
41
42
# File 'lib/graph_weaver/testing/cassette.rb', line 39

def initialize(path)
  @path = Testing.cassette_path(path)
  @entries = File.exist?(@path) ? YAML.safe_load_file(@path, aliases: true) : []
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



37
38
39
# File 'lib/graph_weaver/testing/cassette.rb', line 37

def path
  @path
end

Class Method Details

.key(query, variables) ⇒ Object



94
95
96
# File 'lib/graph_weaver/testing/cassette.rb', line 94

def self.key(query, variables)
  { "query" => query.gsub(/\s+/, " ").strip, "variables" => variables || {} }
end

.use(path, client: nil) ⇒ Object

Replay when recorded, record when not (VCR's once mode). client: is required to record; omit it to replay-or-raise. With Testing.config.record on (or GRAPHWEAVER_RECORD=1), always records — the "just re-record everything" switch.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/graph_weaver/testing/cassette.rb', line 51

def self.use(path, client: nil)
  cassette = new(path)
  if Testing.config.record && client
    Recorder.new(client, cassette)
  elsif cassette.exist?
    Replayer.new(cassette)
  elsif client
    Recorder.new(client, cassette)
  else
    raise MissingRecording.new(path: cassette.path, query: "(no client to record with)")
  end
end

Instance Method Details

#anonymize!(schema:, seed: nil, mode: nil) ⇒ Object

Replace recorded response values with fakes, preserving structure. Walks each entry's query against the schema (like FakeClient, but transforming what's there instead of generating from scratch).



84
85
86
87
88
89
90
91
92
# File 'lib/graph_weaver/testing/cassette.rb', line 84

def anonymize!(schema:, seed: nil, mode: nil)
  anonymizer = Anonymizer.new(schema:, seed:, mode:)
  @entries.each do |entry|
    data = entry.dig("response", "data")
    entry["response"]["data"] = anonymizer.anonymize(entry["query"], data) if data
  end
  save
  self
end

#exist?Boolean

Returns:

  • (Boolean)


44
# File 'lib/graph_weaver/testing/cassette.rb', line 44

def exist? = File.exist?(@path)

#lookup(query, variables) ⇒ Object



64
65
66
67
# File 'lib/graph_weaver/testing/cassette.rb', line 64

def lookup(query, variables)
  wanted = self.class.key(query, variables)
@entries.find { |entry| entry["key"] == wanted }
end

#record(query, variables, response) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/graph_weaver/testing/cassette.rb', line 69

def record(query, variables, response)
  entry = {
    "key" => self.class.key(query, variables),
    "query" => query,
    "variables" => variables,
    "response" => response,
  }
  @entries.reject! { |existing| existing["key"] == entry["key"] }
  @entries << entry
  save
end

#sizeObject



45
# File 'lib/graph_weaver/testing/cassette.rb', line 45

def size = @entries.size