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
# Promotes a logged DAV exchange to a fixture step: the request the
# client sent, less the headers that identify the tailnet
# (docs/plans/2026-09-13-dav-observability.md, "Every DAV exchange is
# logged locally"). The response is `rake fixtures`'s to write, because
# a response file is a snapshot of this server rather than evidence.

namespace :fixtures do
  desc "Write logged exchange EXCHANGE's request into fixture step directory STEP"
  task :extract do
    require "pathname"
    require "pro_tacts"
    require "pro_tacts/exchange_log"
    require_relative "../test/pro_tacts/exchange_fixtures"

    id = ENV.fetch("EXCHANGE") { abort "EXCHANGE= names the exchange: the id on its log lines and its Sentry exchange tag." }
    step = Pathname.new(ENV.fetch("STEP") { abort "STEP= names the step directory to write, e.g. test/fixtures/ios-exchange/07-delete-contact." })
    request = step / "request"
    abort "#{request} exists. A request file is evidence; delete it first if this exchange supersedes it." if request.exist?

    # The configured log, then rake dev's.
    logs = [ProTacts.config.exchange_log_path, "log/dev.log"].uniq
    head, body = logs.lazy.filter_map { ProTacts::ExchangeLog.read_request(id, it) }.first
    abort "No exchange #{id} in #{logs.join(' or ')}, or their rotations." if head.nil?

    dropped = ExchangeFixtures.write_request(step, head, body)
    puts <<~DONE
      Wrote #{request}, dropping #{dropped.empty? ? 'no headers' : dropped.join(', ')}.
      Run `rake fixtures` to record its response, and review the diff.
    DONE
  end
end