<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.6.17 (Ruby 3.1.2) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-dcook-ppm-dap-interop-test-design-01" category="info" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.14.2 -->
  <front>
    <title>DAP Interoperation Test Design</title>
    <seriesInfo name="Internet-Draft" value="draft-dcook-ppm-dap-interop-test-design-01"/>
    <author fullname="David Cook">
      <organization>ISRG</organization>
      <address>
        <email>dcook@letsencrypt.org</email>
      </address>
    </author>
    <date year="2022" month="August" day="31"/>
    <area>Security</area>
    <workgroup>Privacy Preserving Measurement</workgroup>
    <abstract>
      <t>This document defines a common test interface for implementations of the Distributed Aggregation Protocol for Privacy Preserving Measurement (DAP-PPM) and describes how this test interface can be used to perform interoperation testing between the implementations. Tests are orchestrated with containers, and new test-only APIs are introduced to provision DAP-PPM tasks and initiate processing.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://divergentdave.github.io/draft-dcook-ppm-dap-interop-test-design/draft-dcook-ppm-dap-interop-test-design.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-dcook-ppm-dap-interop-test-design/"/>.
      </t>
      <t>
        Discussion of this document takes place on the
        Privacy Preserving Measurement Working Group mailing list (<eref target="mailto:ppm@ietf.org"/>),
        which is archived at <eref target="https://mailarchive.ietf.org/arch/browse/ppm/"/>.
        Subscribe at <eref target="https://www.ietf.org/mailman/listinfo/ppm/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/divergentdave/draft-dcook-ppm-dap-interop-test-design"/>.</t>
    </note>
  </front>
  <middle>
    <section anchor="introduction">
      <name>Introduction</name>
      <t>This document defines a common test interface for implementations of the Distributed Aggregation Protocol for Privacy Preserving Measurement <xref target="DAP-PPM"/>. This test interface facilitates interoperation tests between different participating DAP-PPM implementations. As DAP-PPM has four distinct protocol roles, (client, leader aggregator, helper aggregator, and collector) manual interoperation testing between all combinations of even a small number of DAP-PPM implementations could be taxing. The goal of this document's common test interface is to enable automation of these interoperation tests, so that different participating implementations can be exchanged for each other, and the same test suite can be re-run on different combinations of implementations. Simplifying interoperation testing will aid in identifying errors in implementations, identifying ambiguities in the protocol specification, and reducing regressions in implementations.</t>
      <t>Taking inspiration from QuicInteropRunner <xref target="SI2020"/>, each participating implementation provides one or more container images adhering to a common interface. A test runner will start one container for each protocol participant, configure networking between the containers, and send various HTTP API requests. As part of this common testing interface, the HTTP servers in the containers will support some new test-only HTTP APIs, which will allow the test runner to provision shared task parameters and secrets, as well as trigger the start of different sub-protocols.</t>
    </section>
    <section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
    </section>
    <section anchor="container-interface">
      <name>Container Interface</name>
      <t>Each participating DAP implementation may provide one or more container images, one for each protocol role it implements. (client, leader, helper, and collector) A list of available container images will be maintained for each role. Implementations may want to submit a single aggregator image in both the leader list and helper list. The test runner will fetch each container using the given repository, image name, and tag.</t>
      <t>When the container's entry point executable is run, it <bcp14>SHALL</bcp14> start up an HTTP server listening on port 8080. In all cases, the container will serve the endpoints described in <xref target="test-api"/> (particularly, the subsection appropriate to its protocol role). In the case of a helper or leader container, it <bcp14>SHALL</bcp14> also serve the endpoints specified by <xref target="DAP-PPM"/> on a port (which <bcp14>MAY</bcp14> be the same port 8080 as used to serve the interoperation test API) at some relative path. The container should run indefinitely, and the test runner will terminate the container on completion of the test case. (While DAP-PPM requires HTTPS connections, only using HTTP between containers simplifies test setup. Putting TLS client/server interop out-of-scope for these tests is acceptable, as it's not of interest.)</t>
      <t>Log output <bcp14>SHOULD</bcp14> be captured into the directory "/logs" inside the container. This will be copied out to the host for inspection on completion of the test case.</t>
      <t>No environment variables or volume mounts will be provided to the containers.</t>
    </section>
    <section anchor="test-api">
      <name>Interoperation Test API</name>
      <t>Each container will have an HTTP server listening on port 8080 for commands from the test runner. All requests <bcp14>MUST</bcp14> use the HTTP method POST. Requests and responses for each endpoint listed below <bcp14>SHALL</bcp14> be encoded JSON objects <xref target="RFC8729"/>, with media type <tt>application/json</tt>. All binary blobs (i.e. task IDs, HPKE configurations, and verification keys) <bcp14>SHALL</bcp14> be encoded as strings with base64url <xref target="RFC4648"/>, inside the JSON objects. Certain integer values will be encoded as strings in base 10 instead of as numbers, where noted, if JSON numbers cannot fully represent the range of valid values.</t>
      <t>Each of these test APIs should return a status code of 200 OK if the command was received, recognized, and parsed successfully, regardless of whether any underlying DAP-PPM request succeeded or failed. The DAP-level success or failure will be included in the test API response body. If a request is made to an endpoint starting with "/internal/test/", but not listed here, a status code of 404 Not Found <bcp14>SHOULD</bcp14> be returned, to simplify the introduction of new test APIs.</t>
      <section anchor="common-structures">
        <name>Common Structures</name>
        <t>In multiple APIs defined below, the test runner will send the name of a VDAF, along with the parameters necessary to fully specify the VDAF. These will be stored in a nested object, with the following attributes (new <tt>type</tt> values and new keys will be added as new VDAFs are defined).</t>
        <table anchor="vdaf-object">
          <name>VDAF JSON object structure</name>
          <thead>
            <tr>
              <th align="left">Key</th>
              <th align="left">Value</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td align="left">
                <tt>type</tt></td>
              <td align="left">One of <tt>"Prio3Aes128Count"</tt>, <tt>"Prio3Aes128Sum"</tt>, or <tt>"Prio3Aes128Histogram"</tt></td>
            </tr>
            <tr>
              <td align="left">
                <tt>bits</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt>)</td>
              <td align="left">The bit width of the integers being summed, (as a number) used to parameterize the Prio3Aes128Sum VDAF.</td>
            </tr>
            <tr>
              <td align="left">
                <tt>buckets</tt> (only present if <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>)</td>
              <td align="left">An array of histogram bucket boundaries, (encoded in base 10 as strings) used to parameterize the Prio3Aes128Histogram VDAF.</td>
            </tr>
          </tbody>
        </table>
      </section>
      <section anchor="client">
        <name>Client</name>
        <section anchor="client-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check if the client container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14> return a status code of 200 OK.</t>
        </section>
        <section anchor="upload">
          <name><tt>/internal/test/upload</tt></name>
          <t>Upon receipt of this command, the client container will construct a DAP-PPM report with the given configuration and measurement, and submit it. The client container will send its response to the test runner once report submission has either succeeded or permanently failed.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>taskId</tt></td>
                <td align="left">A base64url-encoded DAP-PPM <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The helper's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in {vdaf-object}. This determines the VDAF to be used when constructing a report.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>measurement</tt></td>
                <td align="left">If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Count"</tt>: 0 or 1. If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Sum"</tt>: a string (representing an integer in base 10). If the VDAF's <tt>type</tt> is <tt>"Prio3Aes128Histogram"</tt>: a string (representing an integer in base 10).</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>nonceTime</tt> (optional)</td>
                <td align="left">If present, this provides a substitute time value that should be used when constructing the report. If not present, the current system time should be used, as per normal. The time is represented as a number, with a value of the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>minBatchDuration</tt></td>
                <td align="left">A number, providing the minimum number of seconds that can be in a batch's interval. The batch interval will always be a multiple of this value.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the report was submitted to the leader successfully, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="aggregator-leader-or-helper">
        <name>Aggregator (Leader or Helper)</name>
        <section anchor="aggregator-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check if the aggregator container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14> return a status code of 200 OK.</t>
        </section>
        <section anchor="endpoint-for-task">
          <name><tt>/internal/test/endpoint_for_task</tt></name>
          <t>Request the base URL for DAP-PPM endpoints for a new task. This API will be invoked immediately before <tt>/internal/test/add_task</tt> (see <xref target="aggregator-add-task"/>), to determine the endpoint URLs of the aggregators. If the aggregator uses a common set of DAP-PPM endpoints for all tasks, it could always return the same value, such as the relative URL <tt>/</tt>. Alternately, implementations may wish to generate new endpoints for each task, derive the endpoint based on the <tt>TaskId</tt>, etc.</t>
          <t>The test runner will provide the hostname at which the aggregator is externally reachable. If the aggregator returns a relative URL, the test runner will combine it with the hostname into an absolute URL, assuming that the port is 8080. Otherwise, the aggregator can incorporate the hostname into an absolute URL and return that.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>taskId</tt></td>
                <td align="left">A base64url-encoded DAP-PPM <tt>TaskId</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>aggregatorId</tt></td>
                <td align="left">0 if this aggregator is the leader, or 1 if this aggregator is the helper.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>hostname</tt></td>
                <td align="left">This aggregator's hostname in the interoperation test environment. This may optionally be used in constructing the endpoint URL as an absolute URL.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the endpoint was successfully selected or set up, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>endpoint</tt></td>
                <td align="left">A relative or absolute URL, specifying the DAP-PPM aggregator endpoint that should be used for this task. If the test runner receives a relative URL, it will transform it into an absolute URL before performing the next phase of task setup.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="aggregator-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the aggregator, with the given configuration and secrets.</t>
          <t>The HPKE keypair generated for this task should use the mandatory-to-implement algorithms in section 6 of <xref target="DAP-PPM"/>, for broad compatibility.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>taskId</tt></td>
                <td align="left">A base64url-encoded DAP-PPM <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>helper</tt></td>
                <td align="left">The helper's endpoint URL. The test runner will ensure this is an absolute URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in {vdaf-object}. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leaderAuthenticationToken</tt></td>
                <td align="left">The authentication bearer token that is shared with the other aggregator, as a string. This string must be safe for use as an HTTP header value.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collectorAuthenticationToken</tt> (only present if <tt>aggregatorId</tt> is 0)</td>
                <td align="left">The authentication bearer token that is shared between the leader and collector, as a string. This string must be safe for use as an HTTP header value.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>aggregatorId</tt></td>
                <td align="left">0 if this aggregator is the leader, or 1 if this aggregator is the helper.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>verifyKey</tt></td>
                <td align="left">The verification key shared by the two aggregators, encoded with base64url.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>maxBatchLifetime</tt></td>
                <td align="left">A number, providing the maximum number of times any report can be included in a collect request.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>minBatchSize</tt></td>
                <td align="left">A number, providing the minimum number of reports that must be in a batch for it to be collected.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>minBatchDuration</tt></td>
                <td align="left">A number, providing the minimum number of seconds that can be in a batch's interval. The batch interval will always be a multiple of this value.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collectorHpkeConfig</tt></td>
                <td align="left">The collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="collector">
        <name>Collector</name>
        <section anchor="collector-ready">
          <name><tt>/internal/test/ready</tt></name>
          <t>The test runner will POST an empty object (i.e. <tt>{}</tt>) to this endpoint to check if the collector container is ready to serve requests. If it is ready, it <bcp14>MUST</bcp14> return a status code of 200 OK.</t>
        </section>
        <section anchor="collector-add-task">
          <name><tt>/internal/test/add_task</tt></name>
          <t>Register a task with the collector, with the given configuration. Returns the collector's HPKE configuration for this task.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>taskId</tt></td>
                <td align="left">A base64url-encoded DAP-PPM <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>leader</tt></td>
                <td align="left">The leader's endpoint URL.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>vdaf</tt></td>
                <td align="left">An object, with the layout given in {vdaf-object}. This determines the task's VDAF.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collectorAuthenticationToken</tt></td>
                <td align="left">The authentication bearer token that is shared between the leader and collector, as a string. This string must be safe for use as an HTTP header value.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the task was successfully set up, or <tt>"error"</tt> otherwise. (for example, if the VDAF was not supported)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>collectorHpkeConfig</tt> (if successful)</td>
                <td align="left">The collector's HPKE configuration, encoded in base64url, for encryption of aggregate shares.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collect-start">
          <name><tt>/internal/test/collect_start</tt></name>
          <t>Send a collect request to the leader with the provided parameters, and return a handle to the test runner identifying this collect request. The test runner will provide this handle to the collector in subsequent <tt>/internal/test/collect_poll</tt> requests (see <xref target="collect-poll"/>).</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>taskId</tt></td>
                <td align="left">A base64url-encoded DAP-PPM <tt>TaskId</tt>.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>aggParam</tt></td>
                <td align="left">A base64url-encoded aggregation parameter.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batchIntervalStart</tt></td>
                <td align="left">The start of the batch interval, represented as a number equal to the number of seconds since the UNIX epoch.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>batchIntervalDuration</tt></td>
                <td align="left">The duration of the batch interval in seconds, as a number.</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">
                  <tt>"success"</tt> if the collect request succeeded, or <tt>"error"</tt> otherwise.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>handle</tt> (if successful)</td>
                <td align="left">A handle produced by the collector to refer to this collect request. This must be a string.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="collect-poll">
          <name><tt>/internal/test/collect_poll</tt></name>
          <t>Upon receiving this command, the collector will poll the leader's collect URL for the collect job associated with the provided handle, and provide the status and result to the test runner.</t>
          <table>
            <name>Request JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>handle</tt></td>
                <td align="left">The handle for a collect request from a previous invocation of <tt>/internal/test/collect_start</tt>. (see <xref target="collect-start"/>)</td>
              </tr>
            </tbody>
          </table>
          <table>
            <name>Response JSON object structure</name>
            <thead>
              <tr>
                <th align="left">Key</th>
                <th align="left">Value</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>status</tt></td>
                <td align="left">Either <tt>"complete"</tt> if the result was returned, <tt>"in progress"</tt> if the result was not yet ready, or <tt>"error"</tt> if an error occurred.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>error</tt> (optional)</td>
                <td align="left">An optional error message, to assist in troubleshooting. This will be included in the test runner logs.</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>result</tt> (if complete)</td>
                <td align="left">The result of the aggregation. If the VDAF is of type Prio3Aes128Count, this will be a number. If the VDAF is of type Prio3Aes128Sum, this will be a string, representing an integer in base 10. If the VDAF is of type Prio3Aes128Histogram, this will be an array of numbers.</td>
              </tr>
            </tbody>
          </table>
        </section>
        <section anchor="heavy-hitters">
          <name>Heavy Hitters</name>
          <t>Once Poplar1 reaches a future draft of <xref target="DAP-PPM"/>, additional test APIs for collector containers should be introduced to perform an entire Heavy Hitters computation on a given Poplar1 task and collection interval, encompassing multiple collect flows automatically initiated by the collector.</t>
        </section>
      </section>
      <section anchor="test-cases">
        <name>Test Cases</name>
        <t>Test cases could be written to cover the following scenarios.</t>
        <ul spacing="normal">
          <li>Test successful aggregations with each VDAF.</li>
          <li>Test an aggregation over a few hundred or thousand reports, to exercise the aggregators' division of reports into aggregation jobs.</li>
          <li>Test that uploading a report with a time far in the future is rejected.</li>
          <li>Confirm that leaders and helpers reject requests with respective authentication tokens that are incorrect.</li>
          <li>Test enforcement of <tt>max_batch_lifetime</tt> by making overlapping collect requests.</li>
          <li>Perform an entire aggregation and collect flow, attempt to upload a late report that falls into the same collect interval, and test that performing the collect request a second time yields the same result.</li>
          <li>Attempt to upload a canned report from the test runner more than once, and confirm that anti-replay measures were effective by inspecting the aggregation result.</li>
        </ul>
      </section>
      <section anchor="other-test-considerations">
        <name>Other Test Considerations</name>
        <t>All test cases should automatically fail after a generous timeout.</t>
        <t>It is the responsibility of the test runner to wait for all containers to start up and respond successfully to a request to <tt>/internal/test/ready</tt> before sending any further commands.</t>
        <t>Aggregator URLs will be constructed by the test runner with hostnames that resolve to the respective containers within the container network.</t>
        <t>Once a future <xref target="DAP-PPM"/> draft solves the issue of retries in the aggregate flow, a reverse proxy could be introduced in front of each aggregator to inject failures when sending requests or responses, to test the protocol's resilience. (It is known such a test would fail based on the current protocol.)</t>
      </section>
      <section anchor="test-runner-operation">
        <name>Test Runner Operation</name>
        <t>The following sequence outlines how the test runner will use the above APIs on port 8080 of each container to perform a typical integration test, executing a successful aggregation.</t>
        <ol spacing="normal" type="1"><li>Create and start containers.</li>
          <li>Set up networking between containers.</li>
          <li>Try sending <tt>/internal/test/ready</tt> requests to each container, and retry until they succeed.</li>
          <li>Generate a random <tt>TaskId</tt>, random authentication tokens, and a VDAF verification key.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request (<xref target="endpoint-for-task"/>) to the leader.</li>
          <li>Send a <tt>/internal/test/endpoint_for_task</tt> request to the helper.</li>
          <li>Construct aggregator URLs using the above responses.</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="collector-add-task"/>) to the collector. (the collector generates an HPKE key pair as a side-effect)</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the leader.</li>
          <li>Send a <tt>/internal/test/add_task</tt> request (<xref target="aggregator-add-task"/>) to the helper.</li>
          <li>Send one or more <tt>/internal/test/upload</tt> requests (<xref target="upload"/>) to the client.</li>
          <li>Send a <tt>/internal/test/collect_start</tt> request (<xref target="collect-start"/>) to the collector. (this provides a handle for use in the next step)</li>
          <li>Send <tt>/internal/test/collect_poll</tt> requests (<xref target="collect-poll"/>) to the collector, polling until it is completed. (the collector will provide the calculated aggregate result)</li>
          <li>Stop containers.</li>
          <li>Copy logs out of each container.</li>
          <li>Delete containers, and clean up container networking resources.</li>
        </ol>
      </section>
    </section>
    <section anchor="implementation-status">
      <name>Implementation Status</name>
      <t><xref target="Janus"/> currently implements a version of this test interface.</t>
      <t>Additional DAP-PPM implementations would be warmly welcomed.</t>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>Any DAP-PPM implementation that adopts this testing interface should ensure that the test-only APIs described herein are only present in software used for testing purposes, and not in production systems.</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="DAP-PPM" target="https://datatracker.ietf.org/doc/html/draft-ietf-ppm-dap-01">
          <front>
            <title>Distributed Aggregation Protocol for Privacy Preserving Measurement</title>
            <author initials="T." surname="Geoghegan">
              <organization/>
            </author>
            <author initials="C." surname="Patton">
              <organization/>
            </author>
            <author initials="E." surname="Rescorla">
              <organization/>
            </author>
            <author initials="C. A." surname="Wood">
              <organization/>
            </author>
            <date year="2022" month="July" day="11"/>
          </front>
        </reference>
        <reference anchor="RFC2119">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner">
              <organization/>
            </author>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification.  These words are often capitalized. This document defines these words as they should be interpreted in IETF documents.  This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC8174">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba">
              <organization/>
            </author>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol  specifications.  This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the  defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC8729">
          <front>
            <title>The RFC Series and RFC Editor</title>
            <author fullname="R. Housley" initials="R." role="editor" surname="Housley">
              <organization/>
            </author>
            <author fullname="L. Daigle" initials="L." role="editor" surname="Daigle">
              <organization/>
            </author>
            <date month="February" year="2020"/>
            <abstract>
              <t>This document describes the framework for an RFC Series and an RFC Editor function that incorporate the principles of organized community involvement and accountability that has become necessary as the Internet technical community has grown, thereby enabling the RFC Series to continue to fulfill its mandate. This document obsoletes RFC 4844.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8729"/>
          <seriesInfo name="DOI" value="10.17487/RFC8729"/>
        </reference>
        <reference anchor="RFC4648">
          <front>
            <title>The Base16, Base32, and Base64 Data Encodings</title>
            <author fullname="S. Josefsson" initials="S." surname="Josefsson">
              <organization/>
            </author>
            <date month="October" year="2006"/>
            <abstract>
              <t>This document describes the commonly used base 64, base 32, and base 16 encoding schemes.  It also discusses the use of line-feeds in encoded data, use of padding in encoded data, use of non-alphabet characters in encoded data, use of different encoding alphabets, and canonical encodings.  [STANDARDS-TRACK]</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="4648"/>
          <seriesInfo name="DOI" value="10.17487/RFC4648"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="SI2020" target="https://research.protocol.ai/publications/automating-quic-interoperability-testing/seemann2020.pdf">
          <front>
            <title>Automating QUIC Interoperability Testing</title>
            <author initials="M." surname="Seemann">
              <organization/>
            </author>
            <author initials="J." surname="Iyengar">
              <organization/>
            </author>
            <date year="2020" month="August" day="10"/>
          </front>
        </reference>
        <reference anchor="Janus" target="https://github.com/divviup/janus">
          <front>
            <title>Experimental implementation of the DAP-PPM specification</title>
            <author>
              <organization/>
            </author>
            <date year="2022" month="August" day="25"/>
          </front>
        </reference>
      </references>
    </references>
    <section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>Thanks to Brandon Pitman, Christopher Patton, and Tim Geoghegan for feedback and contributions.</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
