Search results

There are no results.

std.net.http.test.Mock

type pub inline Mock

A type for defining an expected request and its response.

Mocks are defined using methods such as Server.get and Server.post. A Mock is only activated if Mock.then is called.

Mocks can define requirements such as expected query string parameters, headers, and how many times the request is expected to be received.

Query string matching

When a mock defines a set of expected query string parameters, these parameters are treated as the minimum required parameters, meaning additional parameters are allowed. The order of the request parameters must match that of the mock, and the values assigned must match exactly:

Mock valueRequest valueResult
name=Alicename=Alice&age=42match
name=Alice&name=Bobname=Aliceno match
name=Alice&name=Bobname=Bob&name=Aliceno match

Header matching

Header matching follows the same rules as query string matching:

Mock valueRequest valueResult
Accept: fooAccept: foomatch
Accept: foo, barAccept: foono match
Accept: foo, barAccept: bar, foono match

Body matching

Request bodies must match exactly that of the mock. If a mock doesn't define a body, a request only matches if it too doesn't specify a body.

Ordering

Mocks are matched against requests in order, skipping over mocks that are disabled (e.g. they're received the maximum number of requests allowed). For example:

import std.net.http.client (Client)
import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)
import std.uri (Uri)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let server = Server.new(t, fn (srv) {
        srv.get('/').then(fn { Response.new.string('hello') })
        srv.get('/').then(fn { Response.new.string('world') })
      })

      let client = Client.new

      server.prepare_client(client)

      let body = ByteArray.new

      2.times(fn (_) {
        let resp = client
          .get(Uri.parse('http://example.com').or_panic)
          .send
          .or_panic

        resp.body.read_all(body).or_panic
      })

      t.equal(body.to_string, 'helloworld')
    })

    tests.run
  }
}

For the first request the response body is "hello", while for the second response it's "world", resulting in the buffer being equal to "helloworld".

Instance methods

at_least

Show source code
Hide source code
fn pub move at_least(amount: Int) -> Self {
  @calls = Calls.Minimum(amount)
  self
}
fn pub move at_least(amount: Int) -> Mock

Sets the expected minimum number of requests to the given value.

Once the given number of requests is received this mock remains active.

Examples

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').at_least(2).then(fn { Response.new.string('hello') })
      })
    })

    tests.run
  }
}

at_most

Show source code
Hide source code
fn pub move at_most(amount: Int) -> Self {
  @calls = Calls.Maximum(amount)
  self
}
fn pub move at_most(amount: Int) -> Mock

Sets the expected maximum number of requests to the given value.

Once the given number of requests is received, the mock is disabled.

Examples

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').at_most(2).then(fn { Response.new.string('hello') })
      })
    })

    tests.run
  }
}

bytes

Show source code
Hide source code
fn pub move bytes(body: ref ByteArray) -> Self {
  @body = recover Body.Bytes(body.clone)
  self
}
fn pub move bytes(body: ref ByteArray) -> Mock

Sets the expected body to the given ByteArray.

Examples

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').bytes('hello'.to_byte_array).then(fn {
          Response.new.string('hello')
        })
      })
    })

    tests.run
  }
}

exactly

Show source code
Hide source code
fn pub move exactly(amount: Int) -> Self {
  @calls = Calls.Exactly(amount)
  self
}
fn pub move exactly(amount: Int) -> Mock

Sets the expected exact number of requests to the given value.

Once the given number of requests is received, the mock is disabled.

Examples

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').exactly(2).then(fn { Response.new.string('hello') })
      })
    })

    tests.run
  }
}

header

Show source code
Hide source code
fn pub move header(header: ref Header, value: String) -> Self {
  @headers.add(recover header.clone, value)
  self
}
fn pub move header(header: ref Header, value: String) -> Mock

Adds a header and its value to the set of expected headers.

If the header is already assigned a value, the value is appended to the list of expected values.

Examples

This defines a mock that only matches if the request includes at least the Accept: foo and User-Agent: bar headers:

import std.net.http (Header)
import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv
          .get('/')
          .header(Header.accept, 'foo')
          .header(Header.user_agent, 'bar')
          .then(fn { Response.new.string('hello') })
      })
    })

    tests.run
  }
}

multipart_form

Show source code
Hide source code
fn pub move multipart_form(
  body: fn (mut multipart.Form[mut ByteArray, IoError]),
) -> Self {
  let (header, buf) = multipart_form_data(body)

  header(Header.content_type, header).bytes(buf)
}
fn pub move multipart_form(body: fn (mut Form[mut ByteArray, Error])) -> Mock

