1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
require_relative "../test_helper"
require_relative "../fixture_data"
require "pathname"
require "rack/test"

require_relative "exchange_fixtures"
require "pro_tacts/web"

# Replays each recorded client session against the app and compares
# every response, byte for byte, with the recorded baseline. A failure
# means the responses drifted from what a working client session saw;
# either fix the drift or, if the change was deliberate, regenerate with
# `rake fixtures`.
#
# The steps of one recording run in sorted order against one freshly
# seeded store, in a single test: the session is a conversation, and a
# step that writes has to be able to change what the steps after it see.
# A failure stops that replay at the first divergent step — the steps
# after it would be measuring a state the real session never produced.
#
# The two recordings are independent conversations and get a store each,
# so what the iOS session deletes is still there for the macOS one.
class ExchangesTest < Minitest::Test
  include Rack::Test::Methods

  def app
    ProTacts::Web
  end

  def test_the_recorded_macos_session_replays_in_order
    replay(ExchangeFixtures::MACOS)
  end

  def test_the_recorded_ios_session_replays_in_order
    replay(ExchangeFixtures::IOS)
  end

  private

  def replay(recording)
    # A store of its own so the writes a session may make never touch the
    # one the rest of the suite reads, built fresh so a replay never depends
    # on what a previous run left behind.
    directory = Pathname.new(__dir__).parent.parent / "tmp" / recording.directory.basename
    store = FixtureData.install(directory, genesis: ExchangeFixtures::GENESIS)
    original = ProTacts::Web.store
    ProTacts::Web.store = store

    recording.steps.each do |step|
      request step.path, { method: step.method, input: step.body }.merge(recording.env_for(step))

      expected = recording.recorded_response(step)

      assert_equal expected.status, last_response.status, "#{step.name}: status"
      expected.headers.each do |name, value|
        assert_equal value, last_response[name], "#{step.name}: #{name}"
      end
      assert_equal expected.body, last_response.body.chomp, "#{step.name}: body"
    end
  ensure
    ProTacts::Web.store = original
    store&.close
  end
end