# Testing

`GcpCompute` is built to be tested **without GCP, credentials, or the network**.
Two seams make that possible:

1. **`req_options: [adapter: fun]`** — `Req`'s native adapter option lets you
   answer requests from an in-memory routing table instead of the wire.
2. **`GcpCompute.TokenProvider.Static`** — `GcpCompute.Config.local/1` defaults to
   it, so no Goth and no real token are required.

## A minimal stubbed config

```elixir
config =
  GcpCompute.Config.local!(
    project: "test-project",
    zone: "us-central1-a",
    req_options: [
      retry: false,
      adapter: fn request ->
        response =
          case {request.method, request.url.path} do
            {:get, "/compute/v1/projects/test-project/zones/us-central1-a/instances/w"} ->
              %Req.Response{status: 200, body: %{"name" => "w", "status" => "RUNNING"}}

            _ ->
              %Req.Response{status: 404, body: %{"error" => %{"code" => 404}}}
          end

        {request, response}
      end
    ]
  )

{:ok, instance} = GcpCompute.get_instance(config, "w")
```

The adapter returns `{request, %Req.Response{}}` — and the response `body` is an
already-decoded map (no JSON round-trip needed).

## A reusable routing-table helper

The library's own test suite uses a small helper,
[`test/support/req_stub.ex`](https://github.com/nyo16/gcp_compute/blob/master/test/support/req_stub.ex),
that turns a `%{ {method, path} => response }` map into a config:

```elixir
config =
  GcpCompute.ReqStub.config(%{
    {:post, "/compute/v1/projects/test-project/zones/us-central1-a/instances"} =>
      {200, GcpCompute.ReqStub.operation("op-1", "PENDING")},
    {:post, "/compute/v1/projects/test-project/zones/us-central1-a/operations/op-1/wait"} =>
      {200, GcpCompute.ReqStub.operation("op-1", "DONE")},
    {:get, "/compute/v1/projects/test-project/zones/us-central1-a/instances/worker-1"} =>
      {200, GcpCompute.ReqStub.instance("worker-1")}
  })

assert {:ok, %GcpCompute.Instance{name: "worker-1"}} =
         GcpCompute.insert_instance_and_wait(config, name: "worker-1", machine_type: "e2-micro")
```

Copy it into your own `test/support/` if you build on top of `GcpCompute`.

## Simulating failures

- **API error** — return a non-2xx status with the Google error envelope; it
  becomes `{:error, %GcpCompute.Error{reason: :api_error, status: 403}}`.
- **Transport error** — return an exception from the adapter (e.g.
  `%Mint.TransportError{reason: :econnrefused}`); it becomes
  `{:error, %GcpCompute.Error{reason: :transport}}`.
- **Token failure** — set `token_provider: {GcpCompute.TokenProvider.Static, fn -> {:error, :no_creds} end}`;
  the call short-circuits with `{:error, %GcpCompute.Error{reason: :token_fetch_failed}}`
  before any request is made.

## Asserting telemetry

```elixir
:telemetry.attach("t", [:gcp_compute, :request, :stop], &handler/4, self())
# ... make a call ...
assert_receive {:telemetry, _measurements, %{method: :get, result: :ok}}
```