uw/QNBQzQByUi7MFlOCwz9JHB4/ESxj1BYBHGiEgiwCZiF7KJcveUYUCEs7g
kx+SIEELRoyUX51XJYwDLkB6AV54VWeVBoxjWXOxyRnpcLuTorwOv8GQhp33
X55MvoB9ZMZvnxLrJlsC9wMSQOMEwlkn2WMz9fg6Sc02orGA6ywYCa8Tt9ia
hs0Kc4P5G+X1lSt0geHj3qcIFFNvZr6Qh9YdFpCpszj8Bingyp7jwB4w7d3X
avPuLzjFu8E7nvHdaU5bnmJnx/x+ouzhw8fHiPE702H76Xm9wmegda3Hz0Dy
ZgGc2ZnirDMIjqZil7yyN1IwJUc/qFp/zr13qN/wImwlrbzJemjBihtyxNar
FWrKrsTSIcPCXlM+9bIBm6S324uwRIi+OrlU70NitL+9dxOQXllCbA40Lv0X
gucE0wLtBpdJ9TuPgBHsNWB4P7rDyp766yMuwv9xBx/EiIwTsxHsiE/WqZyP
+PmNsxSKbvDjJ2LaMeMSkHgzBYfMIdCI/r7hTL1nKughCSRWRbXxa7Nnml7f
TPc4DNC2QRF4kCxVchnwlFaJUyTEVViyCVSbigrAlK7CCIqbyXnfDd0MD/2d
1kVmZIpb5U+wydcAigzrRbtuAxY23E4v8QFbrcRxoKFBcApJgjFz4tTy0GS4
q6YY7epKnN9pl6BtX5FAChOPgOQu4oplZPJEeTqaDjKVm5Um79NyLxCWwUZh
MTAE52p6IAHxxkk6fTdpQoSRV22/8ekFDyL74mRnSibNnx9E2vD61XMaxckR
j+LP20ahIk/R5npAmckNxrrMYszlYp13sXGqOPvAqN9hsqvqkPFhNaaRI2Gu
Yx2tHUlp+u5kHqYAOm8BCoeaR+IAeXtI2vvzbxECHpEqU8lzN8Q2RFITYTVA
snfPqSPket8FkAM5atOFXinEygLVV2Z7yAr3+pCtJZR0JWXMAFA1pnvwHnsr
LtO7mOt25lMgx+zH3WFMEa0DVlGXXHDcgOtc8fztSSkFw5SbOt+Zq3bgOAIQ
Nxu7SO9AnEpJR6rzPE3fw0Kch1kFpJcJ4/Prlyf/IYDOZMlqovPPZZUsnzgL
R0vxUzNn/OZgpF6BJ+pPTgxybQYKD2Y4I6WSBGBrvxl6Hp75iu+V3Fjy/03o
45GMdtVyHC65ucV33HTNn/F1+m6644LOnakHcg936NUIwKomC3QVj3Y4TIED
tTVgEmq6XGmriIv0tKVmaPTuD26FAGxCwLVQFCpKwDVqIgmIEmvMUJfGVK4B
FaXFW8Nhh5WYXXdY44D1Vt6gK500xbfd57xN+PiMMGzvZzxsU7j72F42KhH+
Gp7W0/U9JM3fowvB3fuHI3g4woewfa+OFWk38B6gnzJt71uaihg+lZwHwLtO
0JjaNMJem0uU9YpSa6xMweM5FoK79EGo7MjatUpBzhtJBr5j6m72SNeCG2lV
6JDQ0JZu3rYBmiMJ1DbudltVxR3VzgaxZoatdpIAd1edhTtJhEof2fYQbWxJ
fZplVLxDLk73qYpA2+YqXbclSjVmbZe4y4XKsUbDXaY2TVT0QKKGwItSd0qV
JDZscNBTHwoMhaqS8S3q7Uv1vkxFaRdAIFc0O8xDLX/LsqPEH4jBktQ2PjOH
LPnxhhG3JH3cu6Wyf4gqAjFUkAMDlDNrMnRnNA+gTr1iOJessoSAQCGXpE89
pg17NkhuNjElvOCLoXcu5mpPTuKy+rviMhzfEIMPDhgnsILZ4nSD3QTXh3cM
46iNgzm3EwznWmMf2HiXtxago5KjM2vUTA//ZMUcNegtMUNskOTZ24z8SM4v
rMrur3FzYN7YGeL4Gk29Ln6rng8Wd7tANQomgyjUUntX2PAM99oVqUTjhraE
elw2R60h2D6Z9+hxZbW+4ZJtIiKWMrd8Oqrabi4O590hKk9qDsghiqVryVDp
lEv/7+32+y6ucSEtvx68B/q2BRbF0GvR0gFm4oM7P5stup68w1Iq+l6qTSF1
GTC7w2MvAF/hxlwWF9uMKjMKPgDcysKUsPqKSrO+0/Upcir0m4Y086yEZJk6
B0AWHzf8R+SJ27uVeMAZ2/O4W73N3O+TXH7w1L9oRoo8A8pCaYrZMKnhO0jO
uIB/ASFNzluRrS9A5WVJZzsuFfsIpNkd7ghUGS45xyfFbEgFHVUuL1zVwA6s
U8o597pQfxhQqUey5GDXpxTvpqEJvo3iLeW1lgtCYg/23ndf8ekcfzAubsf/
ktv7mB6TWjQbMCCWbLdjE7bL1eTqysQh5jB0V9qtG85H5VvKR5/rucLc9458
VL7t5KM43lJ/wuV2ISdtfIn0zPa5QysLPtc/3LViLwPmdVwG7CXUpMDcv6xc
1cYtrNJ/osQ7spJnxaU6JnhnoYcvaL1+N68RsyvMkJAZkd1Jf9cb8bqhWG+6
We0vFfOwE+vHO3fGOGKX6H0r0e8M/VRUi8OpsMzjDrmpdO+frBRw7CX4c4V1
P+6j19b9Qr9K0h9HRM2W7xMQRfh9VzyErXnO8Kp72E8n8PwHFbY/aoBwp8f9
7frS/wejD8rOtnkOwIN5tOW9tid58FH9yAelSY627+kwQYQMI3oAb51jX6sX
VXQKyU0L3h83anrxw7hWIsUS/sq2NsfiY/Ouz9eOZLZnBU21Cl5pz97gLWZQ
eFwQJgIwuI0JBfw7bc4hudqj5wh+e3OzpU//vkgFcjxD9mx/RUb3ZgIXuTWO
Uc6JC3LOSWCkX+HYd9ULhYa3NVYEbBJMxDHqPRoqLSKa4A7pSD2ybyXFZbE4
+zCm5CMBUFdhQ2P1t1vuYeXtY8jEq3Xhr4C5xKPRbyClVHO+SnCb7WDdzjmG
4DZ+QQRh42kAhMwl7uCvI8OOG/hhE2zOBgtKAVooePCb8c2HWL5vzAylYBLd
3KlrIRHzzh0DiyrbLnZyhwghQdiCST1bdyLi0gULhfseXXWjY44S8+s1XUfB
5kcSrONuFB53kYex+GbvF7CUp3zMYLrjDpqquGFIXOCDdP7c13RH060guo20
dSx65Y2qfIzaMi4YjQEzmY1JqEWc/vp2xsSznXk2sKd22+q0jSi0jTr6GKTh
EDwa2j1f4Jru4cyXB7l7vH9er3pvs5lGKH7rmYD7rBAOHHTXiU5NufOcH4IM
z5Rcb8Qz7DSXdjA4RQ9yZopMlofcGKIS8rzGl/i3FjrFTMhFtFOB5jQonx7u
JU42qmN3Lse6m7p0VrLSsFaLMBJ57S9EY0jCIb+nlCLXKNLW/m4buVP00qtC
0rXapqzgrX+emSsbLkkm1Bzx93H7qO2OSdKZ62O8JwGJpz/sHV3dvCqR8Jyy
SbN2V72ak4g2UTneecMS9O94ssZ5xDrsjhJTu5BSFj8cxR8FHbQGyEldiWWd
pyX3SirgtmWwpJoQmaV6q8pEuzp2VAJ7IFLtrrNFVSTuDEQrAXbbQAWlP3zw
Kz7s4w+A0EGROd/JIgawGlFy/IbLTjATheHliidjD2KjazZ+cBPj0ex4ZAsl
ve4laJSZuUIUX8NOTIl3AALZCn+RIOGSPSL7Sr79ngKf7zNf4kPBr/i6JvI2
k0WBnzsugzhx1tPdmF+RVpKqDfEYKpYjUBbMOuAU/m6GZx4RPgc9tM0lBupO
+2kazaY7IkEQnU5N171JF8qxWDZaZaltJmcYxf1MttCHJ8WV16Ot9wH4dhjQ
kdOJOX+nKxKtBOaMYAZI2v15PbwSCW+p+dyJcrYJdyvcLmJeehrJBKkz7AzR
0JF6d2J/MJhkWXMFI4BO28TxbJ4AOCO7oa4PunxkjalxiZPK15jd4UDXpWld
8Wjugl5JXYXTBhHgYYWouYXl7z60T7XzldwoS7ul6uUacnhqkV0K7KIuiQ3+
qgYQHh2mofMUzXUW1+WNiuCtvAyMyneWnfUAsSZbh9QsMrnWpVrwS51bav72
79h5lOBBmltX7EtofmaztpZPi0EgU0ZXr5sM2pkPDMD7vRQuvt00mBt5FE0X
q9m6CTujzgHeNcsJUNxBf8sn5zxbA8zQ6Qd3U4WQs/Knavw9tQd0clTj2VK8
Qr3LOnOZ42VTPkPC71wRiaRxrZMd/vxd+IWSvci7uJvgp76vzxXOyIdQYgzM
BX3NqLy13HJNmaTvu5ZyBljGHrp12cdzqZFf7JIxJEGj4QAmOmQwdJcPGfq3
OzDQgMOxOAYNrhT3Xska4itNh/ibKWQgW+6MdwZelJsgqFusJMgPfV1rV6Gq
UeJtkUpTzrLxWSbN/6U/siPxBkwKMNecvnEPtnobnprvOPRaT26LNOAep7o8
EOxeX/ePd93stcs4Hzi3v9DG7TMSUXP6ugMgzbVV1p9gFHet3RSuo+1sqWA3
+2lCLLHbzjF9T56Loa5bL6hdz+VUwP4R+5C99yVp+yG1+/P4AyaNmE6Txrer
bzte39S3rq/dQfuIc3S4/S4qOxXDvkhCurpdGu0jyVESXdtwCokOhthKFY0M
7luz69XrelQMqcaAesiGyy0WnwimPZXpnYkD/MKLyFVUrPMxD9NbmaILNsem
2FAGSndAeyBJY54oJKD3sxYJaE6OkNZziexirKkhAPX3Ods3788p7R8MvqXf
ifrOewlMS8JdejxdDYuFwl3vN3cwEGgys9t+auYq5CuyXOGvKagMeEpXFoAu
//N2/fgKIo/tU7pILzUFdaAdWa1f6vDhWDgZ4s79dX5AqblWjvffsJOMP8XU
Ov8ATtbMqyv8ojkV5ZYr6rIw5LjpXpeh4UVz/Y2PvHsBTF5Oepts/8DSkqom
PFIm/gdd6BeaZjK5xFkmCXr+TKULEhHk4pyXq/SPO/TbgTvUpZT5Jfmmz8mf
QA6rKwjdhuJ4WWKqX2A4x7+1xrRf6FXzq2y0xzl4K1zUh9h8q41J+j871hjS
VFEAAA==

-->

</rfc>
