# Testing Policies & Oracles \[Unit-test Newton Rego policies with opa test, test WASM data oracles locally and via the Gateway, and run end-to-end policy simulations before deploying.]

Test at every layer before you deploy: unit-test your Rego rules with `opa test`, run your WASM oracle in isolation, then simulate the full policy end-to-end against the Newton network. This guide covers all three. For diagnosing failures after deployment, see [Testing & Debugging](/developers/resources/testing-debugging).

## Unit-test your Rego with `opa test`

Rego rules are pure functions of `input`, `data.params`, and `data.wasm`, which makes them easy to unit-test without the network. Write a companion `policy_test.rego` and mock the data with `with`.

Given a policy like this:

```rego
package vault_risk_rating

import future.keywords

default allow := false

t := data.params

# Each oracle's output is namespaced under its pack id.
v := data.wasm.vaultsfyi

deny contains "apy_spike" if v.apy_z_score > t.apy_z_max

deny contains "risk_score_below_floor" if {
    v.risk_score != null
    v.risk_score < t.risk_score_floor
}

allow if count(deny) == 0
```

Write `policy_test.rego` alongside it. Mock `data.params` and `data.wasm` with `with`, and assert on both `allow` and membership in the `deny` set:

```rego
package vault_risk_rating_test

import data.vault_risk_rating

default_params := {
    "apy_z_max": 4,
    "risk_score_floor": 60,
}

clean_data := {
    "apy_z_score": 0.5,
    "risk_score": 90,
}

# The policy reads data.wasm.vaultsfyi.*, so wrap fixtures under the pack id.
wrap(inner) := {"vaultsfyi": inner}

with_data(overrides) := wrap(object.union(clean_data, overrides))

test_allow_when_all_clean if {
    d := wrap(clean_data)
    vault_risk_rating.allow with data.params as default_params with data.wasm as d
}

test_deny_apy_spike if {
    d := with_data({"apy_z_score": 5})
    "apy_spike" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
    not vault_risk_rating.allow with data.params as default_params with data.wasm as d
}

test_deny_risk_score_below_floor if {
    d := with_data({"risk_score": 30})
    "risk_score_below_floor" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
}

# A null risk score must not deny — guards against a fail-closed regression.
test_risk_score_null_does_not_deny if {
    d := with_data({"risk_score": null})
    not "risk_score_below_floor" in vault_risk_rating.deny with data.params as default_params with data.wasm as d
}

# Two simultaneous failures must coexist in the deny set, not fail open.
test_multiple_denies_do_not_fail_open if {
    d := with_data({"apy_z_score": 99, "risk_score": 10})
    deny := vault_risk_rating.deny with data.params as default_params with data.wasm as d
    count(deny) >= 2
    not vault_risk_rating.allow with data.params as default_params with data.wasm as d
}
```

Run the tests:

```bash
opa test ./policy.rego ./policy_test.rego -v
```

Recommended coverage:

* **Happy path** — clean data allows.
* **Each deny rule** — one test per rule, asserting the expected reason is in `deny` and `allow` is false.
* **Param toggles** — a deny flag set to `false` does not deny.
* **Edge values** — `null`, empty, and out-of-range fields behave as intended.
* **No fail-open under multiple failures** — two deny conditions at once still denies.
* **Multi-oracle** — for chained oracles, wrap each fixture under its own pack id (`{"vaultsfyi": {...}, "webacy": {...}}`) and assert deny rules from each namespace fire independently.

### Quick local checks with the CLI

The CLI bundles a Rego engine with Newton's crypto and identity built-ins. Use it to evaluate a query or validate syntax:

```bash
# Evaluate a query against policy + data + input files
newton-cli regorus eval \
  -d policy.rego \
  -d data.json \
  -i intent.json \
  --non-strict \
  "data.vault_risk_rating.allow"

# Validate syntax / inspect the policy
newton-cli regorus parse policy.rego
newton-cli regorus lex policy.rego
```

:::note
Pass `--non-strict` for OPA-compatible evaluation. `-d` (data/policy) can be repeated; `-i` is the input (intent) file. See the [CLI Reference](/developers/reference/command-line-tool#regorus).
:::

## Test your WASM oracle

Run the compiled oracle in isolation to confirm it produces the data shape your Rego expects.

```bash
# Local execution in a sandbox with the Newton HTTP host
newton-cli --chain-id 11155111 policy-data simulate \
  --wasm-file ./dist/policy.wasm \
  --input-json '{"network":"mainnet","vaultAddress":"0x..."}'
```

For an oracle that needs secrets, test through the Gateway:

| Method | Secrets source | Ownership |
|--------|----------------|-----------|
| `newt_simulatePolicyData` | Provided inline by the caller | None required |
| `newt_simulatePolicyDataWithClient` | Read from stored, uploaded secrets | PolicyClient owner |

```bash
curl -X POST https://gateway.testnet.newton.xyz/rpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $NEWTON_API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "newt_simulatePolicyData",
    "params": {
      "policy_data_address": "0xYourPolicyData",
      "wasm_args": "0x7b226e6574776f726b223a226d61696e6e6574227d"
    },
    "id": "7ca6621b-7aa4-4bb7-a896-1f2b58a18c78"
  }'
```

See [Uploading & Accessing Secrets in Oracles](/developers/guides/secrets-in-oracles) for the secrets lifecycle.

## Simulate the full policy

The final step before deploying: evaluate the complete policy (Rego + every oracle + merged data) against a sample intent with `newt_simulatePolicy`. Pass one entry per oracle in `policy_data` and namespaced `policy_params`:

```bash
curl -X POST https://gateway.testnet.newton.xyz/rpc \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $NEWTON_API_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "method": "newt_simulatePolicy",
    "params": {
      "policy_client": "0xYourPolicyClient",
      "policy": "package vault_risk_rating\n...",
      "entrypoint": "vault_risk_rating.allow",
      "intent": {
        "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
        "to": "0xb1aD5f82407bC0f19f42b2614fb9083035a36b69",
        "value": "0x0",
        "data": "0x",
        "chain_id": "0xaa36a7",
        "function_signature": "0x"
      },
      "policy_data": [
        { "policy_data_address": "0xYourPolicyData" }
      ],
      "policy_params": { "vaultsfyi": { "apy_z_max": 4, "risk_score_floor": 60 } }
    },
    "id": "7ca6621b-7aa4-4bb7-a896-1f2b58a18c78"
  }'
```

Inspect `evaluation_result.policy_params_and_data` in the response to confirm the merged `data.wasm` shape, and `evaluation_result.result` for the allow/deny decision. For multi-oracle policies, see [Chaining Multiple Data Oracles](/developers/guides/chaining-data-oracles#simulating-a-multi-oracle-policy).

## Recommended workflow

1. `opa test` your Rego rules.
2. `policy-data simulate` each oracle locally.
3. Upload secrets, then `newt_simulatePolicyDataWithClient` to verify they resolve.
4. `newt_simulatePolicy` for the full end-to-end decision.
5. Deploy, then submit production tasks.

## Next Steps

<Cards>
  <Card icon="bug" to="/developers/resources/testing-debugging" title="Testing & Debugging">
    Diagnose failures after deployment
  </Card>

  <Card icon="link" to="/developers/guides/chaining-data-oracles" title="Chaining Data Oracles">
    Simulate and test multi-oracle policies
  </Card>

  <Card icon="list-check" to="/developers/resources/deployment-checklist" title="Deployment Checklist">
    Pre-launch verification steps
  </Card>

  <Card icon="book" to="/developers/advanced/rego-syntax-guide" title="Rego Syntax Guide">
    Rego language reference
  </Card>
</Cards>
