SECTOR 02 // HOW-TO GUIDES

Compose a domain provider

Give your system under test its own vocabulary (submit, await, count) with zero Go.

Goal: scenarios that read app.submit / app.await instead of raw http.post plumbing.

1. Declare a kind: Provider resource #

Create providers/app.yml:

apiVersion: shinari/v1
kind: Provider
name: app
verbs:
  submit:
    params: [job, "inputs?"]
    do:
      - run: http.post
        with:
          path: "/jobs/${.params.job}"
          form: "${.params.inputs}"
        read: ".id"
  await:
    params: [of, timeout]
    do:
      - run: wait_until
        with:
          probe:
            run: http.get
            with:
              path: "/jobs/${.params.of}"
          read: ".state"
          in: [SUCCESS, FAILED]
          timeout: "${.params.timeout}"
  count:
    params: [job]
    probe:
      run: http.get
      with:
        path: "/jobs?type=${.params.job}"
      read: ".items | length"

A trailing ? marks a param optional, and it must be quoted ("inputs?"): bare inputs?] is invalid YAML flow syntax.

2. Configure it #

In project.yml, point an instance at the definition and give it config:

providers:
  http:
    config:
      baseUrl: http://localhost:8080
  app:
    use: ./providers/app

The instance name (app) becomes the verb namespace: app.submit, app.count. The body’s leaf verbs (http.post, http.get) resolve against the instances configured in the same project, so the http instance’s baseUrl decides which deployment the vocabulary talks to.

3. Use the vocabulary #

method:
  - phase: "Submit a long job"
    steps:
      - run: app.submit
        with:
          job: sleep
          inputs:
            seconds: 30
        as: job
verify:
  - run: app.await
    with:
      of: "${.outputs.job.value}"
      timeout: 420
  - run: app.count
    with: sleep
    as: total
  - run: assert
    with:
      of: "${.outputs.total.value}"
      equals: 1
    desc: "exactly once"

A composed verb’s result is its last body step’s value, after that step’s read: transform. app.submit ends in read: ".id", so the verb returns the job id; as: job then wraps it in the usual {value, output, meta} envelope, which is why the scenario passes ${.outputs.job.value} (the id itself) to app.await.

What the engine infers for you #

Rules to know #