Expects the request to be a multipart form request.

The body argument is a closure used to set the expected form fields and their values.

The returned mock expects the Content-Type header to be set to multipart/form-data; boundary=XXX where XXX is randomly generated string using a fixed seed. To achieve this, the Client.random field of a std.net.http.client.Client must be set to the result of std.rand.Random.new(0). The easiest way of doing so is by using RunningServer.prepare_client.

The body must be exactly the same as the form populated by the body argument. If additional fields are provided or the order doesn't match, the mock won't match a request.

Examples

import std.net.http.client (Client)
import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)
import std.uri (Uri)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let server = Server.new(t, fn (srv) {
        srv
          .post('/')
          .multipart_form(fn (f) {
            let _ = f.field('name').text('Alice')
            let _ = f.field('age').text('42')
          })
          .then(fn { Response.new.string('created') })
      })

      let client = Client.new

      server.prepare_client(client)

      let body = ByteArray.new
      let form = client.post(Uri.parse('/').or_panic).multipart_form.or_panic

      form.add('name').text('Alice').or_panic
      form.add('age').text('42').or_panic

      let resp = form.send.or_panic
      let _ = resp.body.read_all(body).or_panic

      t.equal(body.to_string, 'created')
    })

    tests.run
  }
}

query

Show source code
Hide source code
fn pub move query(key: String, value: String) -> Self {
  @query.add(key, value)
  self
}
fn pub move query(key: String, value: String) -> Mock

Adds a query string parameter and its value to the set of expected parameters.

If the parameter is already assigned a value, the value is appended to the list of expected values.

Examples

This defines a mock that only matches if the request includes at least the name=Alice and age=42 query string parameters:

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').query('name', 'Alice').query('age', '42').then(fn {
          Response.new.string('hello')
        })
      })
    })

    tests.run
  }
}

string

Show source code
Hide source code
fn pub move string(body: String) -> Self {
  @body = recover Body.String(body)
  self
}
fn pub move string(body: String) -> Mock

Sets the expected body to the given String.

Examples

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        srv.get('/').string('hello').then(fn { Response.new.string('hello') })
      })
    })

    tests.run
  }
}

then

Show source code
Hide source code
fn pub move then(response: uni fn -> Response) {
  let id = @server.next_id
  let mat = recover {
    Matcher(
      id: id,
      enabled: true,
      calls: @calls,
      query: @query,
      headers: @headers,
      body: @body,
    )
  }

  @server.locations.push(@location)
  @server.matchers.add(@method, @path, mat)
  @server.responses.set(id, response)
}
fn pub move then(response: uni fn -> Response)

Installs the mock and defines its response body.

The response argument is the closure used for generating the response body. This closure must be a uni closure such that it can be moved between processes. This means the closure can only capture data that is sendable.

Examples

In this example a uni Array[Int] is captured and modified for each request, with the response showing the size of the array:

import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let _server = Server.new(t, fn (srv) {
        let mut counts = recover []

        srv.get('/').then(fn move {
          counts.push(1)
          Response.new.string('size: ${counts.size}')
        })
      })
    })

    tests.run
  }
}

url_encoded_form

Show source code
Hide source code
fn pub move url_encoded_form(body: fn (mut Values)) -> Self {
  header(Header.content_type, URL_FORM).string(url_encoded_form_data(body))
}
fn pub move url_encoded_form(body: fn (mut Values)) -> Mock

Expect the request to be a URL encoded form request.

The body argument is a closure used to set the expected form fields and their values.

The returned mock expects the Content-Type header to be set to application/x-www-form-urlencoded and the body to be exactly the same as the form populated by the body argument. If additional fields are provided or the order doesn't match, the mock won't match a request.

Examples

import std.net.http.client (Client)
import std.net.http.server (Response)
import std.net.http.test (Server)
import std.test (Tests)
import std.uri (Uri)

type async Main {
  fn async main {
    let tests = Tests.new

    tests.test('Example test', fn (t) {
      let server = Server.new(t, fn (srv) {
        srv
          .post('/')
          .url_encoded_form(fn (f) {
            f.add('name', 'Alice')
            f.add('age', '42')
          })
          .then(fn { Response.new.string('created') })
      })

      let client = Client.new

      server.prepare_client(client)

      let body = ByteArray.new
      let form = client.post(Uri.parse('/').or_panic).url_encoded_form

      form.add('name', 'Alice')
      form.add('age', '42')

      let resp = form.send.or_panic
      let _ = resp.body.read_all(body).or_panic

      t.equal(body.to_string, 'created')
    })

    tests.run
  }
}